blob: dd07ae56078543f616a3c5f002a4579b5783d6e1 [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
Vasanthakumar Thiagarajan1b2df402012-02-06 20:15:53 +05303 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
Kalle Valobdcd8172011-07-18 00:22:30 +03004 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040018#include <linux/moduleparam.h>
Raja Manic08631c2011-12-16 14:24:24 +053019#include <linux/inetdevice.h>
Kalle Valod6a434d2012-01-17 20:09:36 +020020#include <linux/export.h>
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040021
Kalle Valobdcd8172011-07-18 00:22:30 +030022#include "core.h"
23#include "cfg80211.h"
24#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030025#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030026#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030027
28#define RATETAB_ENT(_rate, _rateid, _flags) { \
29 .bitrate = (_rate), \
30 .flags = (_flags), \
31 .hw_value = (_rateid), \
32}
33
34#define CHAN2G(_channel, _freq, _flags) { \
35 .band = IEEE80211_BAND_2GHZ, \
36 .hw_value = (_channel), \
37 .center_freq = (_freq), \
38 .flags = (_flags), \
39 .max_antenna_gain = 0, \
40 .max_power = 30, \
41}
42
43#define CHAN5G(_channel, _flags) { \
44 .band = IEEE80211_BAND_5GHZ, \
45 .hw_value = (_channel), \
46 .center_freq = 5000 + (5 * (_channel)), \
47 .flags = (_flags), \
48 .max_antenna_gain = 0, \
49 .max_power = 30, \
50}
51
52static struct ieee80211_rate ath6kl_rates[] = {
53 RATETAB_ENT(10, 0x1, 0),
54 RATETAB_ENT(20, 0x2, 0),
55 RATETAB_ENT(55, 0x4, 0),
56 RATETAB_ENT(110, 0x8, 0),
57 RATETAB_ENT(60, 0x10, 0),
58 RATETAB_ENT(90, 0x20, 0),
59 RATETAB_ENT(120, 0x40, 0),
60 RATETAB_ENT(180, 0x80, 0),
61 RATETAB_ENT(240, 0x100, 0),
62 RATETAB_ENT(360, 0x200, 0),
63 RATETAB_ENT(480, 0x400, 0),
64 RATETAB_ENT(540, 0x800, 0),
65};
66
67#define ath6kl_a_rates (ath6kl_rates + 4)
68#define ath6kl_a_rates_size 8
69#define ath6kl_g_rates (ath6kl_rates + 0)
70#define ath6kl_g_rates_size 12
71
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +053072#define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
73 IEEE80211_HT_CAP_SGI_20 | \
74 IEEE80211_HT_CAP_SGI_40)
75
Kalle Valobdcd8172011-07-18 00:22:30 +030076static struct ieee80211_channel ath6kl_2ghz_channels[] = {
77 CHAN2G(1, 2412, 0),
78 CHAN2G(2, 2417, 0),
79 CHAN2G(3, 2422, 0),
80 CHAN2G(4, 2427, 0),
81 CHAN2G(5, 2432, 0),
82 CHAN2G(6, 2437, 0),
83 CHAN2G(7, 2442, 0),
84 CHAN2G(8, 2447, 0),
85 CHAN2G(9, 2452, 0),
86 CHAN2G(10, 2457, 0),
87 CHAN2G(11, 2462, 0),
88 CHAN2G(12, 2467, 0),
89 CHAN2G(13, 2472, 0),
90 CHAN2G(14, 2484, 0),
91};
92
93static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
94 CHAN5G(34, 0), CHAN5G(36, 0),
95 CHAN5G(38, 0), CHAN5G(40, 0),
96 CHAN5G(42, 0), CHAN5G(44, 0),
97 CHAN5G(46, 0), CHAN5G(48, 0),
98 CHAN5G(52, 0), CHAN5G(56, 0),
99 CHAN5G(60, 0), CHAN5G(64, 0),
100 CHAN5G(100, 0), CHAN5G(104, 0),
101 CHAN5G(108, 0), CHAN5G(112, 0),
102 CHAN5G(116, 0), CHAN5G(120, 0),
103 CHAN5G(124, 0), CHAN5G(128, 0),
104 CHAN5G(132, 0), CHAN5G(136, 0),
105 CHAN5G(140, 0), CHAN5G(149, 0),
106 CHAN5G(153, 0), CHAN5G(157, 0),
107 CHAN5G(161, 0), CHAN5G(165, 0),
108 CHAN5G(184, 0), CHAN5G(188, 0),
109 CHAN5G(192, 0), CHAN5G(196, 0),
110 CHAN5G(200, 0), CHAN5G(204, 0),
111 CHAN5G(208, 0), CHAN5G(212, 0),
112 CHAN5G(216, 0),
113};
114
115static struct ieee80211_supported_band ath6kl_band_2ghz = {
116 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
117 .channels = ath6kl_2ghz_channels,
118 .n_bitrates = ath6kl_g_rates_size,
119 .bitrates = ath6kl_g_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530120 .ht_cap.cap = ath6kl_g_htcap,
121 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300122};
123
124static struct ieee80211_supported_band ath6kl_band_5ghz = {
125 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
126 .channels = ath6kl_5ghz_a_channels,
127 .n_bitrates = ath6kl_a_rates_size,
128 .bitrates = ath6kl_a_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530129 .ht_cap.cap = ath6kl_g_htcap,
130 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300131};
132
Jouni Malinen837cb972011-10-11 17:31:57 +0300133#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
134
Kalle Valo10509f92011-12-13 14:52:07 +0200135/* returns true if scheduled scan was stopped */
136static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
137{
138 struct ath6kl *ar = vif->ar;
139
140 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
141 return false;
142
143 del_timer_sync(&vif->sched_scan_timer);
144
145 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
146 ATH6KL_HOST_MODE_AWAKE);
147
148 ar->state = ATH6KL_STATE_ON;
149
150 return true;
151}
152
153static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
154{
155 struct ath6kl *ar = vif->ar;
156 bool stopped;
157
158 stopped = __ath6kl_cfg80211_sscan_stop(vif);
159
160 if (!stopped)
161 return;
162
163 cfg80211_sched_scan_stopped(ar->wiphy);
164}
165
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530166static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300167 enum nl80211_wpa_versions wpa_version)
168{
169 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
170
171 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530172 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300173 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530174 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300175 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530176 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300177 } else {
178 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
179 return -ENOTSUPP;
180 }
181
182 return 0;
183}
184
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530185static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300186 enum nl80211_auth_type auth_type)
187{
Kalle Valobdcd8172011-07-18 00:22:30 +0300188 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
189
190 switch (auth_type) {
191 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530192 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300193 break;
194 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530195 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300196 break;
197 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530198 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300199 break;
200
201 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530202 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300203 break;
204
205 default:
Masanari Iida3c325fb2012-01-31 23:32:55 +0900206 ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300207 return -ENOTSUPP;
208 }
209
210 return 0;
211}
212
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530213static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300214{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530215 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
216 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
217 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300218
219 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
220 __func__, cipher, ucast);
221
222 switch (cipher) {
223 case 0:
224 /* our own hack to use value 0 as no crypto used */
225 *ar_cipher = NONE_CRYPT;
226 *ar_cipher_len = 0;
227 break;
228 case WLAN_CIPHER_SUITE_WEP40:
229 *ar_cipher = WEP_CRYPT;
230 *ar_cipher_len = 5;
231 break;
232 case WLAN_CIPHER_SUITE_WEP104:
233 *ar_cipher = WEP_CRYPT;
234 *ar_cipher_len = 13;
235 break;
236 case WLAN_CIPHER_SUITE_TKIP:
237 *ar_cipher = TKIP_CRYPT;
238 *ar_cipher_len = 0;
239 break;
240 case WLAN_CIPHER_SUITE_CCMP:
241 *ar_cipher = AES_CRYPT;
242 *ar_cipher_len = 0;
243 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200244 case WLAN_CIPHER_SUITE_SMS4:
245 *ar_cipher = WAPI_CRYPT;
246 *ar_cipher_len = 0;
247 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300248 default:
249 ath6kl_err("cipher 0x%x not supported\n", cipher);
250 return -ENOTSUPP;
251 }
252
253 return 0;
254}
255
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530256static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300257{
258 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
259
260 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530261 if (vif->auth_mode == WPA_AUTH)
262 vif->auth_mode = WPA_PSK_AUTH;
263 else if (vif->auth_mode == WPA2_AUTH)
264 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300265 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530266 if (vif->auth_mode == WPA_AUTH)
267 vif->auth_mode = WPA_AUTH_CCKM;
268 else if (vif->auth_mode == WPA2_AUTH)
269 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300270 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530271 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300272 }
273}
274
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530275static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300276{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530277 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530278
Kalle Valobdcd8172011-07-18 00:22:30 +0300279 if (!test_bit(WMI_READY, &ar->flag)) {
280 ath6kl_err("wmi is not ready\n");
281 return false;
282 }
283
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530284 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300285 ath6kl_err("wlan disabled\n");
286 return false;
287 }
288
289 return true;
290}
291
Kevin Fang6981ffd2011-10-07 08:51:19 +0800292static bool ath6kl_is_wpa_ie(const u8 *pos)
293{
294 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
295 pos[2] == 0x00 && pos[3] == 0x50 &&
296 pos[4] == 0xf2 && pos[5] == 0x01;
297}
298
299static bool ath6kl_is_rsn_ie(const u8 *pos)
300{
301 return pos[0] == WLAN_EID_RSN;
302}
303
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700304static bool ath6kl_is_wps_ie(const u8 *pos)
305{
306 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
307 pos[1] >= 4 &&
308 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
309 pos[5] == 0x04);
310}
311
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530312static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
313 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800314{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530315 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800316 const u8 *pos;
317 u8 *buf = NULL;
318 size_t len = 0;
319 int ret;
320
321 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700322 * Clear previously set flag
323 */
324
325 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
326
327 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800328 * Filter out RSN/WPA IE(s)
329 */
330
331 if (ies && ies_len) {
332 buf = kmalloc(ies_len, GFP_KERNEL);
333 if (buf == NULL)
334 return -ENOMEM;
335 pos = ies;
336
337 while (pos + 1 < ies + ies_len) {
338 if (pos + 2 + pos[1] > ies + ies_len)
339 break;
340 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
341 memcpy(buf + len, pos, 2 + pos[1]);
342 len += 2 + pos[1];
343 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700344
345 if (ath6kl_is_wps_ie(pos))
346 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
347
Kevin Fang6981ffd2011-10-07 08:51:19 +0800348 pos += 2 + pos[1];
349 }
350 }
351
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530352 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
353 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800354 kfree(buf);
355 return ret;
356}
357
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530358static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
359{
360 switch (type) {
361 case NL80211_IFTYPE_STATION:
362 *nw_type = INFRA_NETWORK;
363 break;
364 case NL80211_IFTYPE_ADHOC:
365 *nw_type = ADHOC_NETWORK;
366 break;
367 case NL80211_IFTYPE_AP:
368 *nw_type = AP_NETWORK;
369 break;
370 case NL80211_IFTYPE_P2P_CLIENT:
371 *nw_type = INFRA_NETWORK;
372 break;
373 case NL80211_IFTYPE_P2P_GO:
374 *nw_type = AP_NETWORK;
375 break;
376 default:
377 ath6kl_err("invalid interface type %u\n", type);
378 return -ENOTSUPP;
379 }
380
381 return 0;
382}
383
384static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
385 u8 *if_idx, u8 *nw_type)
386{
387 int i;
388
389 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
390 return false;
391
392 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
Kalle Valo96f1fad2012-03-07 20:03:57 +0200393 ar->num_vif))
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530394 return false;
395
396 if (type == NL80211_IFTYPE_STATION ||
397 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200398 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530399 if ((ar->avail_idx_map >> i) & BIT(0)) {
400 *if_idx = i;
401 return true;
402 }
403 }
404 }
405
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530406 if (type == NL80211_IFTYPE_P2P_CLIENT ||
407 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200408 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530409 if ((ar->avail_idx_map >> i) & BIT(0)) {
410 *if_idx = i;
411 return true;
412 }
413 }
414 }
415
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530416 return false;
417}
418
Kalle Valo8c9bb052012-03-07 20:04:00 +0200419static bool ath6kl_is_tx_pending(struct ath6kl *ar)
420{
421 return ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0;
422}
423
424
Kalle Valobdcd8172011-07-18 00:22:30 +0300425static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
426 struct cfg80211_connect_params *sme)
427{
428 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530429 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300430 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800431 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Raja Manice0dc0c2012-02-20 19:08:08 +0530432 u16 interval;
Kalle Valobdcd8172011-07-18 00:22:30 +0300433
Kalle Valo10509f92011-12-13 14:52:07 +0200434 ath6kl_cfg80211_sscan_disable(vif);
435
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530436 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300437
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530438 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300439 return -EIO;
440
441 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
442 ath6kl_err("destroy in progress\n");
443 return -EBUSY;
444 }
445
446 if (test_bit(SKIP_SCAN, &ar->flag) &&
447 ((sme->channel && sme->channel->center_freq == 0) ||
448 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
449 ath6kl_err("SkipScan: channel or bssid invalid\n");
450 return -EINVAL;
451 }
452
453 if (down_interruptible(&ar->sem)) {
454 ath6kl_err("busy, couldn't get access\n");
455 return -ERESTARTSYS;
456 }
457
458 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
459 ath6kl_err("busy, destroy in progress\n");
460 up(&ar->sem);
461 return -EBUSY;
462 }
463
464 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
465 /*
466 * sleep until the command queue drains
467 */
468 wait_event_interruptible_timeout(ar->event_wq,
Kalle Valo8c9bb052012-03-07 20:04:00 +0200469 ath6kl_is_tx_pending(ar),
470 WMI_TIMEOUT);
Kalle Valobdcd8172011-07-18 00:22:30 +0300471 if (signal_pending(current)) {
472 ath6kl_err("cmd queue drain timeout\n");
473 up(&ar->sem);
474 return -EINTR;
475 }
476 }
477
Jouni Malinen6e786cb2011-12-15 14:16:00 +0200478 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
479 if (status) {
480 up(&ar->sem);
481 return status;
482 }
483
484 if (sme->ie == NULL || sme->ie_len == 0)
Raja Mani542c5192011-11-15 14:14:56 +0530485 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800486
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530487 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530488 vif->ssid_len == sme->ssid_len &&
489 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530490 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530491 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
492 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530493 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300494
495 up(&ar->sem);
496 if (status) {
497 ath6kl_err("wmi_reconnect_cmd failed\n");
498 return -EIO;
499 }
500 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530501 } else if (vif->ssid_len == sme->ssid_len &&
502 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530503 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300504 }
505
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530506 memset(vif->ssid, 0, sizeof(vif->ssid));
507 vif->ssid_len = sme->ssid_len;
508 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300509
510 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530511 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300512
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530513 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300514 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530515 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300516
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530517 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300518
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530519 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300520 if (status) {
521 up(&ar->sem);
522 return status;
523 }
524
525 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530526 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300527 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530528 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300529
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530530 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300531
532 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530533 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300534
535 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530536 (vif->auth_mode == NONE_AUTH) &&
537 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300538 struct ath6kl_key *key = NULL;
539
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530540 if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300541 ath6kl_err("key index %d out of bounds\n",
542 sme->key_idx);
543 up(&ar->sem);
544 return -ENOENT;
545 }
546
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530547 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300548 key->key_len = sme->key_len;
549 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530550 key->cipher = vif->prwise_crypto;
551 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300552
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530553 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530554 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300555 GROUP_USAGE | TX_USAGE,
556 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200557 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300558 key->key, KEY_OP_INIT_VAL, NULL,
559 NO_SYNC_WMIFLAG);
560 }
561
562 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530563 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530564 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200565 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300566 ath6kl_err("couldn't set bss filtering\n");
567 up(&ar->sem);
568 return -EIO;
569 }
570 }
571
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530572 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300573
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800574 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
575 nw_subtype = SUBTYPE_P2PCLIENT;
576
Kalle Valobdcd8172011-07-18 00:22:30 +0300577 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
578 "%s: connect called with authmode %d dot11 auth %d"
579 " PW crypto %d PW crypto len %d GRP crypto %d"
580 " GRP crypto len %d channel hint %u\n",
581 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530582 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
583 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530584 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300585
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530586 vif->reconnect_flag = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +0530587
588 if (vif->nw_type == INFRA_NETWORK) {
Kalle Valob5283872012-03-12 13:23:23 +0200589 interval = max_t(u16, vif->listen_intvl_t,
590 ATH6KL_MAX_WOW_LISTEN_INTL);
Raja Manice0dc0c2012-02-20 19:08:08 +0530591 status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
592 interval,
593 0);
594 if (status) {
595 ath6kl_err("couldn't set listen intervel\n");
596 up(&ar->sem);
597 return status;
598 }
599 }
600
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530601 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530602 vif->dot11_auth_mode, vif->auth_mode,
603 vif->prwise_crypto,
604 vif->prwise_crypto_len,
605 vif->grp_crypto, vif->grp_crypto_len,
606 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530607 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800608 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300609
610 up(&ar->sem);
611
612 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530613 memset(vif->ssid, 0, sizeof(vif->ssid));
614 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300615 ath6kl_err("invalid request\n");
616 return -ENOENT;
617 } else if (status) {
618 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
619 return -EIO;
620 }
621
622 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Kalle Valoddc3d772012-03-07 20:03:58 +0200623 ((vif->auth_mode == WPA_PSK_AUTH) ||
624 (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530625 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300626 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
627 }
628
629 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530630 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300631
632 return 0;
633}
634
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530635static struct cfg80211_bss *
636ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
637 enum network_type nw_type,
638 const u8 *bssid,
639 struct ieee80211_channel *chan,
640 const u8 *beacon_ie,
641 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300642{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530643 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300644 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530645 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300646 u8 *ie;
647
Raja Mani4eab6f42011-11-09 17:02:23 +0530648 if (nw_type & ADHOC_NETWORK) {
649 cap_mask = WLAN_CAPABILITY_IBSS;
650 cap_val = WLAN_CAPABILITY_IBSS;
651 } else {
652 cap_mask = WLAN_CAPABILITY_ESS;
653 cap_val = WLAN_CAPABILITY_ESS;
654 }
655
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530656 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530657 vif->ssid, vif->ssid_len,
658 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300659 if (bss == NULL) {
660 /*
661 * Since cfg80211 may not yet know about the BSS,
662 * generate a partial entry until the first BSS info
663 * event becomes available.
664 *
665 * Prepend SSID element since it is not included in the Beacon
666 * IEs from the target.
667 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530668 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300669 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530670 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300671 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530672 ie[1] = vif->ssid_len;
673 memcpy(ie + 2, vif->ssid, vif->ssid_len);
674 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530675 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530676 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530677 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300678 0, GFP_KERNEL);
679 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530680 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
681 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300682 kfree(ie);
683 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530684 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300685
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530686 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300687}
688
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530689void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300690 u8 *bssid, u16 listen_intvl,
691 u16 beacon_intvl,
692 enum network_type nw_type,
693 u8 beacon_ie_len, u8 assoc_req_len,
694 u8 assoc_resp_len, u8 *assoc_info)
695{
Jouni Malinen01cac472011-09-19 19:14:59 +0300696 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530697 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530698 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300699
700 /* capinfo + listen interval */
701 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
702
703 /* capinfo + status code + associd */
704 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
705
706 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
707 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
708 assoc_resp_ie_offset;
709
710 assoc_req_len -= assoc_req_ie_offset;
711 assoc_resp_len -= assoc_resp_ie_offset;
712
Jouni Malinen32c10872011-09-19 19:15:07 +0300713 /*
714 * Store Beacon interval here; DTIM period will be available only once
715 * a Beacon frame from the AP is seen.
716 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530717 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530718 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300719
Kalle Valobdcd8172011-07-18 00:22:30 +0300720 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530721 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300722 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
723 "%s: ath6k not in ibss mode\n", __func__);
724 return;
725 }
726 }
727
728 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530729 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
730 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300731 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
732 "%s: ath6k not in station mode\n", __func__);
733 return;
734 }
735 }
736
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530737 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300738
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530739 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
740 assoc_info, beacon_ie_len);
741 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530742 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300743 return;
744 }
745
Raja Mani4eab6f42011-11-09 17:02:23 +0530746 if (nw_type & ADHOC_NETWORK) {
747 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
748 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
749 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530750 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300751 return;
752 }
753
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530754 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300755 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530756 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530757 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300758 assoc_req_ie, assoc_req_len,
759 assoc_resp_ie, assoc_resp_len,
760 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530761 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530762 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300763 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530764 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
765 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300766 }
767}
768
769static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
770 struct net_device *dev, u16 reason_code)
771{
Kalle Valod6d5c062011-11-25 13:17:37 +0200772 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530773 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300774
775 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
776 reason_code);
777
Kalle Valo10509f92011-12-13 14:52:07 +0200778 ath6kl_cfg80211_sscan_disable(vif);
779
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530780 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300781 return -EIO;
782
783 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
784 ath6kl_err("busy, destroy in progress\n");
785 return -EBUSY;
786 }
787
788 if (down_interruptible(&ar->sem)) {
789 ath6kl_err("busy, couldn't get access\n");
790 return -ERESTARTSYS;
791 }
792
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530793 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530794 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530795 memset(vif->ssid, 0, sizeof(vif->ssid));
796 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300797
798 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530799 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300800
801 up(&ar->sem);
802
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530803 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530804
Kalle Valobdcd8172011-07-18 00:22:30 +0300805 return 0;
806}
807
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530808void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300809 u8 *bssid, u8 assoc_resp_len,
810 u8 *assoc_info, u16 proto_reason)
811{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530812 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530813
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530814 if (vif->scan_req) {
815 cfg80211_scan_done(vif->scan_req, true);
816 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300817 }
818
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530819 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530820 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300821 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
822 "%s: ath6k not in ibss mode\n", __func__);
823 return;
824 }
825 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530826 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300827 return;
828 }
829
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530830 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530831 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
832 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300833 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
834 "%s: ath6k not in station mode\n", __func__);
835 return;
836 }
837 }
838
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530839 /*
840 * Send a disconnect command to target when a disconnect event is
841 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
842 * request from host) to make the firmware stop trying to connect even
843 * after giving disconnect event. There will be one more disconnect
844 * event for this disconnect command with reason code DISCONNECT_CMD
845 * which will be notified to cfg80211.
846 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300847
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530848 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530849 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300850 return;
851 }
852
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530853 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300854
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530855 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530856 cfg80211_connect_result(vif->ndev,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200857 bssid, NULL, 0,
858 NULL, 0,
859 WLAN_STATUS_UNSPECIFIED_FAILURE,
860 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530861 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530862 cfg80211_disconnected(vif->ndev, reason,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200863 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300864 }
865
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530866 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300867}
868
Kalle Valobdcd8172011-07-18 00:22:30 +0300869static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
870 struct cfg80211_scan_request *request)
871{
Kalle Valod6d5c062011-11-25 13:17:37 +0200872 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530873 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300874 s8 n_channels = 0;
875 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300876 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530877 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300878
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530879 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300880 return -EIO;
881
Kalle Valo10509f92011-12-13 14:52:07 +0200882 ath6kl_cfg80211_sscan_disable(vif);
883
Kalle Valobdcd8172011-07-18 00:22:30 +0300884 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530885 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300886 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530887 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530888 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300889 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
890 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300891 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300892 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300893 }
894 }
895
896 if (request->n_ssids && request->ssids[0].ssid_len) {
897 u8 i;
898
899 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
900 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
901
902 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530903 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
904 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300905 request->ssids[i].ssid_len,
906 request->ssids[i].ssid);
907 }
908
Aarthi Thiruvengadam080eec42012-02-28 09:17:04 -0800909 /* this also clears IE in fw if it's not set */
910 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
911 WMI_FRAME_PROBE_REQ,
912 request->ie, request->ie_len);
913 if (ret) {
914 ath6kl_err("failed to set Probe Request appie for "
915 "scan");
916 return ret;
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300917 }
918
Jouni Malinen11869be2011-09-02 20:07:06 +0300919 /*
920 * Scan only the requested channels if the request specifies a set of
921 * channels. If the list is longer than the target supports, do not
922 * configure the list and instead, scan all available channels.
923 */
924 if (request->n_channels > 0 &&
925 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300926 u8 i;
927
Jouni Malinen11869be2011-09-02 20:07:06 +0300928 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300929
930 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
931 if (channels == NULL) {
932 ath6kl_warn("failed to set scan channels, "
933 "scan all channels");
934 n_channels = 0;
935 }
936
937 for (i = 0; i < n_channels; i++)
938 channels[i] = request->channels[i]->center_freq;
939 }
940
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530941 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530942 force_fg_scan = 1;
943
Raja Mani5b35dff2012-03-28 18:50:35 +0530944 vif->scan_req = request;
945
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800946 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200947 ar->fw_capabilities)) {
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800948 /*
949 * If capable of doing P2P mgmt operations using
950 * station interface, send additional information like
951 * supported rates to advertise and xmit rates for
952 * probe requests
953 */
954 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
955 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530956 false, 0,
957 ATH6KL_FG_SCAN_INTERVAL,
958 n_channels, channels,
959 request->no_cck,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800960 request->rates);
961 } else {
962 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
963 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530964 false, 0,
965 ATH6KL_FG_SCAN_INTERVAL,
966 n_channels, channels);
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800967 }
Raja Mani5b35dff2012-03-28 18:50:35 +0530968 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300969 ath6kl_err("wmi_startscan_cmd failed\n");
Raja Mani5b35dff2012-03-28 18:50:35 +0530970 vif->scan_req = NULL;
971 }
Kalle Valobdcd8172011-07-18 00:22:30 +0300972
Edward Lu1276c9e2011-08-30 21:58:00 +0300973 kfree(channels);
974
Kalle Valobdcd8172011-07-18 00:22:30 +0300975 return ret;
976}
977
Kalle Valo1c17d312011-11-01 08:43:56 +0200978void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300979{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530980 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300981 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300982
Kalle Valo1c17d312011-11-01 08:43:56 +0200983 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
984 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300985
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530986 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300987 return;
Kalle Valobdcd8172011-07-18 00:22:30 +0300988
Kalle Valo1c17d312011-11-01 08:43:56 +0200989 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300990 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300991
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530992 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
993 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530994 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
995 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300996 0, NULL);
997 }
998 }
999
1000out:
Kalle Valocb938212011-10-27 18:47:46 +03001001 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301002 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001003}
1004
1005static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1006 u8 key_index, bool pairwise,
1007 const u8 *mac_addr,
1008 struct key_params *params)
1009{
Kalle Valod6d5c062011-11-25 13:17:37 +02001010 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301011 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001012 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301013 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001014 u8 key_usage;
1015 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001016
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301017 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001018 return -EIO;
1019
Jouni Malinen837cb972011-10-11 17:31:57 +03001020 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
1021 if (params->key_len != WMI_KRK_LEN)
1022 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301023 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
1024 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +03001025 }
1026
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301027 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001028 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1029 "%s: key index %d out of bounds\n", __func__,
1030 key_index);
1031 return -ENOENT;
1032 }
1033
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301034 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001035 memset(key, 0, sizeof(struct ath6kl_key));
1036
1037 if (pairwise)
1038 key_usage = PAIRWISE_USAGE;
1039 else
1040 key_usage = GROUP_USAGE;
1041
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301042 seq_len = params->seq_len;
1043 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1044 seq_len > ATH6KL_KEY_SEQ_LEN) {
1045 /* Only first half of the WPI PN is configured */
1046 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001047 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301048 if (params->key_len > WLAN_MAX_KEY_LEN ||
1049 seq_len > sizeof(key->seq))
1050 return -EINVAL;
1051
1052 key->key_len = params->key_len;
1053 memcpy(key->key, params->key, key->key_len);
1054 key->seq_len = seq_len;
1055 memcpy(key->seq, params->seq, key->seq_len);
1056 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001057
1058 switch (key->cipher) {
1059 case WLAN_CIPHER_SUITE_WEP40:
1060 case WLAN_CIPHER_SUITE_WEP104:
1061 key_type = WEP_CRYPT;
1062 break;
1063
1064 case WLAN_CIPHER_SUITE_TKIP:
1065 key_type = TKIP_CRYPT;
1066 break;
1067
1068 case WLAN_CIPHER_SUITE_CCMP:
1069 key_type = AES_CRYPT;
1070 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001071 case WLAN_CIPHER_SUITE_SMS4:
1072 key_type = WAPI_CRYPT;
1073 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001074
1075 default:
1076 return -ENOTSUPP;
1077 }
1078
Kalle Valoddc3d772012-03-07 20:03:58 +02001079 if (((vif->auth_mode == WPA_PSK_AUTH) ||
1080 (vif->auth_mode == WPA2_PSK_AUTH)) &&
1081 (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301082 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001083
1084 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1085 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1086 __func__, key_index, key->key_len, key_type,
1087 key_usage, key->seq_len);
1088
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301089 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001090 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
Vasanthakumar Thiagarajancc4d6232012-02-14 20:33:00 +05301091 key_type == WAPI_CRYPT)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001092 ar->ap_mode_bkey.valid = true;
1093 ar->ap_mode_bkey.key_index = key_index;
1094 ar->ap_mode_bkey.key_type = key_type;
1095 ar->ap_mode_bkey.key_len = key->key_len;
1096 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301097 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001098 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1099 "key configuration until AP mode has been "
1100 "started\n");
1101 /*
1102 * The key will be set in ath6kl_connect_ap_mode() once
1103 * the connected event is received from the target.
1104 */
1105 return 0;
1106 }
1107 }
1108
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301109 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301110 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001111 /*
1112 * Store the key locally so that it can be re-configured after
1113 * the AP mode has properly started
1114 * (ath6kl_install_statioc_wep_keys).
1115 */
1116 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1117 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301118 vif->wep_key_list[key_index].key_len = key->key_len;
1119 memcpy(vif->wep_key_list[key_index].key, key->key,
1120 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001121 return 0;
1122 }
1123
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301124 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001125 key_type, key_usage, key->key_len,
1126 key->seq, key->seq_len, key->key,
1127 KEY_OP_INIT_VAL,
1128 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001129}
1130
1131static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1132 u8 key_index, bool pairwise,
1133 const u8 *mac_addr)
1134{
Kalle Valod6d5c062011-11-25 13:17:37 +02001135 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301136 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001137
1138 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1139
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301140 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001141 return -EIO;
1142
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301143 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001144 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1145 "%s: key index %d out of bounds\n", __func__,
1146 key_index);
1147 return -ENOENT;
1148 }
1149
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301150 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001151 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1152 "%s: index %d is empty\n", __func__, key_index);
1153 return 0;
1154 }
1155
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301156 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001157
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301158 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001159}
1160
1161static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1162 u8 key_index, bool pairwise,
1163 const u8 *mac_addr, void *cookie,
1164 void (*callback) (void *cookie,
1165 struct key_params *))
1166{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301167 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001168 struct ath6kl_key *key = NULL;
1169 struct key_params params;
1170
1171 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1172
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301173 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001174 return -EIO;
1175
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301176 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001177 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1178 "%s: key index %d out of bounds\n", __func__,
1179 key_index);
1180 return -ENOENT;
1181 }
1182
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301183 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001184 memset(&params, 0, sizeof(params));
1185 params.cipher = key->cipher;
1186 params.key_len = key->key_len;
1187 params.seq_len = key->seq_len;
1188 params.seq = key->seq;
1189 params.key = key->key;
1190
1191 callback(cookie, &params);
1192
1193 return key->key_len ? 0 : -ENOENT;
1194}
1195
1196static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1197 struct net_device *ndev,
1198 u8 key_index, bool unicast,
1199 bool multicast)
1200{
Kalle Valod6d5c062011-11-25 13:17:37 +02001201 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301202 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001203 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001204 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001205 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001206
1207 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1208
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301209 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001210 return -EIO;
1211
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301212 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001213 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1214 "%s: key index %d out of bounds\n",
1215 __func__, key_index);
1216 return -ENOENT;
1217 }
1218
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301219 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001220 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1221 __func__, key_index);
1222 return -EINVAL;
1223 }
1224
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301225 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301226 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001227 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301228 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001229 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001230 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301231 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001232 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301233 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001234
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301235 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001236 return 0; /* Delay until AP mode has been started */
1237
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001238 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1239 vif->def_txkey_index,
1240 key_type, key_usage,
1241 key->key_len, key->seq, key->seq_len,
1242 key->key,
1243 KEY_OP_INIT_VAL, NULL,
1244 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001245}
1246
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301247void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001248 bool ismcast)
1249{
1250 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1251 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1252
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301253 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001254 (ismcast ? NL80211_KEYTYPE_GROUP :
1255 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1256 GFP_KERNEL);
1257}
1258
1259static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1260{
1261 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301262 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001263 int ret;
1264
1265 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1266 changed);
1267
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301268 vif = ath6kl_vif_first(ar);
1269 if (!vif)
1270 return -EIO;
1271
1272 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001273 return -EIO;
1274
1275 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1276 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1277 if (ret != 0) {
1278 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1279 return -EIO;
1280 }
1281 }
1282
1283 return 0;
1284}
1285
1286/*
1287 * The type nl80211_tx_power_setting replaces the following
1288 * data type from 2.6.36 onwards
1289*/
1290static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1291 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001292 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001293{
1294 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301295 struct ath6kl_vif *vif;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001296 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001297
1298 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1299 type, dbm);
1300
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301301 vif = ath6kl_vif_first(ar);
1302 if (!vif)
1303 return -EIO;
1304
1305 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001306 return -EIO;
1307
1308 switch (type) {
1309 case NL80211_TX_POWER_AUTOMATIC:
1310 return 0;
1311 case NL80211_TX_POWER_LIMITED:
Kalle Valod0d670a2012-03-07 20:03:58 +02001312 ar->tx_pwr = dbm;
Kalle Valobdcd8172011-07-18 00:22:30 +03001313 break;
1314 default:
1315 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1316 __func__, type);
1317 return -EOPNOTSUPP;
1318 }
1319
Kalle Valod0d670a2012-03-07 20:03:58 +02001320 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001321
1322 return 0;
1323}
1324
1325static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1326{
1327 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301328 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001329
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301330 vif = ath6kl_vif_first(ar);
1331 if (!vif)
1332 return -EIO;
1333
1334 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001335 return -EIO;
1336
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301337 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001338 ar->tx_pwr = 0;
1339
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301340 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001341 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1342 return -EIO;
1343 }
1344
1345 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1346 5 * HZ);
1347
1348 if (signal_pending(current)) {
1349 ath6kl_err("target did not respond\n");
1350 return -EINTR;
1351 }
1352 }
1353
1354 *dbm = ar->tx_pwr;
1355 return 0;
1356}
1357
1358static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1359 struct net_device *dev,
1360 bool pmgmt, int timeout)
1361{
1362 struct ath6kl *ar = ath6kl_priv(dev);
1363 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301364 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001365
1366 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1367 __func__, pmgmt, timeout);
1368
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301369 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001370 return -EIO;
1371
1372 if (pmgmt) {
1373 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1374 mode.pwr_mode = REC_POWER;
1375 } else {
1376 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1377 mode.pwr_mode = MAX_PERF_POWER;
1378 }
1379
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301380 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001381 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001382 ath6kl_err("wmi_powermode_cmd failed\n");
1383 return -EIO;
1384 }
1385
1386 return 0;
1387}
1388
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301389static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1390 char *name,
1391 enum nl80211_iftype type,
1392 u32 *flags,
1393 struct vif_params *params)
1394{
1395 struct ath6kl *ar = wiphy_priv(wiphy);
1396 struct net_device *ndev;
1397 u8 if_idx, nw_type;
1398
Kalle Valo71f96ee2011-11-14 19:31:30 +02001399 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301400 ath6kl_err("Reached maximum number of supported vif\n");
1401 return ERR_PTR(-EINVAL);
1402 }
1403
1404 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1405 ath6kl_err("Not a supported interface type\n");
1406 return ERR_PTR(-EINVAL);
1407 }
1408
1409 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1410 if (!ndev)
1411 return ERR_PTR(-ENOMEM);
1412
1413 ar->num_vif++;
1414
1415 return ndev;
1416}
1417
1418static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1419 struct net_device *ndev)
1420{
1421 struct ath6kl *ar = wiphy_priv(wiphy);
1422 struct ath6kl_vif *vif = netdev_priv(ndev);
1423
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301424 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301425 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301426 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301427
1428 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1429
Kalle Valoc25889e2012-01-17 20:08:27 +02001430 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301431
1432 return 0;
1433}
1434
Kalle Valobdcd8172011-07-18 00:22:30 +03001435static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1436 struct net_device *ndev,
1437 enum nl80211_iftype type, u32 *flags,
1438 struct vif_params *params)
1439{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301440 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001441
1442 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1443
Kalle Valobdcd8172011-07-18 00:22:30 +03001444 switch (type) {
1445 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301446 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001447 break;
1448 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301449 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001450 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001451 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301452 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001453 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001454 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301455 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001456 break;
1457 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301458 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001459 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001460 default:
1461 ath6kl_err("invalid interface type %u\n", type);
1462 return -EOPNOTSUPP;
1463 }
1464
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301465 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001466
1467 return 0;
1468}
1469
1470static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1471 struct net_device *dev,
1472 struct cfg80211_ibss_params *ibss_param)
1473{
1474 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301475 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001476 int status;
1477
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301478 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001479 return -EIO;
1480
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301481 vif->ssid_len = ibss_param->ssid_len;
1482 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001483
1484 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301485 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001486
1487 if (ibss_param->channel_fixed) {
1488 /*
1489 * TODO: channel_fixed: The channel should be fixed, do not
1490 * search for IBSSs to join on other channels. Target
1491 * firmware does not support this feature, needs to be
1492 * updated.
1493 */
1494 return -EOPNOTSUPP;
1495 }
1496
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301497 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001498 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301499 memcpy(vif->req_bssid, ibss_param->bssid,
1500 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001501
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301502 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001503
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301504 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001505 if (status)
1506 return status;
1507
1508 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301509 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1510 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001511 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301512 ath6kl_set_cipher(vif, 0, true);
1513 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001514 }
1515
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301516 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001517
1518 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1519 "%s: connect called with authmode %d dot11 auth %d"
1520 " PW crypto %d PW crypto len %d GRP crypto %d"
1521 " GRP crypto len %d channel hint %u\n",
1522 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301523 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1524 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301525 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001526
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301527 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301528 vif->dot11_auth_mode, vif->auth_mode,
1529 vif->prwise_crypto,
1530 vif->prwise_crypto_len,
1531 vif->grp_crypto, vif->grp_crypto_len,
1532 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301533 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001534 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301535 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001536
1537 return 0;
1538}
1539
1540static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1541 struct net_device *dev)
1542{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301543 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001544
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301545 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001546 return -EIO;
1547
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301548 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301549 memset(vif->ssid, 0, sizeof(vif->ssid));
1550 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001551
1552 return 0;
1553}
1554
1555static const u32 cipher_suites[] = {
1556 WLAN_CIPHER_SUITE_WEP40,
1557 WLAN_CIPHER_SUITE_WEP104,
1558 WLAN_CIPHER_SUITE_TKIP,
1559 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001560 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001561 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001562};
1563
1564static bool is_rate_legacy(s32 rate)
1565{
1566 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1567 6000, 9000, 12000, 18000, 24000,
1568 36000, 48000, 54000
1569 };
1570 u8 i;
1571
1572 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1573 if (rate == legacy[i])
1574 return true;
1575
1576 return false;
1577}
1578
1579static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1580{
1581 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1582 52000, 58500, 65000, 72200
1583 };
1584 u8 i;
1585
1586 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1587 if (rate == ht20[i]) {
1588 if (i == ARRAY_SIZE(ht20) - 1)
1589 /* last rate uses sgi */
1590 *sgi = true;
1591 else
1592 *sgi = false;
1593
1594 *mcs = i;
1595 return true;
1596 }
1597 }
1598 return false;
1599}
1600
1601static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1602{
1603 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1604 81000, 108000, 121500, 135000,
1605 150000
1606 };
1607 u8 i;
1608
1609 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1610 if (rate == ht40[i]) {
1611 if (i == ARRAY_SIZE(ht40) - 1)
1612 /* last rate uses sgi */
1613 *sgi = true;
1614 else
1615 *sgi = false;
1616
1617 *mcs = i;
1618 return true;
1619 }
1620 }
1621
1622 return false;
1623}
1624
1625static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1626 u8 *mac, struct station_info *sinfo)
1627{
1628 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301629 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001630 long left;
1631 bool sgi;
1632 s32 rate;
1633 int ret;
1634 u8 mcs;
1635
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301636 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001637 return -ENOENT;
1638
1639 if (down_interruptible(&ar->sem))
1640 return -EBUSY;
1641
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301642 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001643
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301644 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001645
1646 if (ret != 0) {
1647 up(&ar->sem);
1648 return -EIO;
1649 }
1650
1651 left = wait_event_interruptible_timeout(ar->event_wq,
1652 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301653 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001654 WMI_TIMEOUT);
1655
1656 up(&ar->sem);
1657
1658 if (left == 0)
1659 return -ETIMEDOUT;
1660 else if (left < 0)
1661 return left;
1662
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301663 if (vif->target_stats.rx_byte) {
1664 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001665 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301666 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001667 sinfo->filled |= STATION_INFO_RX_PACKETS;
1668 }
1669
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301670 if (vif->target_stats.tx_byte) {
1671 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001672 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301673 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001674 sinfo->filled |= STATION_INFO_TX_PACKETS;
1675 }
1676
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301677 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001678 sinfo->filled |= STATION_INFO_SIGNAL;
1679
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301680 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001681
1682 if (is_rate_legacy(rate)) {
1683 sinfo->txrate.legacy = rate / 100;
1684 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1685 if (sgi) {
1686 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1687 sinfo->txrate.mcs = mcs - 1;
1688 } else {
1689 sinfo->txrate.mcs = mcs;
1690 }
1691
1692 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1693 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1694 if (sgi) {
1695 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1696 sinfo->txrate.mcs = mcs - 1;
1697 } else {
1698 sinfo->txrate.mcs = mcs;
1699 }
1700
1701 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1702 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1703 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001704 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1705 "invalid rate from stats: %d\n", rate);
1706 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001707 return 0;
1708 }
1709
1710 sinfo->filled |= STATION_INFO_TX_BITRATE;
1711
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301712 if (test_bit(CONNECTED, &vif->flags) &&
1713 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301714 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001715 sinfo->filled |= STATION_INFO_BSS_PARAM;
1716 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301717 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1718 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001719 }
1720
Kalle Valobdcd8172011-07-18 00:22:30 +03001721 return 0;
1722}
1723
1724static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1725 struct cfg80211_pmksa *pmksa)
1726{
1727 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301728 struct ath6kl_vif *vif = netdev_priv(netdev);
1729
1730 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001731 pmksa->pmkid, true);
1732}
1733
1734static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1735 struct cfg80211_pmksa *pmksa)
1736{
1737 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301738 struct ath6kl_vif *vif = netdev_priv(netdev);
1739
1740 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001741 pmksa->pmkid, false);
1742}
1743
1744static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1745{
1746 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301747 struct ath6kl_vif *vif = netdev_priv(netdev);
1748
1749 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301750 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1751 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001752 return 0;
1753}
1754
Raja Manid91e8ee2012-01-30 17:13:10 +05301755static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1756 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001757{
Raja Manid91e8ee2012-01-30 17:13:10 +05301758 int ret, pos;
1759 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001760 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001761
Raja Manid91e8ee2012-01-30 17:13:10 +05301762 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001763 for (i = 0; i < wow->n_patterns; i++) {
1764
1765 /*
1766 * Convert given nl80211 specific mask value to equivalent
1767 * driver specific mask value and send it to the chip along
1768 * with patterns. For example, If the mask value defined in
1769 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1770 * then equivalent driver specific mask value is
1771 * "0xFF 0x00 0xFF 0x00".
1772 */
1773 memset(&mask, 0, sizeof(mask));
1774 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1775 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1776 mask[pos] = 0xFF;
1777 }
1778 /*
1779 * Note: Pattern's offset is not passed as part of wowlan
1780 * parameter from CFG layer. So it's always passed as ZERO
1781 * to the firmware. It means, given WOW patterns are always
1782 * matched from the first byte of received pkt in the firmware.
1783 */
1784 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301785 vif->fw_vif_idx, WOW_LIST_ID,
1786 wow->patterns[i].pattern_len,
1787 0 /* pattern offset */,
1788 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001789 if (ret)
1790 return ret;
1791 }
1792
Raja Manid91e8ee2012-01-30 17:13:10 +05301793 if (wow->disconnect)
1794 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1795
1796 if (wow->magic_pkt)
1797 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1798
1799 if (wow->gtk_rekey_failure)
1800 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1801
1802 if (wow->eap_identity_req)
1803 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1804
1805 if (wow->four_way_handshake)
1806 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1807
1808 return 0;
1809}
1810
1811static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1812{
1813 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1814 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1815 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1816 0x00, 0x08 };
1817 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1818 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1819 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1820 0x00, 0x7f };
1821 u8 unicst_offset = 0;
1822 static const u8 arp_pattern[] = { 0x08, 0x06 };
1823 static const u8 arp_mask[] = { 0xff, 0xff };
1824 u8 arp_offset = 20;
1825 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1826 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1827 u8 discvr_offset = 38;
1828 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1829 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1830 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1831 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1832 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1833 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1834 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1835 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1836 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1837 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1838 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1839 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1840 u8 dhcp_offset = 0;
1841 int ret;
1842
1843 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1844 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1845 vif->fw_vif_idx, WOW_LIST_ID,
1846 sizeof(unicst_pattern), unicst_offset,
1847 unicst_pattern, unicst_mask);
1848 if (ret) {
1849 ath6kl_err("failed to add WOW unicast IP pattern\n");
1850 return ret;
1851 }
1852
1853 /* Setup all ARP pkt pattern */
1854 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1855 vif->fw_vif_idx, WOW_LIST_ID,
1856 sizeof(arp_pattern), arp_offset,
1857 arp_pattern, arp_mask);
1858 if (ret) {
1859 ath6kl_err("failed to add WOW ARP pattern\n");
1860 return ret;
1861 }
1862
1863 /*
1864 * Setup multicast pattern for mDNS 224.0.0.251,
1865 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1866 */
1867 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1868 vif->fw_vif_idx, WOW_LIST_ID,
1869 sizeof(discvr_pattern), discvr_offset,
1870 discvr_pattern, discvr_mask);
1871 if (ret) {
1872 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1873 return ret;
1874 }
1875
1876 /* Setup all DHCP broadcast pkt pattern */
1877 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1878 vif->fw_vif_idx, WOW_LIST_ID,
1879 sizeof(dhcp_pattern), dhcp_offset,
1880 dhcp_pattern, dhcp_mask);
1881 if (ret) {
1882 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
1883 return ret;
1884 }
1885
1886 return 0;
1887}
1888
1889static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
1890{
1891 struct net_device *ndev = vif->ndev;
1892 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1893 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1894 u8 discvr_offset = 38;
1895 u8 mac_mask[ETH_ALEN];
1896 int ret;
1897
1898 /* Setup unicast pkt pattern */
1899 memset(mac_mask, 0xff, ETH_ALEN);
1900 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1901 vif->fw_vif_idx, WOW_LIST_ID,
1902 ETH_ALEN, 0, ndev->dev_addr,
1903 mac_mask);
1904 if (ret) {
1905 ath6kl_err("failed to add WOW unicast pattern\n");
1906 return ret;
1907 }
1908
1909 /*
1910 * Setup multicast pattern for mDNS 224.0.0.251,
1911 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1912 */
1913 if ((ndev->flags & IFF_ALLMULTI) ||
1914 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
1915 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1916 vif->fw_vif_idx, WOW_LIST_ID,
1917 sizeof(discvr_pattern), discvr_offset,
1918 discvr_pattern, discvr_mask);
1919 if (ret) {
1920 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
1921 "pattern\n");
1922 return ret;
1923 }
1924 }
1925
1926 return 0;
1927}
1928
Raja Mani055bde42012-03-21 15:03:37 +05301929static int is_hsleep_mode_procsed(struct ath6kl_vif *vif)
1930{
1931 return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
1932}
1933
1934static bool is_ctrl_ep_empty(struct ath6kl *ar)
1935{
1936 return !ar->tx_pending[ar->ctrl_ep];
1937}
1938
1939static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
1940{
1941 int ret, left;
1942
1943 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
1944
1945 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1946 ATH6KL_HOST_MODE_ASLEEP);
1947 if (ret)
1948 return ret;
1949
1950 left = wait_event_interruptible_timeout(ar->event_wq,
1951 is_hsleep_mode_procsed(vif),
1952 WMI_TIMEOUT);
1953 if (left == 0) {
1954 ath6kl_warn("timeout, didn't get host sleep cmd processed event\n");
1955 ret = -ETIMEDOUT;
1956 } else if (left < 0) {
1957 ath6kl_warn("error while waiting for host sleep cmd processed event %d\n",
1958 left);
1959 ret = left;
1960 }
1961
1962 if (ar->tx_pending[ar->ctrl_ep]) {
1963 left = wait_event_interruptible_timeout(ar->event_wq,
1964 is_ctrl_ep_empty(ar),
1965 WMI_TIMEOUT);
1966 if (left == 0) {
1967 ath6kl_warn("clear wmi ctrl data timeout\n");
1968 ret = -ETIMEDOUT;
1969 } else if (left < 0) {
1970 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
1971 ret = left;
1972 }
1973 }
1974
1975 return ret;
1976}
1977
Raja Manid91e8ee2012-01-30 17:13:10 +05301978static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1979{
1980 struct in_device *in_dev;
1981 struct in_ifaddr *ifa;
1982 struct ath6kl_vif *vif;
Raja Mani055bde42012-03-21 15:03:37 +05301983 int ret;
Raja Manid91e8ee2012-01-30 17:13:10 +05301984 u32 filter = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +05301985 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05301986 u8 index = 0;
1987 __be32 ips[MAX_IP_ADDRS];
1988
1989 vif = ath6kl_vif_first(ar);
1990 if (!vif)
1991 return -EIO;
1992
1993 if (!ath6kl_cfg80211_ready(vif))
1994 return -EIO;
1995
1996 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05301997 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05301998
1999 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
2000 return -EINVAL;
2001
2002 /* Clear existing WOW patterns */
2003 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
2004 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
2005 WOW_LIST_ID, i);
2006
2007 /*
2008 * Skip the default WOW pattern configuration
2009 * if the driver receives any WOW patterns from
2010 * the user.
2011 */
2012 if (wow)
2013 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
2014 else if (vif->nw_type == AP_NETWORK)
2015 ret = ath6kl_wow_ap(ar, vif);
2016 else
2017 ret = ath6kl_wow_sta(ar, vif);
2018
2019 if (ret)
2020 return ret;
2021
Raja Mani390a8c82012-03-07 11:35:04 +05302022 netif_stop_queue(vif->ndev);
2023
Raja Manice0dc0c2012-02-20 19:08:08 +05302024 if (vif->nw_type != AP_NETWORK) {
2025 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2026 ATH6KL_MAX_WOW_LISTEN_INTL,
2027 0);
2028 if (ret)
2029 return ret;
2030
2031 /* Set listen interval x 15 times as bmiss time */
2032 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
2033 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
2034 bmiss_time = ATH6KL_MAX_BMISS_TIME;
2035
2036 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2037 bmiss_time, 0);
2038 if (ret)
2039 return ret;
2040
2041 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2042 0xFFFF, 0, 0xFFFF, 0, 0, 0,
2043 0, 0, 0, 0);
2044 if (ret)
2045 return ret;
2046 }
2047
Raja Mani390a8c82012-03-07 11:35:04 +05302048 ar->state = ATH6KL_STATE_SUSPENDING;
2049
Raja Manic08631c2011-12-16 14:24:24 +05302050 /* Setup own IP addr for ARP agent. */
2051 in_dev = __in_dev_get_rtnl(vif->ndev);
2052 if (!in_dev)
2053 goto skip_arp;
2054
2055 ifa = in_dev->ifa_list;
2056 memset(&ips, 0, sizeof(ips));
2057
2058 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2059 while (index < MAX_IP_ADDRS && ifa) {
2060 ips[index] = ifa->ifa_local;
2061 ifa = ifa->ifa_next;
2062 index++;
2063 }
2064
2065 if (ifa) {
2066 ath6kl_err("total IP addr count is exceeding fw limit\n");
2067 return -EINVAL;
2068 }
2069
2070 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2071 if (ret) {
2072 ath6kl_err("fail to setup ip for arp agent\n");
2073 return ret;
2074 }
2075
2076skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02002077 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2078 ATH6KL_WOW_MODE_ENABLE,
2079 filter,
2080 WOW_HOST_REQ_DELAY);
2081 if (ret)
2082 return ret;
2083
Raja Mani055bde42012-03-21 15:03:37 +05302084 ret = ath6kl_cfg80211_host_sleep(ar, vif);
Raja Mani6cb3c712011-11-07 22:52:45 +02002085 if (ret)
2086 return ret;
2087
Raja Mani055bde42012-03-21 15:03:37 +05302088 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002089}
2090
2091static int ath6kl_wow_resume(struct ath6kl *ar)
2092{
2093 struct ath6kl_vif *vif;
2094 int ret;
2095
2096 vif = ath6kl_vif_first(ar);
2097 if (!vif)
2098 return -EIO;
2099
Raja Mani390a8c82012-03-07 11:35:04 +05302100 ar->state = ATH6KL_STATE_RESUMING;
2101
Raja Mani6cb3c712011-11-07 22:52:45 +02002102 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2103 ATH6KL_HOST_MODE_AWAKE);
Raja Mani390a8c82012-03-07 11:35:04 +05302104 if (ret) {
2105 ath6kl_warn("Failed to configure host sleep mode for "
2106 "wow resume: %d\n", ret);
2107 ar->state = ATH6KL_STATE_WOW;
2108 return ret;
2109 }
2110
Raja Manice0dc0c2012-02-20 19:08:08 +05302111 if (vif->nw_type != AP_NETWORK) {
2112 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2113 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2114 if (ret)
2115 return ret;
2116
2117 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2118 vif->listen_intvl_t, 0);
2119 if (ret)
2120 return ret;
2121
2122 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2123 vif->bmiss_time_t, 0);
2124 if (ret)
2125 return ret;
2126 }
2127
Raja Mani390a8c82012-03-07 11:35:04 +05302128 ar->state = ATH6KL_STATE_ON;
2129
2130 netif_wake_queue(vif->ndev);
2131
2132 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002133}
2134
Raja Mani40abc2d2012-03-21 15:03:38 +05302135static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
2136{
2137 struct ath6kl_vif *vif;
2138 int ret;
2139
2140 vif = ath6kl_vif_first(ar);
2141 if (!vif)
2142 return -EIO;
2143
2144 if (!ath6kl_cfg80211_ready(vif))
2145 return -EIO;
2146
2147 ath6kl_cfg80211_stop_all(ar);
2148
2149 /* Save the current power mode before enabling power save */
2150 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2151
2152 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2153 if (ret)
2154 return ret;
2155
2156 /* Disable WOW mode */
2157 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2158 ATH6KL_WOW_MODE_DISABLE,
2159 0, 0);
2160 if (ret)
2161 return ret;
2162
2163 /* Flush all non control pkts in TX path */
2164 ath6kl_tx_data_cleanup(ar);
2165
2166 ret = ath6kl_cfg80211_host_sleep(ar, vif);
2167 if (ret)
2168 return ret;
2169
2170 return 0;
2171}
2172
2173static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
2174{
2175 struct ath6kl_vif *vif;
2176 int ret;
2177
2178 vif = ath6kl_vif_first(ar);
2179
2180 if (!vif)
2181 return -EIO;
2182
2183 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2184 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2185 ar->wmi->saved_pwr_mode);
2186 if (ret)
2187 return ret;
2188 }
2189
2190 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2191 ATH6KL_HOST_MODE_AWAKE);
2192 if (ret)
2193 return ret;
2194
2195 ar->state = ATH6KL_STATE_ON;
2196
2197 /* Reset scan parameter to default values */
2198 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2199 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2200 if (ret)
2201 return ret;
2202
2203 return 0;
2204}
2205
Kalle Valo52d81a62011-11-01 08:44:21 +02002206int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002207 enum ath6kl_cfg_suspend_mode mode,
2208 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002209{
Raja Mani390a8c82012-03-07 11:35:04 +05302210 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002211 int ret;
2212
Kalle Valo52d81a62011-11-01 08:44:21 +02002213 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002214 case ATH6KL_CFG_SUSPEND_WOW:
2215
2216 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2217
2218 /* Flush all non control pkts in TX path */
2219 ath6kl_tx_data_cleanup(ar);
2220
Raja Mani390a8c82012-03-07 11:35:04 +05302221 prev_state = ar->state;
2222
Raja Manid7c44e02011-11-07 22:52:46 +02002223 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302224 if (ret) {
2225 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002226 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302227 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302228
Raja Manid7c44e02011-11-07 22:52:46 +02002229 ar->state = ATH6KL_STATE_WOW;
2230 break;
2231
Kalle Valo52d81a62011-11-01 08:44:21 +02002232 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002233
Raja Mani40abc2d2012-03-21 15:03:38 +05302234 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
Raja Mani524441e2011-11-07 22:52:46 +02002235
Raja Mani40abc2d2012-03-21 15:03:38 +05302236 ret = ath6kl_cfg80211_deepsleep_suspend(ar);
Kalle Valo52d81a62011-11-01 08:44:21 +02002237 if (ret) {
Raja Mani40abc2d2012-03-21 15:03:38 +05302238 ath6kl_err("deepsleep suspend failed: %d\n", ret);
2239 return ret;
Kalle Valo52d81a62011-11-01 08:44:21 +02002240 }
2241
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002242 ar->state = ATH6KL_STATE_DEEPSLEEP;
2243
Kalle Valo52d81a62011-11-01 08:44:21 +02002244 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002245
2246 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002247
Kalle Valo7125f012011-12-13 14:51:37 +02002248 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002249
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002250 if (ar->state == ATH6KL_STATE_OFF) {
2251 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2252 "suspend hw off, no action for cutpower\n");
2253 break;
2254 }
2255
2256 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2257
2258 ret = ath6kl_init_hw_stop(ar);
2259 if (ret) {
2260 ath6kl_warn("failed to stop hw during suspend: %d\n",
2261 ret);
2262 }
2263
2264 ar->state = ATH6KL_STATE_CUTPOWER;
2265
2266 break;
2267
Kalle Valo10509f92011-12-13 14:52:07 +02002268 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2269 /*
2270 * Nothing needed for schedule scan, firmware is already in
2271 * wow mode and sleeping most of the time.
2272 */
2273 break;
2274
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002275 default:
2276 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002277 }
2278
2279 return 0;
2280}
Kalle Valod6a434d2012-01-17 20:09:36 +02002281EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002282
2283int ath6kl_cfg80211_resume(struct ath6kl *ar)
2284{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002285 int ret;
2286
2287 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002288 case ATH6KL_STATE_WOW:
2289 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2290
2291 ret = ath6kl_wow_resume(ar);
2292 if (ret) {
2293 ath6kl_warn("wow mode resume failed: %d\n", ret);
2294 return ret;
2295 }
2296
Raja Manid7c44e02011-11-07 22:52:46 +02002297 break;
2298
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002299 case ATH6KL_STATE_DEEPSLEEP:
Raja Mani40abc2d2012-03-21 15:03:38 +05302300 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
2301
2302 ret = ath6kl_cfg80211_deepsleep_resume(ar);
2303 if (ret) {
2304 ath6kl_warn("deep sleep resume failed: %d\n", ret);
2305 return ret;
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002306 }
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002307 break;
2308
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002309 case ATH6KL_STATE_CUTPOWER:
2310 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2311
2312 ret = ath6kl_init_hw_start(ar);
2313 if (ret) {
2314 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2315 return ret;
2316 }
Raja Manid7c44e02011-11-07 22:52:46 +02002317 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002318
Kalle Valo10509f92011-12-13 14:52:07 +02002319 case ATH6KL_STATE_SCHED_SCAN:
2320 break;
2321
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002322 default:
2323 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002324 }
2325
2326 return 0;
2327}
Kalle Valod6a434d2012-01-17 20:09:36 +02002328EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002329
Kalle Valoabcb3442011-07-22 08:26:20 +03002330#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002331
2332/* hif layer decides what suspend mode to use */
2333static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002334 struct cfg80211_wowlan *wow)
2335{
2336 struct ath6kl *ar = wiphy_priv(wiphy);
2337
Raja Mani0f60e9f2011-11-07 22:52:45 +02002338 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002339}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002340
Kalle Valo52d81a62011-11-01 08:44:21 +02002341static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002342{
2343 struct ath6kl *ar = wiphy_priv(wiphy);
2344
2345 return ath6kl_hif_resume(ar);
2346}
Raja Mania918fb32011-11-07 22:52:46 +02002347
2348/*
2349 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2350 * both sdio irq wake up and keep power. The target pulls sdio data line to
2351 * wake up the host when WOW pattern matches. This causes sdio irq handler
2352 * is being called in the host side which internally hits ath6kl's RX path.
2353 *
2354 * Since sdio interrupt is not disabled, RX path executes even before
2355 * the host executes the actual resume operation from PM module.
2356 *
2357 * In the current scenario, WOW resume should happen before start processing
2358 * any data from the target. So It's required to perform WOW resume in RX path.
2359 * Ideally we should perform WOW resume only in the actual platform
2360 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2361 *
2362 * ath6kl_check_wow_status() is called from ath6kl_rx().
2363 */
2364void ath6kl_check_wow_status(struct ath6kl *ar)
2365{
Raja Mani390a8c82012-03-07 11:35:04 +05302366 if (ar->state == ATH6KL_STATE_SUSPENDING)
2367 return;
2368
Raja Mania918fb32011-11-07 22:52:46 +02002369 if (ar->state == ATH6KL_STATE_WOW)
2370 ath6kl_cfg80211_resume(ar);
2371}
2372
2373#else
2374
2375void ath6kl_check_wow_status(struct ath6kl *ar)
2376{
2377}
Kalle Valoabcb3442011-07-22 08:26:20 +03002378#endif
2379
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002380static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2381 struct ieee80211_channel *chan,
2382 enum nl80211_channel_type channel_type)
2383{
Sujith Manoharane68f6752011-12-22 12:15:27 +05302384 struct ath6kl_vif *vif;
2385
2386 /*
2387 * 'dev' could be NULL if a channel change is required for the hardware
2388 * device itself, instead of a particular VIF.
2389 *
2390 * FIXME: To be handled properly when monitor mode is supported.
2391 */
2392 if (!dev)
2393 return -EBUSY;
2394
2395 vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002396
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302397 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002398 return -EIO;
2399
2400 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2401 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302402 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002403
2404 return 0;
2405}
2406
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002407static bool ath6kl_is_p2p_ie(const u8 *pos)
2408{
2409 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2410 pos[2] == 0x50 && pos[3] == 0x6f &&
2411 pos[4] == 0x9a && pos[5] == 0x09;
2412}
2413
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302414static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2415 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002416{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302417 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002418 const u8 *pos;
2419 u8 *buf = NULL;
2420 size_t len = 0;
2421 int ret;
2422
2423 /*
2424 * Filter out P2P IE(s) since they will be included depending on
2425 * the Probe Request frame in ath6kl_send_go_probe_resp().
2426 */
2427
2428 if (ies && ies_len) {
2429 buf = kmalloc(ies_len, GFP_KERNEL);
2430 if (buf == NULL)
2431 return -ENOMEM;
2432 pos = ies;
2433 while (pos + 1 < ies + ies_len) {
2434 if (pos + 2 + pos[1] > ies + ies_len)
2435 break;
2436 if (!ath6kl_is_p2p_ie(pos)) {
2437 memcpy(buf + len, pos, 2 + pos[1]);
2438 len += 2 + pos[1];
2439 }
2440 pos += 2 + pos[1];
2441 }
2442 }
2443
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302444 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2445 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002446 kfree(buf);
2447 return ret;
2448}
2449
Johannes Berg88600202012-02-13 15:17:18 +01002450static int ath6kl_set_ies(struct ath6kl_vif *vif,
2451 struct cfg80211_beacon_data *info)
2452{
2453 struct ath6kl *ar = vif->ar;
2454 int res;
2455
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002456 /* this also clears IE in fw if it's not set */
2457 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2458 WMI_FRAME_BEACON,
2459 info->beacon_ies,
2460 info->beacon_ies_len);
2461 if (res)
2462 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002463
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002464 /* this also clears IE in fw if it's not set */
2465 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2466 info->proberesp_ies_len);
2467 if (res)
2468 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002469
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002470 /* this also clears IE in fw if it's not set */
2471 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2472 WMI_FRAME_ASSOC_RESP,
2473 info->assocresp_ies,
2474 info->assocresp_ies_len);
2475 if (res)
2476 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002477
2478 return 0;
2479}
2480
2481static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2482 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002483{
2484 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302485 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002486 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002487 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002488 u8 *ies;
2489 int ies_len;
2490 struct wmi_connect_cmd p;
2491 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302492 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002493
Johannes Berg88600202012-02-13 15:17:18 +01002494 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002495
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302496 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002497 return -EIO;
2498
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302499 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002500 return -EOPNOTSUPP;
2501
Johannes Berg88600202012-02-13 15:17:18 +01002502 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002503
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002504 ar->ap_mode_bkey.valid = false;
2505
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002506 /* TODO:
2507 * info->interval
2508 * info->dtim_period
2509 */
2510
Johannes Berg88600202012-02-13 15:17:18 +01002511 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002512 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002513 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002514 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002515 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002516 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002517 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002518
2519 if (info->ssid == NULL)
2520 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302521 memcpy(vif->ssid, info->ssid, info->ssid_len);
2522 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002523 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002524 hidden = true;
2525
2526 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2527 if (res)
2528 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002529
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302530 ret = ath6kl_set_auth_type(vif, info->auth_type);
2531 if (ret)
2532 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002533
2534 memset(&p, 0, sizeof(p));
2535
2536 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2537 switch (info->crypto.akm_suites[i]) {
2538 case WLAN_AKM_SUITE_8021X:
2539 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2540 p.auth_mode |= WPA_AUTH;
2541 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2542 p.auth_mode |= WPA2_AUTH;
2543 break;
2544 case WLAN_AKM_SUITE_PSK:
2545 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2546 p.auth_mode |= WPA_PSK_AUTH;
2547 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2548 p.auth_mode |= WPA2_PSK_AUTH;
2549 break;
2550 }
2551 }
2552 if (p.auth_mode == 0)
2553 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302554 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002555
2556 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2557 switch (info->crypto.ciphers_pairwise[i]) {
2558 case WLAN_CIPHER_SUITE_WEP40:
2559 case WLAN_CIPHER_SUITE_WEP104:
2560 p.prwise_crypto_type |= WEP_CRYPT;
2561 break;
2562 case WLAN_CIPHER_SUITE_TKIP:
2563 p.prwise_crypto_type |= TKIP_CRYPT;
2564 break;
2565 case WLAN_CIPHER_SUITE_CCMP:
2566 p.prwise_crypto_type |= AES_CRYPT;
2567 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002568 case WLAN_CIPHER_SUITE_SMS4:
2569 p.prwise_crypto_type |= WAPI_CRYPT;
2570 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002571 }
2572 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002573 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002574 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302575 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002576 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302577 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002578
2579 switch (info->crypto.cipher_group) {
2580 case WLAN_CIPHER_SUITE_WEP40:
2581 case WLAN_CIPHER_SUITE_WEP104:
2582 p.grp_crypto_type = WEP_CRYPT;
2583 break;
2584 case WLAN_CIPHER_SUITE_TKIP:
2585 p.grp_crypto_type = TKIP_CRYPT;
2586 break;
2587 case WLAN_CIPHER_SUITE_CCMP:
2588 p.grp_crypto_type = AES_CRYPT;
2589 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002590 case WLAN_CIPHER_SUITE_SMS4:
2591 p.grp_crypto_type = WAPI_CRYPT;
2592 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002593 default:
2594 p.grp_crypto_type = NONE_CRYPT;
2595 break;
2596 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302597 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002598
2599 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302600 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002601
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302602 p.ssid_len = vif->ssid_len;
2603 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2604 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302605 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002606
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302607 /* Enable uAPSD support by default */
2608 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2609 if (res < 0)
2610 return res;
2611
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002612 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2613 p.nw_subtype = SUBTYPE_P2PGO;
2614 } else {
2615 /*
2616 * Due to firmware limitation, it is not possible to
2617 * do P2P mgmt operations in AP mode
2618 */
2619 p.nw_subtype = SUBTYPE_NONE;
2620 }
2621
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05302622 if (info->inactivity_timeout) {
2623 res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
2624 info->inactivity_timeout);
2625 if (res < 0)
2626 return res;
2627 }
2628
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302629 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002630 if (res < 0)
2631 return res;
2632
2633 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002634}
2635
Johannes Berg88600202012-02-13 15:17:18 +01002636static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2637 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002638{
Johannes Berg88600202012-02-13 15:17:18 +01002639 struct ath6kl_vif *vif = netdev_priv(dev);
2640
2641 if (!ath6kl_cfg80211_ready(vif))
2642 return -EIO;
2643
2644 if (vif->next_mode != AP_NETWORK)
2645 return -EOPNOTSUPP;
2646
2647 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002648}
2649
Johannes Berg88600202012-02-13 15:17:18 +01002650static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002651{
2652 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302653 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002654
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302655 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002656 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302657 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002658 return -ENOTCONN;
2659
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302660 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302661 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002662
2663 return 0;
2664}
2665
Jouni Malinen33e53082011-12-27 11:02:56 +02002666static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2667
2668static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2669 u8 *mac)
2670{
2671 struct ath6kl *ar = ath6kl_priv(dev);
2672 struct ath6kl_vif *vif = netdev_priv(dev);
2673 const u8 *addr = mac ? mac : bcast_addr;
2674
2675 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2676 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2677}
2678
Jouni Malinen23875132011-08-30 21:57:53 +03002679static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2680 u8 *mac, struct station_parameters *params)
2681{
2682 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302683 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002684
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302685 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002686 return -EOPNOTSUPP;
2687
2688 /* Use this only for authorizing/unauthorizing a station */
2689 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2690 return -EOPNOTSUPP;
2691
2692 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302693 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2694 WMI_AP_MLME_AUTHORIZE, mac, 0);
2695 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2696 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002697}
2698
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002699static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2700 struct net_device *dev,
2701 struct ieee80211_channel *chan,
2702 enum nl80211_channel_type channel_type,
2703 unsigned int duration,
2704 u64 *cookie)
2705{
2706 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302707 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002708 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002709
2710 /* TODO: if already pending or ongoing remain-on-channel,
2711 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002712 id = ++vif->last_roc_id;
2713 if (id == 0) {
2714 /* Do not use 0 as the cookie value */
2715 id = ++vif->last_roc_id;
2716 }
2717 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002718
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302719 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2720 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002721}
2722
2723static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2724 struct net_device *dev,
2725 u64 cookie)
2726{
2727 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302728 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002729
Jouni Malinen10522612011-10-27 16:00:13 +03002730 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002731 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002732 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002733
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302734 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002735}
2736
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302737static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2738 const u8 *buf, size_t len,
2739 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002740{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302741 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002742 const u8 *pos;
2743 u8 *p2p;
2744 int p2p_len;
2745 int ret;
2746 const struct ieee80211_mgmt *mgmt;
2747
2748 mgmt = (const struct ieee80211_mgmt *) buf;
2749
2750 /* Include P2P IE(s) from the frame generated in user space. */
2751
2752 p2p = kmalloc(len, GFP_KERNEL);
2753 if (p2p == NULL)
2754 return -ENOMEM;
2755 p2p_len = 0;
2756
2757 pos = mgmt->u.probe_resp.variable;
2758 while (pos + 1 < buf + len) {
2759 if (pos + 2 + pos[1] > buf + len)
2760 break;
2761 if (ath6kl_is_p2p_ie(pos)) {
2762 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2763 p2p_len += 2 + pos[1];
2764 }
2765 pos += 2 + pos[1];
2766 }
2767
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302768 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2769 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002770 kfree(p2p);
2771 return ret;
2772}
2773
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002774static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
2775 u32 id,
2776 u32 freq,
2777 u32 wait,
2778 const u8 *buf,
2779 size_t len,
2780 bool *more_data,
2781 bool no_cck)
2782{
2783 struct ieee80211_mgmt *mgmt;
2784 struct ath6kl_sta *conn;
2785 bool is_psq_empty = false;
2786 struct ath6kl_mgmt_buff *mgmt_buf;
2787 size_t mgmt_buf_size;
2788 struct ath6kl *ar = vif->ar;
2789
2790 mgmt = (struct ieee80211_mgmt *) buf;
2791 if (is_multicast_ether_addr(mgmt->da))
2792 return false;
2793
2794 conn = ath6kl_find_sta(vif, mgmt->da);
2795 if (!conn)
2796 return false;
2797
2798 if (conn->sta_flags & STA_PS_SLEEP) {
2799 if (!(conn->sta_flags & STA_PS_POLLED)) {
2800 /* Queue the frames if the STA is sleeping */
2801 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
2802 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
2803 if (!mgmt_buf)
2804 return false;
2805
2806 INIT_LIST_HEAD(&mgmt_buf->list);
2807 mgmt_buf->id = id;
2808 mgmt_buf->freq = freq;
2809 mgmt_buf->wait = wait;
2810 mgmt_buf->len = len;
2811 mgmt_buf->no_cck = no_cck;
2812 memcpy(mgmt_buf->buf, buf, len);
2813 spin_lock_bh(&conn->psq_lock);
2814 is_psq_empty = skb_queue_empty(&conn->psq) &&
2815 (conn->mgmt_psq_len == 0);
2816 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
2817 conn->mgmt_psq_len++;
2818 spin_unlock_bh(&conn->psq_lock);
2819
2820 /*
2821 * If this is the first pkt getting queued
2822 * for this STA, update the PVB for this
2823 * STA.
2824 */
2825 if (is_psq_empty)
2826 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
2827 conn->aid, 1);
2828 return true;
2829 }
2830
2831 /*
2832 * This tx is because of a PsPoll.
2833 * Determine if MoreData bit has to be set.
2834 */
2835 spin_lock_bh(&conn->psq_lock);
2836 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
2837 *more_data = true;
2838 spin_unlock_bh(&conn->psq_lock);
2839 }
2840
2841 return false;
2842}
2843
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07002844/* Check if SSID length is greater than DIRECT- */
2845static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
2846{
2847 const struct ieee80211_mgmt *mgmt;
2848 mgmt = (const struct ieee80211_mgmt *) buf;
2849
2850 /* variable[1] contains the SSID tag length */
2851 if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
2852 (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
2853 return true;
2854 }
2855
2856 return false;
2857}
2858
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002859static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2860 struct ieee80211_channel *chan, bool offchan,
2861 enum nl80211_channel_type channel_type,
2862 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002863 const u8 *buf, size_t len, bool no_cck,
2864 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002865{
2866 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302867 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002868 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002869 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002870 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002871
2872 mgmt = (const struct ieee80211_mgmt *) buf;
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07002873 if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
2874 ieee80211_is_probe_resp(mgmt->frame_control) &&
2875 ath6kl_is_p2p_go_ssid(buf, len)) {
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002876 /*
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07002877 * Send Probe Response frame in GO mode using a separate WMI
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002878 * command to allow the target to fill in the generic IEs.
2879 */
2880 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302881 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002882 chan->center_freq);
2883 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002884
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302885 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002886 if (id == 0) {
2887 /*
2888 * 0 is a reserved value in the WMI command and shall not be
2889 * used for the command.
2890 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302891 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002892 }
2893
2894 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002895
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002896 /* AP mode Power saving processing */
2897 if (vif->nw_type == AP_NETWORK) {
2898 queued = ath6kl_mgmt_powersave_ap(vif,
2899 id, chan->center_freq,
2900 wait, buf,
2901 len, &more_data, no_cck);
2902 if (queued)
2903 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002904 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002905
2906 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2907 chan->center_freq, wait,
2908 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002909}
2910
Jouni Malinenae32c302011-08-30 21:58:01 +03002911static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2912 struct net_device *dev,
2913 u16 frame_type, bool reg)
2914{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302915 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002916
2917 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2918 __func__, frame_type, reg);
2919 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2920 /*
2921 * Note: This notification callback is not allowed to sleep, so
2922 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2923 * hardcode target to report Probe Request frames all the time.
2924 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302925 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002926 }
2927}
2928
Kalle Valo10509f92011-12-13 14:52:07 +02002929static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2930 struct net_device *dev,
2931 struct cfg80211_sched_scan_request *request)
2932{
2933 struct ath6kl *ar = ath6kl_priv(dev);
2934 struct ath6kl_vif *vif = netdev_priv(dev);
2935 u16 interval;
2936 int ret;
2937 u8 i;
2938
2939 if (ar->state != ATH6KL_STATE_ON)
2940 return -EIO;
2941
2942 if (vif->sme_state != SME_DISCONNECTED)
2943 return -EBUSY;
2944
Kalle Valob4d13d32012-03-21 10:01:09 +02002945 ath6kl_cfg80211_scan_complete_event(vif, true);
2946
Kalle Valo10509f92011-12-13 14:52:07 +02002947 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2948 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2949 i, DISABLE_SSID_FLAG,
2950 0, NULL);
2951 }
2952
2953 /* fw uses seconds, also make sure that it's >0 */
2954 interval = max_t(u16, 1, request->interval / 1000);
2955
2956 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2957 interval, interval,
2958 10, 0, 0, 0, 3, 0, 0, 0);
2959
2960 if (request->n_ssids && request->ssids[0].ssid_len) {
2961 for (i = 0; i < request->n_ssids; i++) {
2962 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2963 i, SPECIFIC_SSID_FLAG,
2964 request->ssids[i].ssid_len,
2965 request->ssids[i].ssid);
2966 }
2967 }
2968
2969 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2970 ATH6KL_WOW_MODE_ENABLE,
2971 WOW_FILTER_SSID,
2972 WOW_HOST_REQ_DELAY);
2973 if (ret) {
2974 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2975 return ret;
2976 }
2977
2978 /* this also clears IE in fw if it's not set */
2979 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2980 WMI_FRAME_PROBE_REQ,
2981 request->ie, request->ie_len);
2982 if (ret) {
2983 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
2984 ret);
2985 return ret;
2986 }
2987
2988 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2989 ATH6KL_HOST_MODE_ASLEEP);
2990 if (ret) {
2991 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
2992 ret);
2993 return ret;
2994 }
2995
2996 ar->state = ATH6KL_STATE_SCHED_SCAN;
2997
2998 return ret;
2999}
3000
3001static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
3002 struct net_device *dev)
3003{
3004 struct ath6kl_vif *vif = netdev_priv(dev);
3005 bool stopped;
3006
3007 stopped = __ath6kl_cfg80211_sscan_stop(vif);
3008
3009 if (!stopped)
3010 return -EIO;
3011
3012 return 0;
3013}
3014
Jouni Malinenf80574a2011-08-30 21:58:04 +03003015static const struct ieee80211_txrx_stypes
3016ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
3017 [NL80211_IFTYPE_STATION] = {
3018 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3019 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3020 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3021 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3022 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02003023 [NL80211_IFTYPE_AP] = {
3024 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3025 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3026 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3027 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3028 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03003029 [NL80211_IFTYPE_P2P_CLIENT] = {
3030 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3031 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3032 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3033 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3034 },
3035 [NL80211_IFTYPE_P2P_GO] = {
3036 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3037 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3038 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3039 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3040 },
3041};
3042
Kalle Valobdcd8172011-07-18 00:22:30 +03003043static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303044 .add_virtual_intf = ath6kl_cfg80211_add_iface,
3045 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03003046 .change_virtual_intf = ath6kl_cfg80211_change_iface,
3047 .scan = ath6kl_cfg80211_scan,
3048 .connect = ath6kl_cfg80211_connect,
3049 .disconnect = ath6kl_cfg80211_disconnect,
3050 .add_key = ath6kl_cfg80211_add_key,
3051 .get_key = ath6kl_cfg80211_get_key,
3052 .del_key = ath6kl_cfg80211_del_key,
3053 .set_default_key = ath6kl_cfg80211_set_default_key,
3054 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
3055 .set_tx_power = ath6kl_cfg80211_set_txpower,
3056 .get_tx_power = ath6kl_cfg80211_get_txpower,
3057 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
3058 .join_ibss = ath6kl_cfg80211_join_ibss,
3059 .leave_ibss = ath6kl_cfg80211_leave_ibss,
3060 .get_station = ath6kl_get_station,
3061 .set_pmksa = ath6kl_set_pmksa,
3062 .del_pmksa = ath6kl_del_pmksa,
3063 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03003064 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03003065#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02003066 .suspend = __ath6kl_cfg80211_suspend,
3067 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03003068#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03003069 .set_channel = ath6kl_set_channel,
Johannes Berg88600202012-02-13 15:17:18 +01003070 .start_ap = ath6kl_start_ap,
3071 .change_beacon = ath6kl_change_beacon,
3072 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02003073 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03003074 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003075 .remain_on_channel = ath6kl_remain_on_channel,
3076 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003077 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03003078 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02003079 .sched_scan_start = ath6kl_cfg80211_sscan_start,
3080 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03003081};
3082
Kalle Valo7125f012011-12-13 14:51:37 +02003083void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02003084{
Kalle Valo10509f92011-12-13 14:52:07 +02003085 ath6kl_cfg80211_sscan_disable(vif);
3086
Kalle Valoec4b7f62011-11-01 08:44:04 +02003087 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02003088 case SME_DISCONNECTED:
3089 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02003090 case SME_CONNECTING:
3091 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
3092 NULL, 0,
3093 WLAN_STATUS_UNSPECIFIED_FAILURE,
3094 GFP_KERNEL);
3095 break;
3096 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02003097 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
3098 break;
3099 }
3100
3101 if (test_bit(CONNECTED, &vif->flags) ||
3102 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02003103 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003104
3105 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02003106 clear_bit(CONNECTED, &vif->flags);
3107 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003108
3109 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02003110 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
3111 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
3112 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003113
3114 ath6kl_cfg80211_scan_complete_event(vif, true);
3115}
3116
Kalle Valo7125f012011-12-13 14:51:37 +02003117void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3118{
3119 struct ath6kl_vif *vif;
3120
3121 vif = ath6kl_vif_first(ar);
3122 if (!vif) {
3123 /* save the current power mode before enabling power save */
3124 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3125
3126 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
3127 ath6kl_warn("ath6kl_deep_sleep_enable: "
3128 "wmi_powermode_cmd failed\n");
3129 return;
3130 }
3131
3132 /*
3133 * FIXME: we should take ar->list_lock to protect changes in the
3134 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3135 * sleeps.
3136 */
3137 list_for_each_entry(vif, &ar->vif_list, list)
3138 ath6kl_cfg80211_stop(vif);
3139}
3140
Kalle Valoc25889e2012-01-17 20:08:27 +02003141static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003142{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303143 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303144 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303145 ath6kl_err("failed to initialize aggr\n");
3146 return -ENOMEM;
3147 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003148
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303149 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303150 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003151 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3152 (unsigned long) vif);
3153
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303154 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303155 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303156
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303157 INIT_LIST_HEAD(&vif->mc_filter);
3158
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303159 return 0;
3160}
3161
Kalle Valoc25889e2012-01-17 20:08:27 +02003162void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303163{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303164 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303165 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303166
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303167 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303168
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303169 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3170
3171 if (vif->nw_type == ADHOC_NETWORK)
3172 ar->ibss_if_active = false;
3173
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303174 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3175 list_del(&mc_filter->list);
3176 kfree(mc_filter);
3177 }
3178
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303179 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303180
3181 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303182}
3183
3184struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303185 enum nl80211_iftype type, u8 fw_vif_idx,
3186 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303187{
3188 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303189 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303190
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303191 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303192 if (!ndev)
3193 return NULL;
3194
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303195 vif = netdev_priv(ndev);
3196 ndev->ieee80211_ptr = &vif->wdev;
3197 vif->wdev.wiphy = ar->wiphy;
3198 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303199 vif->ndev = ndev;
3200 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3201 vif->wdev.netdev = ndev;
3202 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303203 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003204 vif->nw_type = nw_type;
3205 vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303206 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303207 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303208
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303209 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
3210 if (fw_vif_idx != 0)
3211 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3212 0x2;
3213
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303214 init_netdev(ndev);
3215
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303216 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303217
Kalle Valoc25889e2012-01-17 20:08:27 +02003218 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303219 goto err;
3220
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303221 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303222 goto err;
3223
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303224 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303225 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303226 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303227 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303228 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303229
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303230 if (type == NL80211_IFTYPE_ADHOC)
3231 ar->ibss_if_active = true;
3232
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303233 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303234 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303235 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303236
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303237 return ndev;
3238
3239err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303240 aggr_module_destroy(vif->aggr_cntxt);
3241 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303242 return NULL;
3243}
3244
Kalle Valo46d33a22012-01-17 20:08:40 +02003245int ath6kl_cfg80211_init(struct ath6kl *ar)
3246{
3247 struct wiphy *wiphy = ar->wiphy;
3248 int ret;
3249
3250 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3251
3252 wiphy->max_remain_on_channel_duration = 5000;
3253
3254 /* set device pointer for wiphy */
3255 set_wiphy_dev(wiphy, ar->dev);
3256
3257 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3258 BIT(NL80211_IFTYPE_ADHOC) |
3259 BIT(NL80211_IFTYPE_AP);
3260 if (ar->p2p) {
3261 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3262 BIT(NL80211_IFTYPE_P2P_CLIENT);
3263 }
3264
3265 /* max num of ssids that can be probed during scanning */
3266 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
3267 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
3268 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3269 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3270 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3271
3272 wiphy->cipher_suites = cipher_suites;
3273 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3274
3275 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3276 WIPHY_WOWLAN_DISCONNECT |
3277 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3278 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3279 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3280 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3281 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3282 wiphy->wowlan.pattern_min_len = 1;
3283 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
3284
3285 wiphy->max_sched_scan_ssids = 10;
3286
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303287 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3288 WIPHY_FLAG_HAVE_AP_SME |
3289 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3290 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3291
3292 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3293 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3294
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303295 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
3296 ar->fw_capabilities))
3297 ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER;
3298
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303299 ar->wiphy->probe_resp_offload =
3300 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3301 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
3302 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
3303 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
3304
Kalle Valo46d33a22012-01-17 20:08:40 +02003305 ret = wiphy_register(wiphy);
3306 if (ret < 0) {
3307 ath6kl_err("couldn't register wiphy device\n");
3308 return ret;
3309 }
3310
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303311 ar->wiphy_registered = true;
3312
Kalle Valo46d33a22012-01-17 20:08:40 +02003313 return 0;
3314}
3315
3316void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303317{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303318 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303319
3320 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003321}
Kalle Valo46d33a22012-01-17 20:08:40 +02003322
Kalle Valo45eaa782012-01-17 20:09:05 +02003323struct ath6kl *ath6kl_cfg80211_create(void)
3324{
3325 struct ath6kl *ar;
3326 struct wiphy *wiphy;
3327
3328 /* create a new wiphy for use with cfg80211 */
3329 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3330
3331 if (!wiphy) {
3332 ath6kl_err("couldn't allocate wiphy device\n");
3333 return NULL;
3334 }
3335
3336 ar = wiphy_priv(wiphy);
3337 ar->wiphy = wiphy;
3338
3339 return ar;
3340}
3341
3342/* Note: ar variable must not be accessed after calling this! */
3343void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3344{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303345 int i;
3346
3347 for (i = 0; i < AP_MAX_NUM_STA; i++)
3348 kfree(ar->sta_list[i].aggr_conn);
3349
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303350 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003351}
Kalle Valo45eaa782012-01-17 20:09:05 +02003352