blob: 51bbe85c574ca19ebfbf54d39a63011b848014ba [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
Vasanthakumar Thiagarajan1b2df402012-02-06 20:15:53 +05303 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
Kalle Valobdcd8172011-07-18 00:22:30 +03004 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
Joe Perches516304b2012-03-18 17:30:52 -070018#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040020#include <linux/moduleparam.h>
Raja Manic08631c2011-12-16 14:24:24 +053021#include <linux/inetdevice.h>
Kalle Valod6a434d2012-01-17 20:09:36 +020022#include <linux/export.h>
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040023
Kalle Valobdcd8172011-07-18 00:22:30 +030024#include "core.h"
25#include "cfg80211.h"
26#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030027#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030028#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030029
30#define RATETAB_ENT(_rate, _rateid, _flags) { \
31 .bitrate = (_rate), \
32 .flags = (_flags), \
33 .hw_value = (_rateid), \
34}
35
36#define CHAN2G(_channel, _freq, _flags) { \
37 .band = IEEE80211_BAND_2GHZ, \
38 .hw_value = (_channel), \
39 .center_freq = (_freq), \
40 .flags = (_flags), \
41 .max_antenna_gain = 0, \
42 .max_power = 30, \
43}
44
45#define CHAN5G(_channel, _flags) { \
46 .band = IEEE80211_BAND_5GHZ, \
47 .hw_value = (_channel), \
48 .center_freq = 5000 + (5 * (_channel)), \
49 .flags = (_flags), \
50 .max_antenna_gain = 0, \
51 .max_power = 30, \
52}
53
Bala Shanmugamf5993592012-03-27 12:17:32 +053054#define DEFAULT_BG_SCAN_PERIOD 60
55
Naveen Singhdd45b752012-05-16 13:29:00 +030056struct ath6kl_cfg80211_match_probe_ssid {
57 struct cfg80211_ssid ssid;
58 u8 flag;
59};
60
Kalle Valobdcd8172011-07-18 00:22:30 +030061static struct ieee80211_rate ath6kl_rates[] = {
62 RATETAB_ENT(10, 0x1, 0),
63 RATETAB_ENT(20, 0x2, 0),
64 RATETAB_ENT(55, 0x4, 0),
65 RATETAB_ENT(110, 0x8, 0),
66 RATETAB_ENT(60, 0x10, 0),
67 RATETAB_ENT(90, 0x20, 0),
68 RATETAB_ENT(120, 0x40, 0),
69 RATETAB_ENT(180, 0x80, 0),
70 RATETAB_ENT(240, 0x100, 0),
71 RATETAB_ENT(360, 0x200, 0),
72 RATETAB_ENT(480, 0x400, 0),
73 RATETAB_ENT(540, 0x800, 0),
74};
75
76#define ath6kl_a_rates (ath6kl_rates + 4)
77#define ath6kl_a_rates_size 8
78#define ath6kl_g_rates (ath6kl_rates + 0)
79#define ath6kl_g_rates_size 12
80
Vasanthakumar Thiagarajanbed56e32012-04-09 19:03:57 +053081#define ath6kl_g_htcap IEEE80211_HT_CAP_SGI_20
82#define ath6kl_a_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +053083 IEEE80211_HT_CAP_SGI_20 | \
84 IEEE80211_HT_CAP_SGI_40)
85
Kalle Valobdcd8172011-07-18 00:22:30 +030086static struct ieee80211_channel ath6kl_2ghz_channels[] = {
87 CHAN2G(1, 2412, 0),
88 CHAN2G(2, 2417, 0),
89 CHAN2G(3, 2422, 0),
90 CHAN2G(4, 2427, 0),
91 CHAN2G(5, 2432, 0),
92 CHAN2G(6, 2437, 0),
93 CHAN2G(7, 2442, 0),
94 CHAN2G(8, 2447, 0),
95 CHAN2G(9, 2452, 0),
96 CHAN2G(10, 2457, 0),
97 CHAN2G(11, 2462, 0),
98 CHAN2G(12, 2467, 0),
99 CHAN2G(13, 2472, 0),
100 CHAN2G(14, 2484, 0),
101};
102
103static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
104 CHAN5G(34, 0), CHAN5G(36, 0),
105 CHAN5G(38, 0), CHAN5G(40, 0),
106 CHAN5G(42, 0), CHAN5G(44, 0),
107 CHAN5G(46, 0), CHAN5G(48, 0),
108 CHAN5G(52, 0), CHAN5G(56, 0),
109 CHAN5G(60, 0), CHAN5G(64, 0),
110 CHAN5G(100, 0), CHAN5G(104, 0),
111 CHAN5G(108, 0), CHAN5G(112, 0),
112 CHAN5G(116, 0), CHAN5G(120, 0),
113 CHAN5G(124, 0), CHAN5G(128, 0),
114 CHAN5G(132, 0), CHAN5G(136, 0),
115 CHAN5G(140, 0), CHAN5G(149, 0),
116 CHAN5G(153, 0), CHAN5G(157, 0),
117 CHAN5G(161, 0), CHAN5G(165, 0),
118 CHAN5G(184, 0), CHAN5G(188, 0),
119 CHAN5G(192, 0), CHAN5G(196, 0),
120 CHAN5G(200, 0), CHAN5G(204, 0),
121 CHAN5G(208, 0), CHAN5G(212, 0),
122 CHAN5G(216, 0),
123};
124
125static struct ieee80211_supported_band ath6kl_band_2ghz = {
126 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
127 .channels = ath6kl_2ghz_channels,
128 .n_bitrates = ath6kl_g_rates_size,
129 .bitrates = ath6kl_g_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530130 .ht_cap.cap = ath6kl_g_htcap,
131 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300132};
133
134static struct ieee80211_supported_band ath6kl_band_5ghz = {
135 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
136 .channels = ath6kl_5ghz_a_channels,
137 .n_bitrates = ath6kl_a_rates_size,
138 .bitrates = ath6kl_a_rates,
Vasanthakumar Thiagarajanbed56e32012-04-09 19:03:57 +0530139 .ht_cap.cap = ath6kl_a_htcap,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530140 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300141};
142
Jouni Malinen837cb972011-10-11 17:31:57 +0300143#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
144
Kalle Valo10509f92011-12-13 14:52:07 +0200145/* returns true if scheduled scan was stopped */
146static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
147{
148 struct ath6kl *ar = vif->ar;
149
150 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
151 return false;
152
153 del_timer_sync(&vif->sched_scan_timer);
154
155 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
156 ATH6KL_HOST_MODE_AWAKE);
157
158 ar->state = ATH6KL_STATE_ON;
159
160 return true;
161}
162
163static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
164{
165 struct ath6kl *ar = vif->ar;
166 bool stopped;
167
168 stopped = __ath6kl_cfg80211_sscan_stop(vif);
169
170 if (!stopped)
171 return;
172
173 cfg80211_sched_scan_stopped(ar->wiphy);
174}
175
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530176static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300177 enum nl80211_wpa_versions wpa_version)
178{
179 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
180
181 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530182 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300183 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530184 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300185 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530186 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300187 } else {
188 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
189 return -ENOTSUPP;
190 }
191
192 return 0;
193}
194
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530195static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300196 enum nl80211_auth_type auth_type)
197{
Kalle Valobdcd8172011-07-18 00:22:30 +0300198 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
199
200 switch (auth_type) {
201 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530202 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300203 break;
204 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530205 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300206 break;
207 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530208 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300209 break;
210
211 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530212 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300213 break;
214
215 default:
Masanari Iida3c325fb2012-01-31 23:32:55 +0900216 ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300217 return -ENOTSUPP;
218 }
219
220 return 0;
221}
222
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530223static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300224{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530225 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
226 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
227 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300228
229 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
230 __func__, cipher, ucast);
231
232 switch (cipher) {
233 case 0:
234 /* our own hack to use value 0 as no crypto used */
235 *ar_cipher = NONE_CRYPT;
236 *ar_cipher_len = 0;
237 break;
238 case WLAN_CIPHER_SUITE_WEP40:
239 *ar_cipher = WEP_CRYPT;
240 *ar_cipher_len = 5;
241 break;
242 case WLAN_CIPHER_SUITE_WEP104:
243 *ar_cipher = WEP_CRYPT;
244 *ar_cipher_len = 13;
245 break;
246 case WLAN_CIPHER_SUITE_TKIP:
247 *ar_cipher = TKIP_CRYPT;
248 *ar_cipher_len = 0;
249 break;
250 case WLAN_CIPHER_SUITE_CCMP:
251 *ar_cipher = AES_CRYPT;
252 *ar_cipher_len = 0;
253 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200254 case WLAN_CIPHER_SUITE_SMS4:
255 *ar_cipher = WAPI_CRYPT;
256 *ar_cipher_len = 0;
257 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300258 default:
259 ath6kl_err("cipher 0x%x not supported\n", cipher);
260 return -ENOTSUPP;
261 }
262
263 return 0;
264}
265
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530266static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300267{
268 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
269
270 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530271 if (vif->auth_mode == WPA_AUTH)
272 vif->auth_mode = WPA_PSK_AUTH;
273 else if (vif->auth_mode == WPA2_AUTH)
274 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300275 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530276 if (vif->auth_mode == WPA_AUTH)
277 vif->auth_mode = WPA_AUTH_CCKM;
278 else if (vif->auth_mode == WPA2_AUTH)
279 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300280 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530281 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300282 }
283}
284
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530285static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300286{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530287 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530288
Kalle Valobdcd8172011-07-18 00:22:30 +0300289 if (!test_bit(WMI_READY, &ar->flag)) {
290 ath6kl_err("wmi is not ready\n");
291 return false;
292 }
293
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530294 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300295 ath6kl_err("wlan disabled\n");
296 return false;
297 }
298
299 return true;
300}
301
Kevin Fang6981ffd2011-10-07 08:51:19 +0800302static bool ath6kl_is_wpa_ie(const u8 *pos)
303{
Arend van Spriel04b23122012-10-12 12:28:14 +0200304 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
Kevin Fang6981ffd2011-10-07 08:51:19 +0800305 pos[2] == 0x00 && pos[3] == 0x50 &&
306 pos[4] == 0xf2 && pos[5] == 0x01;
307}
308
309static bool ath6kl_is_rsn_ie(const u8 *pos)
310{
311 return pos[0] == WLAN_EID_RSN;
312}
313
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700314static bool ath6kl_is_wps_ie(const u8 *pos)
315{
316 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
317 pos[1] >= 4 &&
318 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
319 pos[5] == 0x04);
320}
321
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530322static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
323 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800324{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530325 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800326 const u8 *pos;
327 u8 *buf = NULL;
328 size_t len = 0;
329 int ret;
330
331 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700332 * Clear previously set flag
333 */
334
335 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
336
337 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800338 * Filter out RSN/WPA IE(s)
339 */
340
341 if (ies && ies_len) {
342 buf = kmalloc(ies_len, GFP_KERNEL);
343 if (buf == NULL)
344 return -ENOMEM;
345 pos = ies;
346
347 while (pos + 1 < ies + ies_len) {
348 if (pos + 2 + pos[1] > ies + ies_len)
349 break;
350 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
351 memcpy(buf + len, pos, 2 + pos[1]);
352 len += 2 + pos[1];
353 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700354
355 if (ath6kl_is_wps_ie(pos))
356 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
357
Kevin Fang6981ffd2011-10-07 08:51:19 +0800358 pos += 2 + pos[1];
359 }
360 }
361
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530362 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
363 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800364 kfree(buf);
365 return ret;
366}
367
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530368static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
369{
370 switch (type) {
371 case NL80211_IFTYPE_STATION:
372 *nw_type = INFRA_NETWORK;
373 break;
374 case NL80211_IFTYPE_ADHOC:
375 *nw_type = ADHOC_NETWORK;
376 break;
377 case NL80211_IFTYPE_AP:
378 *nw_type = AP_NETWORK;
379 break;
380 case NL80211_IFTYPE_P2P_CLIENT:
381 *nw_type = INFRA_NETWORK;
382 break;
383 case NL80211_IFTYPE_P2P_GO:
384 *nw_type = AP_NETWORK;
385 break;
386 default:
387 ath6kl_err("invalid interface type %u\n", type);
388 return -ENOTSUPP;
389 }
390
391 return 0;
392}
393
394static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
395 u8 *if_idx, u8 *nw_type)
396{
397 int i;
398
399 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
400 return false;
401
402 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
Kalle Valo96f1fad2012-03-07 20:03:57 +0200403 ar->num_vif))
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530404 return false;
405
406 if (type == NL80211_IFTYPE_STATION ||
407 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200408 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530409 if ((ar->avail_idx_map >> i) & BIT(0)) {
410 *if_idx = i;
411 return true;
412 }
413 }
414 }
415
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530416 if (type == NL80211_IFTYPE_P2P_CLIENT ||
417 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200418 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530419 if ((ar->avail_idx_map >> i) & BIT(0)) {
420 *if_idx = i;
421 return true;
422 }
423 }
424 }
425
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530426 return false;
427}
428
Kalle Valo8c9bb052012-03-07 20:04:00 +0200429static bool ath6kl_is_tx_pending(struct ath6kl *ar)
430{
431 return ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0;
432}
433
434
Kalle Valobdcd8172011-07-18 00:22:30 +0300435static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
436 struct cfg80211_connect_params *sme)
437{
438 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530439 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300440 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800441 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Raja Manice0dc0c2012-02-20 19:08:08 +0530442 u16 interval;
Kalle Valobdcd8172011-07-18 00:22:30 +0300443
Kalle Valo10509f92011-12-13 14:52:07 +0200444 ath6kl_cfg80211_sscan_disable(vif);
445
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530446 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300447
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530448 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300449 return -EIO;
450
451 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
452 ath6kl_err("destroy in progress\n");
453 return -EBUSY;
454 }
455
456 if (test_bit(SKIP_SCAN, &ar->flag) &&
457 ((sme->channel && sme->channel->center_freq == 0) ||
458 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
459 ath6kl_err("SkipScan: channel or bssid invalid\n");
460 return -EINVAL;
461 }
462
463 if (down_interruptible(&ar->sem)) {
464 ath6kl_err("busy, couldn't get access\n");
465 return -ERESTARTSYS;
466 }
467
468 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
469 ath6kl_err("busy, destroy in progress\n");
470 up(&ar->sem);
471 return -EBUSY;
472 }
473
474 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
475 /*
476 * sleep until the command queue drains
477 */
478 wait_event_interruptible_timeout(ar->event_wq,
Kalle Valo8c9bb052012-03-07 20:04:00 +0200479 ath6kl_is_tx_pending(ar),
480 WMI_TIMEOUT);
Kalle Valobdcd8172011-07-18 00:22:30 +0300481 if (signal_pending(current)) {
482 ath6kl_err("cmd queue drain timeout\n");
483 up(&ar->sem);
484 return -EINTR;
485 }
486 }
487
Jouni Malinen6e786cb2011-12-15 14:16:00 +0200488 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
489 if (status) {
490 up(&ar->sem);
491 return status;
492 }
493
494 if (sme->ie == NULL || sme->ie_len == 0)
Raja Mani542c5192011-11-15 14:14:56 +0530495 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800496
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530497 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530498 vif->ssid_len == sme->ssid_len &&
499 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530500 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530501 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
502 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530503 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300504
505 up(&ar->sem);
506 if (status) {
507 ath6kl_err("wmi_reconnect_cmd failed\n");
508 return -EIO;
509 }
510 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530511 } else if (vif->ssid_len == sme->ssid_len &&
512 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530513 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300514 }
515
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530516 memset(vif->ssid, 0, sizeof(vif->ssid));
517 vif->ssid_len = sme->ssid_len;
518 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300519
520 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530521 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300522
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530523 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300524 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530525 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300526
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530527 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300528
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530529 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300530 if (status) {
531 up(&ar->sem);
532 return status;
533 }
534
535 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530536 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300537 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530538 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300539
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530540 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300541
542 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530543 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300544
545 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530546 (vif->auth_mode == NONE_AUTH) &&
547 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300548 struct ath6kl_key *key = NULL;
549
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530550 if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300551 ath6kl_err("key index %d out of bounds\n",
552 sme->key_idx);
553 up(&ar->sem);
554 return -ENOENT;
555 }
556
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530557 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300558 key->key_len = sme->key_len;
559 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530560 key->cipher = vif->prwise_crypto;
561 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300562
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530563 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530564 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300565 GROUP_USAGE | TX_USAGE,
566 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200567 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300568 key->key, KEY_OP_INIT_VAL, NULL,
569 NO_SYNC_WMIFLAG);
570 }
571
572 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530573 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530574 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200575 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300576 ath6kl_err("couldn't set bss filtering\n");
577 up(&ar->sem);
578 return -EIO;
579 }
580 }
581
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530582 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300583
Thomas Pedersenc422d52d2012-05-15 00:09:23 -0700584 /* enable enhanced bmiss detection if applicable */
585 ath6kl_cfg80211_sta_bmiss_enhance(vif, true);
586
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800587 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
588 nw_subtype = SUBTYPE_P2PCLIENT;
589
Kalle Valobdcd8172011-07-18 00:22:30 +0300590 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
591 "%s: connect called with authmode %d dot11 auth %d"
592 " PW crypto %d PW crypto len %d GRP crypto %d"
593 " GRP crypto len %d channel hint %u\n",
594 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530595 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
596 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530597 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300598
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530599 vif->reconnect_flag = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +0530600
601 if (vif->nw_type == INFRA_NETWORK) {
Kalle Valob5283872012-03-12 13:23:23 +0200602 interval = max_t(u16, vif->listen_intvl_t,
603 ATH6KL_MAX_WOW_LISTEN_INTL);
Raja Manice0dc0c2012-02-20 19:08:08 +0530604 status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
605 interval,
606 0);
607 if (status) {
608 ath6kl_err("couldn't set listen intervel\n");
609 up(&ar->sem);
610 return status;
611 }
612 }
613
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530614 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530615 vif->dot11_auth_mode, vif->auth_mode,
616 vif->prwise_crypto,
617 vif->prwise_crypto_len,
618 vif->grp_crypto, vif->grp_crypto_len,
619 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530620 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800621 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300622
Bala Shanmugamf5993592012-03-27 12:17:32 +0530623 /* disable background scan if period is 0 */
624 if (sme->bg_scan_period == 0)
625 sme->bg_scan_period = 0xffff;
626
627 /* configure default value if not specified */
628 if (sme->bg_scan_period == -1)
629 sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
630
631 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0,
632 sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
633
Kalle Valobdcd8172011-07-18 00:22:30 +0300634 up(&ar->sem);
635
636 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530637 memset(vif->ssid, 0, sizeof(vif->ssid));
638 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300639 ath6kl_err("invalid request\n");
640 return -ENOENT;
641 } else if (status) {
642 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
643 return -EIO;
644 }
645
646 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Kalle Valoddc3d772012-03-07 20:03:58 +0200647 ((vif->auth_mode == WPA_PSK_AUTH) ||
648 (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530649 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300650 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
651 }
652
653 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530654 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300655
656 return 0;
657}
658
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530659static struct cfg80211_bss *
660ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
661 enum network_type nw_type,
662 const u8 *bssid,
663 struct ieee80211_channel *chan,
664 const u8 *beacon_ie,
665 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300666{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530667 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300668 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530669 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300670 u8 *ie;
671
Raja Mani4eab6f42011-11-09 17:02:23 +0530672 if (nw_type & ADHOC_NETWORK) {
673 cap_mask = WLAN_CAPABILITY_IBSS;
674 cap_val = WLAN_CAPABILITY_IBSS;
675 } else {
676 cap_mask = WLAN_CAPABILITY_ESS;
677 cap_val = WLAN_CAPABILITY_ESS;
678 }
679
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530680 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530681 vif->ssid, vif->ssid_len,
682 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300683 if (bss == NULL) {
684 /*
685 * Since cfg80211 may not yet know about the BSS,
686 * generate a partial entry until the first BSS info
687 * event becomes available.
688 *
689 * Prepend SSID element since it is not included in the Beacon
690 * IEs from the target.
691 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530692 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300693 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530694 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300695 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530696 ie[1] = vif->ssid_len;
697 memcpy(ie + 2, vif->ssid, vif->ssid_len);
698 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530699 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530700 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530701 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300702 0, GFP_KERNEL);
703 if (bss)
Kalle Valocdeb8602012-04-12 11:02:18 +0300704 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
705 "added bss %pM to cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300706 kfree(ie);
707 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530708 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300709
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530710 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300711}
712
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530713void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300714 u8 *bssid, u16 listen_intvl,
715 u16 beacon_intvl,
716 enum network_type nw_type,
717 u8 beacon_ie_len, u8 assoc_req_len,
718 u8 assoc_resp_len, u8 *assoc_info)
719{
Jouni Malinen01cac472011-09-19 19:14:59 +0300720 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530721 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530722 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300723
724 /* capinfo + listen interval */
725 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
726
727 /* capinfo + status code + associd */
728 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
729
730 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
731 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
732 assoc_resp_ie_offset;
733
734 assoc_req_len -= assoc_req_ie_offset;
735 assoc_resp_len -= assoc_resp_ie_offset;
736
Jouni Malinen32c10872011-09-19 19:15:07 +0300737 /*
738 * Store Beacon interval here; DTIM period will be available only once
739 * a Beacon frame from the AP is seen.
740 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530741 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530742 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300743
Kalle Valobdcd8172011-07-18 00:22:30 +0300744 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530745 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300746 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
747 "%s: ath6k not in ibss mode\n", __func__);
748 return;
749 }
750 }
751
752 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530753 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
754 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300755 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
756 "%s: ath6k not in station mode\n", __func__);
757 return;
758 }
759 }
760
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530761 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300762
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530763 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
764 assoc_info, beacon_ie_len);
765 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530766 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300767 return;
768 }
769
Raja Mani4eab6f42011-11-09 17:02:23 +0530770 if (nw_type & ADHOC_NETWORK) {
771 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
772 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
773 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530774 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300775 return;
776 }
777
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530778 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300779 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530780 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530781 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300782 assoc_req_ie, assoc_req_len,
783 assoc_resp_ie, assoc_resp_len,
784 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530785 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530786 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300787 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530788 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
789 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300790 }
791}
792
793static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
794 struct net_device *dev, u16 reason_code)
795{
Kalle Valod6d5c062011-11-25 13:17:37 +0200796 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530797 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300798
799 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
800 reason_code);
801
Kalle Valo10509f92011-12-13 14:52:07 +0200802 ath6kl_cfg80211_sscan_disable(vif);
803
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530804 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300805 return -EIO;
806
807 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
808 ath6kl_err("busy, destroy in progress\n");
809 return -EBUSY;
810 }
811
812 if (down_interruptible(&ar->sem)) {
813 ath6kl_err("busy, couldn't get access\n");
814 return -ERESTARTSYS;
815 }
816
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530817 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530818 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530819 memset(vif->ssid, 0, sizeof(vif->ssid));
820 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300821
822 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530823 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300824
825 up(&ar->sem);
826
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530827 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530828
Kalle Valobdcd8172011-07-18 00:22:30 +0300829 return 0;
830}
831
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530832void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300833 u8 *bssid, u8 assoc_resp_len,
834 u8 *assoc_info, u16 proto_reason)
835{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530836 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530837
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530838 if (vif->scan_req) {
839 cfg80211_scan_done(vif->scan_req, true);
840 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300841 }
842
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530843 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530844 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300845 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
846 "%s: ath6k not in ibss mode\n", __func__);
847 return;
848 }
849 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530850 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300851 return;
852 }
853
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530854 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530855 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
856 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300857 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
858 "%s: ath6k not in station mode\n", __func__);
859 return;
860 }
861 }
862
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530863 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300864
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530865 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530866 cfg80211_connect_result(vif->ndev,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200867 bssid, NULL, 0,
868 NULL, 0,
869 WLAN_STATUS_UNSPECIFIED_FAILURE,
870 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530871 } else if (vif->sme_state == SME_CONNECTED) {
Thomas Pedersen33a66642012-05-16 13:41:13 -0700872 cfg80211_disconnected(vif->ndev, proto_reason,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200873 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300874 }
875
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530876 vif->sme_state = SME_DISCONNECTED;
Thomas Pedersen33a66642012-05-16 13:41:13 -0700877
878 /*
879 * Send a disconnect command to target when a disconnect event is
880 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
881 * request from host) to make the firmware stop trying to connect even
882 * after giving disconnect event. There will be one more disconnect
883 * event for this disconnect command with reason code DISCONNECT_CMD
884 * which won't be notified to cfg80211.
885 */
886 if (reason != DISCONNECT_CMD)
887 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300888}
889
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300890static int ath6kl_set_probed_ssids(struct ath6kl *ar,
891 struct ath6kl_vif *vif,
Naveen Singhdd45b752012-05-16 13:29:00 +0300892 struct cfg80211_ssid *ssids, int n_ssids,
893 struct cfg80211_match_set *match_set,
894 int n_match_ssid)
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300895{
Naveen Singhdd45b752012-05-16 13:29:00 +0300896 u8 i, j, index_to_add, ssid_found = false;
897 struct ath6kl_cfg80211_match_probe_ssid ssid_list[MAX_PROBED_SSIDS];
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300898
Naveen Singhdd45b752012-05-16 13:29:00 +0300899 memset(ssid_list, 0, sizeof(ssid_list));
900
901 if (n_ssids > MAX_PROBED_SSIDS ||
902 n_match_ssid > MAX_PROBED_SSIDS)
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300903 return -EINVAL;
904
905 for (i = 0; i < n_ssids; i++) {
Naveen Singhdd45b752012-05-16 13:29:00 +0300906 memcpy(ssid_list[i].ssid.ssid,
907 ssids[i].ssid,
908 ssids[i].ssid_len);
909 ssid_list[i].ssid.ssid_len = ssids[i].ssid_len;
910
911 if (ssids[i].ssid_len)
912 ssid_list[i].flag = SPECIFIC_SSID_FLAG;
913 else
914 ssid_list[i].flag = ANY_SSID_FLAG;
915
916 if (n_match_ssid == 0)
917 ssid_list[i].flag |= MATCH_SSID_FLAG;
918 }
919
920 index_to_add = i;
921
922 for (i = 0; i < n_match_ssid; i++) {
923 ssid_found = false;
924
925 for (j = 0; j < n_ssids; j++) {
926 if ((match_set[i].ssid.ssid_len ==
927 ssid_list[j].ssid.ssid_len) &&
928 (!memcmp(ssid_list[j].ssid.ssid,
929 match_set[i].ssid.ssid,
930 match_set[i].ssid.ssid_len))) {
931 ssid_list[j].flag |= MATCH_SSID_FLAG;
932 ssid_found = true;
933 break;
934 }
935 }
936
937 if (ssid_found)
938 continue;
939
940 if (index_to_add >= MAX_PROBED_SSIDS)
941 continue;
942
943 ssid_list[index_to_add].ssid.ssid_len =
944 match_set[i].ssid.ssid_len;
945 memcpy(ssid_list[index_to_add].ssid.ssid,
946 match_set[i].ssid.ssid,
947 match_set[i].ssid.ssid_len);
948 ssid_list[index_to_add].flag |= MATCH_SSID_FLAG;
949 index_to_add++;
950 }
951
952 for (i = 0; i < index_to_add; i++) {
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300953 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
Naveen Singhdd45b752012-05-16 13:29:00 +0300954 ssid_list[i].flag,
955 ssid_list[i].ssid.ssid_len,
956 ssid_list[i].ssid.ssid);
957
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300958 }
959
960 /* Make sure no old entries are left behind */
Naveen Singhdd45b752012-05-16 13:29:00 +0300961 for (i = index_to_add; i < MAX_PROBED_SSIDS; i++) {
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300962 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
963 DISABLE_SSID_FLAG, 0, NULL);
964 }
965
966 return 0;
967}
968
Johannes Bergfd014282012-06-18 19:17:03 +0200969static int ath6kl_cfg80211_scan(struct wiphy *wiphy,
Kalle Valobdcd8172011-07-18 00:22:30 +0300970 struct cfg80211_scan_request *request)
971{
Johannes Bergfd014282012-06-18 19:17:03 +0200972 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(request->wdev);
973 struct ath6kl *ar = ath6kl_priv(vif->ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300974 s8 n_channels = 0;
975 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300976 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530977 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300978
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530979 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300980 return -EIO;
981
Kalle Valo10509f92011-12-13 14:52:07 +0200982 ath6kl_cfg80211_sscan_disable(vif);
983
Kalle Valobdcd8172011-07-18 00:22:30 +0300984 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530985 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan954e6ce2012-04-25 12:39:06 +0530986 ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
987 ALL_BSS_FILTER, 0);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300988 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300989 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300990 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300991 }
992 }
993
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300994 ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
Naveen Singhdd45b752012-05-16 13:29:00 +0300995 request->n_ssids, NULL, 0);
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300996 if (ret < 0)
997 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300998
Aarthi Thiruvengadam080eec42012-02-28 09:17:04 -0800999 /* this also clears IE in fw if it's not set */
1000 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
1001 WMI_FRAME_PROBE_REQ,
1002 request->ie, request->ie_len);
1003 if (ret) {
Joe Perchesf1ff32e2012-05-30 01:58:39 -07001004 ath6kl_err("failed to set Probe Request appie for scan\n");
Aarthi Thiruvengadam080eec42012-02-28 09:17:04 -08001005 return ret;
Jouni Malinenb84da8c2011-08-30 21:57:59 +03001006 }
1007
Jouni Malinen11869be2011-09-02 20:07:06 +03001008 /*
1009 * Scan only the requested channels if the request specifies a set of
1010 * channels. If the list is longer than the target supports, do not
1011 * configure the list and instead, scan all available channels.
1012 */
1013 if (request->n_channels > 0 &&
1014 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +03001015 u8 i;
1016
Jouni Malinen11869be2011-09-02 20:07:06 +03001017 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +03001018
1019 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
1020 if (channels == NULL) {
Kalle Valocdeb8602012-04-12 11:02:18 +03001021 ath6kl_warn("failed to set scan channels, scan all channels");
Edward Lu1276c9e2011-08-30 21:58:00 +03001022 n_channels = 0;
1023 }
1024
1025 for (i = 0; i < n_channels; i++)
1026 channels[i] = request->channels[i]->center_freq;
1027 }
1028
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301029 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +05301030 force_fg_scan = 1;
1031
Raja Mani5b35dff2012-03-28 18:50:35 +05301032 vif->scan_req = request;
1033
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001034 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001035 ar->fw_capabilities)) {
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001036 /*
1037 * If capable of doing P2P mgmt operations using
1038 * station interface, send additional information like
1039 * supported rates to advertise and xmit rates for
1040 * probe requests
1041 */
1042 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
1043 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +05301044 false, 0,
1045 ATH6KL_FG_SCAN_INTERVAL,
1046 n_channels, channels,
1047 request->no_cck,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001048 request->rates);
1049 } else {
1050 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
1051 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +05301052 false, 0,
1053 ATH6KL_FG_SCAN_INTERVAL,
1054 n_channels, channels);
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001055 }
Raja Mani5b35dff2012-03-28 18:50:35 +05301056 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001057 ath6kl_err("wmi_startscan_cmd failed\n");
Raja Mani5b35dff2012-03-28 18:50:35 +05301058 vif->scan_req = NULL;
1059 }
Kalle Valobdcd8172011-07-18 00:22:30 +03001060
Edward Lu1276c9e2011-08-30 21:58:00 +03001061 kfree(channels);
1062
Kalle Valobdcd8172011-07-18 00:22:30 +03001063 return ret;
1064}
1065
Kalle Valo1c17d312011-11-01 08:43:56 +02001066void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +03001067{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301068 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001069 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +03001070
Kalle Valo1c17d312011-11-01 08:43:56 +02001071 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
1072 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +03001073
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301074 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001075 return;
Kalle Valobdcd8172011-07-18 00:22:30 +03001076
Kalle Valo1c17d312011-11-01 08:43:56 +02001077 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001078 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001079
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301080 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
1081 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301082 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
1083 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001084 0, NULL);
1085 }
1086 }
1087
1088out:
Kalle Valocb938212011-10-27 18:47:46 +03001089 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301090 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001091}
1092
Thomas Pedersenc4f78632012-04-06 13:35:48 -07001093void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
1094 enum wmi_phy_mode mode)
1095{
Johannes Berg683b6d32012-11-08 21:25:48 +01001096 struct cfg80211_chan_def chandef;
Thomas Pedersenc4f78632012-04-06 13:35:48 -07001097
1098 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1099 "channel switch notify nw_type %d freq %d mode %d\n",
1100 vif->nw_type, freq, mode);
1101
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001102 cfg80211_chandef_create(&chandef,
1103 ieee80211_get_channel(vif->ar->wiphy, freq),
1104 (mode == WMI_11G_HT20) ?
1105 NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT);
Johannes Berg683b6d32012-11-08 21:25:48 +01001106
1107 cfg80211_ch_switch_notify(vif->ndev, &chandef);
Thomas Pedersenc4f78632012-04-06 13:35:48 -07001108}
1109
Kalle Valobdcd8172011-07-18 00:22:30 +03001110static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1111 u8 key_index, bool pairwise,
1112 const u8 *mac_addr,
1113 struct key_params *params)
1114{
Kalle Valod6d5c062011-11-25 13:17:37 +02001115 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301116 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001117 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301118 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001119 u8 key_usage;
1120 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001121
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301122 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001123 return -EIO;
1124
Jouni Malinen837cb972011-10-11 17:31:57 +03001125 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
1126 if (params->key_len != WMI_KRK_LEN)
1127 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301128 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
1129 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +03001130 }
1131
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301132 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001133 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1134 "%s: key index %d out of bounds\n", __func__,
1135 key_index);
1136 return -ENOENT;
1137 }
1138
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301139 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001140 memset(key, 0, sizeof(struct ath6kl_key));
1141
1142 if (pairwise)
1143 key_usage = PAIRWISE_USAGE;
1144 else
1145 key_usage = GROUP_USAGE;
1146
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301147 seq_len = params->seq_len;
1148 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1149 seq_len > ATH6KL_KEY_SEQ_LEN) {
1150 /* Only first half of the WPI PN is configured */
1151 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001152 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301153 if (params->key_len > WLAN_MAX_KEY_LEN ||
1154 seq_len > sizeof(key->seq))
1155 return -EINVAL;
1156
1157 key->key_len = params->key_len;
1158 memcpy(key->key, params->key, key->key_len);
1159 key->seq_len = seq_len;
1160 memcpy(key->seq, params->seq, key->seq_len);
1161 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001162
1163 switch (key->cipher) {
1164 case WLAN_CIPHER_SUITE_WEP40:
1165 case WLAN_CIPHER_SUITE_WEP104:
1166 key_type = WEP_CRYPT;
1167 break;
1168
1169 case WLAN_CIPHER_SUITE_TKIP:
1170 key_type = TKIP_CRYPT;
1171 break;
1172
1173 case WLAN_CIPHER_SUITE_CCMP:
1174 key_type = AES_CRYPT;
1175 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001176 case WLAN_CIPHER_SUITE_SMS4:
1177 key_type = WAPI_CRYPT;
1178 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001179
1180 default:
1181 return -ENOTSUPP;
1182 }
1183
Kalle Valoddc3d772012-03-07 20:03:58 +02001184 if (((vif->auth_mode == WPA_PSK_AUTH) ||
1185 (vif->auth_mode == WPA2_PSK_AUTH)) &&
1186 (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301187 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001188
1189 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1190 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1191 __func__, key_index, key->key_len, key_type,
1192 key_usage, key->seq_len);
1193
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301194 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001195 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
Vasanthakumar Thiagarajancc4d6232012-02-14 20:33:00 +05301196 key_type == WAPI_CRYPT)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001197 ar->ap_mode_bkey.valid = true;
1198 ar->ap_mode_bkey.key_index = key_index;
1199 ar->ap_mode_bkey.key_type = key_type;
1200 ar->ap_mode_bkey.key_len = key->key_len;
1201 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301202 if (!test_bit(CONNECTED, &vif->flags)) {
Kalle Valocdeb8602012-04-12 11:02:18 +03001203 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1204 "Delay initial group key configuration until AP mode has been started\n");
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001205 /*
1206 * The key will be set in ath6kl_connect_ap_mode() once
1207 * the connected event is received from the target.
1208 */
1209 return 0;
1210 }
1211 }
1212
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301213 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301214 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001215 /*
1216 * Store the key locally so that it can be re-configured after
1217 * the AP mode has properly started
1218 * (ath6kl_install_statioc_wep_keys).
1219 */
Kalle Valocdeb8602012-04-12 11:02:18 +03001220 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1221 "Delay WEP key configuration until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301222 vif->wep_key_list[key_index].key_len = key->key_len;
1223 memcpy(vif->wep_key_list[key_index].key, key->key,
1224 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001225 return 0;
1226 }
1227
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301228 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001229 key_type, key_usage, key->key_len,
1230 key->seq, key->seq_len, key->key,
1231 KEY_OP_INIT_VAL,
1232 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001233}
1234
1235static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1236 u8 key_index, bool pairwise,
1237 const u8 *mac_addr)
1238{
Kalle Valod6d5c062011-11-25 13:17:37 +02001239 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301240 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001241
1242 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1243
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301244 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001245 return -EIO;
1246
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301247 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001248 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1249 "%s: key index %d out of bounds\n", __func__,
1250 key_index);
1251 return -ENOENT;
1252 }
1253
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301254 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001255 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1256 "%s: index %d is empty\n", __func__, key_index);
1257 return 0;
1258 }
1259
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301260 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001261
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301262 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001263}
1264
1265static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1266 u8 key_index, bool pairwise,
1267 const u8 *mac_addr, void *cookie,
1268 void (*callback) (void *cookie,
1269 struct key_params *))
1270{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301271 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001272 struct ath6kl_key *key = NULL;
1273 struct key_params params;
1274
1275 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1276
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301277 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001278 return -EIO;
1279
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301280 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001281 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1282 "%s: key index %d out of bounds\n", __func__,
1283 key_index);
1284 return -ENOENT;
1285 }
1286
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301287 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001288 memset(&params, 0, sizeof(params));
1289 params.cipher = key->cipher;
1290 params.key_len = key->key_len;
1291 params.seq_len = key->seq_len;
1292 params.seq = key->seq;
1293 params.key = key->key;
1294
1295 callback(cookie, &params);
1296
1297 return key->key_len ? 0 : -ENOENT;
1298}
1299
1300static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1301 struct net_device *ndev,
1302 u8 key_index, bool unicast,
1303 bool multicast)
1304{
Kalle Valod6d5c062011-11-25 13:17:37 +02001305 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301306 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001307 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001308 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001309 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001310
1311 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1312
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301313 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001314 return -EIO;
1315
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301316 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001317 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1318 "%s: key index %d out of bounds\n",
1319 __func__, key_index);
1320 return -ENOENT;
1321 }
1322
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301323 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001324 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1325 __func__, key_index);
1326 return -EINVAL;
1327 }
1328
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301329 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301330 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001331 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301332 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001333 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001334 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301335 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001336 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301337 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001338
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301339 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001340 return 0; /* Delay until AP mode has been started */
1341
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001342 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1343 vif->def_txkey_index,
1344 key_type, key_usage,
1345 key->key_len, key->seq, key->seq_len,
1346 key->key,
1347 KEY_OP_INIT_VAL, NULL,
1348 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001349}
1350
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301351void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001352 bool ismcast)
1353{
1354 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1355 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1356
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301357 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001358 (ismcast ? NL80211_KEYTYPE_GROUP :
1359 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1360 GFP_KERNEL);
1361}
1362
1363static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1364{
1365 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301366 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001367 int ret;
1368
1369 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1370 changed);
1371
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301372 vif = ath6kl_vif_first(ar);
1373 if (!vif)
1374 return -EIO;
1375
1376 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001377 return -EIO;
1378
1379 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1380 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1381 if (ret != 0) {
1382 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1383 return -EIO;
1384 }
1385 }
1386
1387 return 0;
1388}
1389
Kalle Valobdcd8172011-07-18 00:22:30 +03001390static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
Johannes Bergc8442112012-10-24 10:17:18 +02001391 struct wireless_dev *wdev,
Kalle Valobdcd8172011-07-18 00:22:30 +03001392 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001393 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001394{
1395 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301396 struct ath6kl_vif *vif;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001397 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001398
1399 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1400 type, dbm);
1401
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301402 vif = ath6kl_vif_first(ar);
1403 if (!vif)
1404 return -EIO;
1405
1406 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001407 return -EIO;
1408
1409 switch (type) {
1410 case NL80211_TX_POWER_AUTOMATIC:
1411 return 0;
1412 case NL80211_TX_POWER_LIMITED:
Kalle Valod0d670a2012-03-07 20:03:58 +02001413 ar->tx_pwr = dbm;
Kalle Valobdcd8172011-07-18 00:22:30 +03001414 break;
1415 default:
1416 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1417 __func__, type);
1418 return -EOPNOTSUPP;
1419 }
1420
Kalle Valod0d670a2012-03-07 20:03:58 +02001421 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001422
1423 return 0;
1424}
1425
Johannes Bergc8442112012-10-24 10:17:18 +02001426static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy,
1427 struct wireless_dev *wdev,
1428 int *dbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001429{
1430 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301431 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001432
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301433 vif = ath6kl_vif_first(ar);
1434 if (!vif)
1435 return -EIO;
1436
1437 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001438 return -EIO;
1439
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301440 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001441 ar->tx_pwr = 0;
1442
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301443 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001444 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1445 return -EIO;
1446 }
1447
1448 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1449 5 * HZ);
1450
1451 if (signal_pending(current)) {
1452 ath6kl_err("target did not respond\n");
1453 return -EINTR;
1454 }
1455 }
1456
1457 *dbm = ar->tx_pwr;
1458 return 0;
1459}
1460
1461static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1462 struct net_device *dev,
1463 bool pmgmt, int timeout)
1464{
1465 struct ath6kl *ar = ath6kl_priv(dev);
1466 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301467 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001468
1469 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1470 __func__, pmgmt, timeout);
1471
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301472 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001473 return -EIO;
1474
1475 if (pmgmt) {
1476 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1477 mode.pwr_mode = REC_POWER;
1478 } else {
1479 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1480 mode.pwr_mode = MAX_PERF_POWER;
1481 }
1482
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301483 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001484 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001485 ath6kl_err("wmi_powermode_cmd failed\n");
1486 return -EIO;
1487 }
1488
1489 return 0;
1490}
1491
Johannes Berg84efbb82012-06-16 00:00:26 +02001492static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
Johannes Berg552bff02012-09-19 09:26:06 +02001493 const char *name,
Johannes Berg84efbb82012-06-16 00:00:26 +02001494 enum nl80211_iftype type,
1495 u32 *flags,
1496 struct vif_params *params)
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301497{
1498 struct ath6kl *ar = wiphy_priv(wiphy);
Johannes Berg84efbb82012-06-16 00:00:26 +02001499 struct wireless_dev *wdev;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301500 u8 if_idx, nw_type;
1501
Kalle Valo71f96ee2011-11-14 19:31:30 +02001502 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301503 ath6kl_err("Reached maximum number of supported vif\n");
1504 return ERR_PTR(-EINVAL);
1505 }
1506
1507 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1508 ath6kl_err("Not a supported interface type\n");
1509 return ERR_PTR(-EINVAL);
1510 }
1511
Johannes Berg84efbb82012-06-16 00:00:26 +02001512 wdev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1513 if (!wdev)
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301514 return ERR_PTR(-ENOMEM);
1515
1516 ar->num_vif++;
1517
Johannes Berg84efbb82012-06-16 00:00:26 +02001518 return wdev;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301519}
1520
1521static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
Johannes Berg84efbb82012-06-16 00:00:26 +02001522 struct wireless_dev *wdev)
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301523{
1524 struct ath6kl *ar = wiphy_priv(wiphy);
Johannes Berg84efbb82012-06-16 00:00:26 +02001525 struct ath6kl_vif *vif = netdev_priv(wdev->netdev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301526
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301527 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301528 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301529 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301530
1531 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1532
Kalle Valoc25889e2012-01-17 20:08:27 +02001533 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301534
1535 return 0;
1536}
1537
Kalle Valobdcd8172011-07-18 00:22:30 +03001538static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1539 struct net_device *ndev,
1540 enum nl80211_iftype type, u32 *flags,
1541 struct vif_params *params)
1542{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301543 struct ath6kl_vif *vif = netdev_priv(ndev);
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301544 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +03001545
1546 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1547
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301548 /*
1549 * Don't bring up p2p on an interface which is not initialized
1550 * for p2p operation where fw does not have capability to switch
1551 * dynamically between non-p2p and p2p type interface.
1552 */
1553 if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
1554 vif->ar->fw_capabilities) &&
1555 (type == NL80211_IFTYPE_P2P_CLIENT ||
1556 type == NL80211_IFTYPE_P2P_GO)) {
1557 if (vif->ar->vif_max == 1) {
1558 if (vif->fw_vif_idx != 0)
1559 return -EINVAL;
1560 else
1561 goto set_iface_type;
1562 }
1563
1564 for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) {
1565 if (i == vif->fw_vif_idx)
1566 break;
1567 }
1568
1569 if (i == vif->ar->vif_max) {
1570 ath6kl_err("Invalid interface to bring up P2P\n");
1571 return -EINVAL;
1572 }
1573 }
1574
Thomas Pedersenc422d52d2012-05-15 00:09:23 -07001575 /* need to clean up enhanced bmiss detection fw state */
1576 ath6kl_cfg80211_sta_bmiss_enhance(vif, false);
1577
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301578set_iface_type:
Kalle Valobdcd8172011-07-18 00:22:30 +03001579 switch (type) {
1580 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301581 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001582 break;
1583 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301584 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001585 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001586 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301587 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001588 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001589 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301590 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001591 break;
1592 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301593 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001594 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001595 default:
1596 ath6kl_err("invalid interface type %u\n", type);
1597 return -EOPNOTSUPP;
1598 }
1599
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301600 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001601
1602 return 0;
1603}
1604
1605static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1606 struct net_device *dev,
1607 struct cfg80211_ibss_params *ibss_param)
1608{
1609 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301610 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001611 int status;
1612
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301613 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001614 return -EIO;
1615
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301616 vif->ssid_len = ibss_param->ssid_len;
1617 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001618
Johannes Berg683b6d32012-11-08 21:25:48 +01001619 if (ibss_param->chandef.chan)
1620 vif->ch_hint = ibss_param->chandef.chan->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001621
1622 if (ibss_param->channel_fixed) {
1623 /*
1624 * TODO: channel_fixed: The channel should be fixed, do not
1625 * search for IBSSs to join on other channels. Target
1626 * firmware does not support this feature, needs to be
1627 * updated.
1628 */
1629 return -EOPNOTSUPP;
1630 }
1631
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301632 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001633 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301634 memcpy(vif->req_bssid, ibss_param->bssid,
1635 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001636
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301637 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001638
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301639 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001640 if (status)
1641 return status;
1642
1643 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301644 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1645 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001646 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301647 ath6kl_set_cipher(vif, 0, true);
1648 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001649 }
1650
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301651 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001652
1653 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1654 "%s: connect called with authmode %d dot11 auth %d"
1655 " PW crypto %d PW crypto len %d GRP crypto %d"
1656 " GRP crypto len %d channel hint %u\n",
1657 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301658 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1659 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301660 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001661
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301662 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301663 vif->dot11_auth_mode, vif->auth_mode,
1664 vif->prwise_crypto,
1665 vif->prwise_crypto_len,
1666 vif->grp_crypto, vif->grp_crypto_len,
1667 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301668 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001669 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301670 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001671
1672 return 0;
1673}
1674
1675static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1676 struct net_device *dev)
1677{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301678 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001679
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301680 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001681 return -EIO;
1682
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301683 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301684 memset(vif->ssid, 0, sizeof(vif->ssid));
1685 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001686
1687 return 0;
1688}
1689
1690static const u32 cipher_suites[] = {
1691 WLAN_CIPHER_SUITE_WEP40,
1692 WLAN_CIPHER_SUITE_WEP104,
1693 WLAN_CIPHER_SUITE_TKIP,
1694 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001695 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001696 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001697};
1698
1699static bool is_rate_legacy(s32 rate)
1700{
1701 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1702 6000, 9000, 12000, 18000, 24000,
1703 36000, 48000, 54000
1704 };
1705 u8 i;
1706
1707 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1708 if (rate == legacy[i])
1709 return true;
1710
1711 return false;
1712}
1713
1714static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1715{
1716 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1717 52000, 58500, 65000, 72200
1718 };
1719 u8 i;
1720
1721 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1722 if (rate == ht20[i]) {
1723 if (i == ARRAY_SIZE(ht20) - 1)
1724 /* last rate uses sgi */
1725 *sgi = true;
1726 else
1727 *sgi = false;
1728
1729 *mcs = i;
1730 return true;
1731 }
1732 }
1733 return false;
1734}
1735
1736static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1737{
1738 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1739 81000, 108000, 121500, 135000,
1740 150000
1741 };
1742 u8 i;
1743
1744 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1745 if (rate == ht40[i]) {
1746 if (i == ARRAY_SIZE(ht40) - 1)
1747 /* last rate uses sgi */
1748 *sgi = true;
1749 else
1750 *sgi = false;
1751
1752 *mcs = i;
1753 return true;
1754 }
1755 }
1756
1757 return false;
1758}
1759
1760static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1761 u8 *mac, struct station_info *sinfo)
1762{
1763 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301764 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001765 long left;
1766 bool sgi;
1767 s32 rate;
1768 int ret;
1769 u8 mcs;
1770
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301771 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001772 return -ENOENT;
1773
1774 if (down_interruptible(&ar->sem))
1775 return -EBUSY;
1776
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301777 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001778
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301779 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001780
1781 if (ret != 0) {
1782 up(&ar->sem);
1783 return -EIO;
1784 }
1785
1786 left = wait_event_interruptible_timeout(ar->event_wq,
1787 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301788 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001789 WMI_TIMEOUT);
1790
1791 up(&ar->sem);
1792
1793 if (left == 0)
1794 return -ETIMEDOUT;
1795 else if (left < 0)
1796 return left;
1797
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301798 if (vif->target_stats.rx_byte) {
1799 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001800 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301801 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001802 sinfo->filled |= STATION_INFO_RX_PACKETS;
1803 }
1804
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301805 if (vif->target_stats.tx_byte) {
1806 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001807 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301808 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001809 sinfo->filled |= STATION_INFO_TX_PACKETS;
1810 }
1811
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301812 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001813 sinfo->filled |= STATION_INFO_SIGNAL;
1814
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301815 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001816
1817 if (is_rate_legacy(rate)) {
1818 sinfo->txrate.legacy = rate / 100;
1819 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1820 if (sgi) {
1821 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1822 sinfo->txrate.mcs = mcs - 1;
1823 } else {
1824 sinfo->txrate.mcs = mcs;
1825 }
1826
1827 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1828 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1829 if (sgi) {
1830 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1831 sinfo->txrate.mcs = mcs - 1;
1832 } else {
1833 sinfo->txrate.mcs = mcs;
1834 }
1835
1836 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1837 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1838 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001839 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1840 "invalid rate from stats: %d\n", rate);
1841 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001842 return 0;
1843 }
1844
1845 sinfo->filled |= STATION_INFO_TX_BITRATE;
1846
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301847 if (test_bit(CONNECTED, &vif->flags) &&
1848 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301849 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001850 sinfo->filled |= STATION_INFO_BSS_PARAM;
1851 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301852 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1853 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001854 }
1855
Kalle Valobdcd8172011-07-18 00:22:30 +03001856 return 0;
1857}
1858
1859static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1860 struct cfg80211_pmksa *pmksa)
1861{
1862 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301863 struct ath6kl_vif *vif = netdev_priv(netdev);
1864
1865 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001866 pmksa->pmkid, true);
1867}
1868
1869static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1870 struct cfg80211_pmksa *pmksa)
1871{
1872 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301873 struct ath6kl_vif *vif = netdev_priv(netdev);
1874
1875 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001876 pmksa->pmkid, false);
1877}
1878
1879static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1880{
1881 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301882 struct ath6kl_vif *vif = netdev_priv(netdev);
1883
1884 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301885 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1886 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001887 return 0;
1888}
1889
Raja Manid91e8ee2012-01-30 17:13:10 +05301890static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1891 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001892{
Raja Manid91e8ee2012-01-30 17:13:10 +05301893 int ret, pos;
1894 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001895 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001896
Raja Manid91e8ee2012-01-30 17:13:10 +05301897 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001898 for (i = 0; i < wow->n_patterns; i++) {
1899
1900 /*
1901 * Convert given nl80211 specific mask value to equivalent
1902 * driver specific mask value and send it to the chip along
1903 * with patterns. For example, If the mask value defined in
1904 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1905 * then equivalent driver specific mask value is
1906 * "0xFF 0x00 0xFF 0x00".
1907 */
1908 memset(&mask, 0, sizeof(mask));
1909 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1910 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1911 mask[pos] = 0xFF;
1912 }
1913 /*
1914 * Note: Pattern's offset is not passed as part of wowlan
1915 * parameter from CFG layer. So it's always passed as ZERO
1916 * to the firmware. It means, given WOW patterns are always
1917 * matched from the first byte of received pkt in the firmware.
1918 */
1919 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301920 vif->fw_vif_idx, WOW_LIST_ID,
1921 wow->patterns[i].pattern_len,
1922 0 /* pattern offset */,
1923 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001924 if (ret)
1925 return ret;
1926 }
1927
Raja Manid91e8ee2012-01-30 17:13:10 +05301928 if (wow->disconnect)
1929 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1930
1931 if (wow->magic_pkt)
1932 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1933
1934 if (wow->gtk_rekey_failure)
1935 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1936
1937 if (wow->eap_identity_req)
1938 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1939
1940 if (wow->four_way_handshake)
1941 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1942
1943 return 0;
1944}
1945
1946static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1947{
1948 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1951 0x00, 0x08 };
1952 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1953 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1954 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1955 0x00, 0x7f };
1956 u8 unicst_offset = 0;
1957 static const u8 arp_pattern[] = { 0x08, 0x06 };
1958 static const u8 arp_mask[] = { 0xff, 0xff };
1959 u8 arp_offset = 20;
1960 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1961 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1962 u8 discvr_offset = 38;
1963 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1964 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1965 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1966 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1967 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1968 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1969 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1970 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1971 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1972 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1973 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1974 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1975 u8 dhcp_offset = 0;
1976 int ret;
1977
1978 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1979 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1980 vif->fw_vif_idx, WOW_LIST_ID,
1981 sizeof(unicst_pattern), unicst_offset,
1982 unicst_pattern, unicst_mask);
1983 if (ret) {
1984 ath6kl_err("failed to add WOW unicast IP pattern\n");
1985 return ret;
1986 }
1987
1988 /* Setup all ARP pkt pattern */
1989 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1990 vif->fw_vif_idx, WOW_LIST_ID,
1991 sizeof(arp_pattern), arp_offset,
1992 arp_pattern, arp_mask);
1993 if (ret) {
1994 ath6kl_err("failed to add WOW ARP pattern\n");
1995 return ret;
1996 }
1997
1998 /*
1999 * Setup multicast pattern for mDNS 224.0.0.251,
2000 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
2001 */
2002 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
2003 vif->fw_vif_idx, WOW_LIST_ID,
2004 sizeof(discvr_pattern), discvr_offset,
2005 discvr_pattern, discvr_mask);
2006 if (ret) {
2007 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
2008 return ret;
2009 }
2010
2011 /* Setup all DHCP broadcast pkt pattern */
2012 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
2013 vif->fw_vif_idx, WOW_LIST_ID,
2014 sizeof(dhcp_pattern), dhcp_offset,
2015 dhcp_pattern, dhcp_mask);
2016 if (ret) {
2017 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
2018 return ret;
2019 }
2020
2021 return 0;
2022}
2023
2024static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
2025{
2026 struct net_device *ndev = vif->ndev;
2027 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
2028 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
2029 u8 discvr_offset = 38;
2030 u8 mac_mask[ETH_ALEN];
2031 int ret;
2032
2033 /* Setup unicast pkt pattern */
2034 memset(mac_mask, 0xff, ETH_ALEN);
2035 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
2036 vif->fw_vif_idx, WOW_LIST_ID,
2037 ETH_ALEN, 0, ndev->dev_addr,
2038 mac_mask);
2039 if (ret) {
2040 ath6kl_err("failed to add WOW unicast pattern\n");
2041 return ret;
2042 }
2043
2044 /*
2045 * Setup multicast pattern for mDNS 224.0.0.251,
2046 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
2047 */
2048 if ((ndev->flags & IFF_ALLMULTI) ||
2049 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
2050 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
2051 vif->fw_vif_idx, WOW_LIST_ID,
2052 sizeof(discvr_pattern), discvr_offset,
2053 discvr_pattern, discvr_mask);
2054 if (ret) {
Kalle Valocdeb8602012-04-12 11:02:18 +03002055 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
Raja Manid91e8ee2012-01-30 17:13:10 +05302056 return ret;
2057 }
2058 }
2059
2060 return 0;
2061}
2062
Raja Mani055bde42012-03-21 15:03:37 +05302063static int is_hsleep_mode_procsed(struct ath6kl_vif *vif)
2064{
2065 return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
2066}
2067
2068static bool is_ctrl_ep_empty(struct ath6kl *ar)
2069{
2070 return !ar->tx_pending[ar->ctrl_ep];
2071}
2072
2073static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
2074{
2075 int ret, left;
2076
2077 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
2078
2079 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2080 ATH6KL_HOST_MODE_ASLEEP);
2081 if (ret)
2082 return ret;
2083
2084 left = wait_event_interruptible_timeout(ar->event_wq,
2085 is_hsleep_mode_procsed(vif),
2086 WMI_TIMEOUT);
2087 if (left == 0) {
2088 ath6kl_warn("timeout, didn't get host sleep cmd processed event\n");
2089 ret = -ETIMEDOUT;
2090 } else if (left < 0) {
2091 ath6kl_warn("error while waiting for host sleep cmd processed event %d\n",
2092 left);
2093 ret = left;
2094 }
2095
2096 if (ar->tx_pending[ar->ctrl_ep]) {
2097 left = wait_event_interruptible_timeout(ar->event_wq,
2098 is_ctrl_ep_empty(ar),
2099 WMI_TIMEOUT);
2100 if (left == 0) {
2101 ath6kl_warn("clear wmi ctrl data timeout\n");
2102 ret = -ETIMEDOUT;
2103 } else if (left < 0) {
2104 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
2105 ret = left;
2106 }
2107 }
2108
2109 return ret;
2110}
2111
Raja Manid91e8ee2012-01-30 17:13:10 +05302112static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
2113{
2114 struct in_device *in_dev;
2115 struct in_ifaddr *ifa;
2116 struct ath6kl_vif *vif;
Raja Mani055bde42012-03-21 15:03:37 +05302117 int ret;
Raja Manid91e8ee2012-01-30 17:13:10 +05302118 u32 filter = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +05302119 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05302120 u8 index = 0;
2121 __be32 ips[MAX_IP_ADDRS];
2122
Thomas Pedersen77ed4e42012-04-19 16:29:19 -07002123 /* The FW currently can't support multi-vif WoW properly. */
2124 if (ar->num_vif > 1)
2125 return -EIO;
2126
Raja Manid91e8ee2012-01-30 17:13:10 +05302127 vif = ath6kl_vif_first(ar);
2128 if (!vif)
2129 return -EIO;
2130
2131 if (!ath6kl_cfg80211_ready(vif))
2132 return -EIO;
2133
2134 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05302135 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05302136
2137 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
2138 return -EINVAL;
2139
Naveen Gangadharan6821d4f2012-05-11 14:19:09 -07002140 if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) &&
2141 test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
2142 ar->fw_capabilities)) {
Naveen Gangadharan6251d802012-04-20 12:46:56 -07002143 ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
2144 vif->fw_vif_idx, false);
2145 if (ret)
2146 return ret;
2147 }
2148
Raja Manid91e8ee2012-01-30 17:13:10 +05302149 /* Clear existing WOW patterns */
2150 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
2151 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
2152 WOW_LIST_ID, i);
2153
2154 /*
2155 * Skip the default WOW pattern configuration
2156 * if the driver receives any WOW patterns from
2157 * the user.
2158 */
2159 if (wow)
2160 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
2161 else if (vif->nw_type == AP_NETWORK)
2162 ret = ath6kl_wow_ap(ar, vif);
2163 else
2164 ret = ath6kl_wow_sta(ar, vif);
2165
2166 if (ret)
2167 return ret;
2168
Raja Mani390a8c82012-03-07 11:35:04 +05302169 netif_stop_queue(vif->ndev);
2170
Raja Manice0dc0c2012-02-20 19:08:08 +05302171 if (vif->nw_type != AP_NETWORK) {
2172 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2173 ATH6KL_MAX_WOW_LISTEN_INTL,
2174 0);
2175 if (ret)
2176 return ret;
2177
2178 /* Set listen interval x 15 times as bmiss time */
2179 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
2180 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
2181 bmiss_time = ATH6KL_MAX_BMISS_TIME;
2182
2183 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2184 bmiss_time, 0);
2185 if (ret)
2186 return ret;
2187
2188 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2189 0xFFFF, 0, 0xFFFF, 0, 0, 0,
2190 0, 0, 0, 0);
2191 if (ret)
2192 return ret;
2193 }
2194
Raja Mani390a8c82012-03-07 11:35:04 +05302195 ar->state = ATH6KL_STATE_SUSPENDING;
2196
Raja Manic08631c2011-12-16 14:24:24 +05302197 /* Setup own IP addr for ARP agent. */
2198 in_dev = __in_dev_get_rtnl(vif->ndev);
2199 if (!in_dev)
2200 goto skip_arp;
2201
2202 ifa = in_dev->ifa_list;
2203 memset(&ips, 0, sizeof(ips));
2204
2205 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2206 while (index < MAX_IP_ADDRS && ifa) {
2207 ips[index] = ifa->ifa_local;
2208 ifa = ifa->ifa_next;
2209 index++;
2210 }
2211
2212 if (ifa) {
2213 ath6kl_err("total IP addr count is exceeding fw limit\n");
2214 return -EINVAL;
2215 }
2216
2217 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2218 if (ret) {
2219 ath6kl_err("fail to setup ip for arp agent\n");
2220 return ret;
2221 }
2222
2223skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02002224 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2225 ATH6KL_WOW_MODE_ENABLE,
2226 filter,
2227 WOW_HOST_REQ_DELAY);
2228 if (ret)
2229 return ret;
2230
Raja Mani055bde42012-03-21 15:03:37 +05302231 ret = ath6kl_cfg80211_host_sleep(ar, vif);
Raja Mani6cb3c712011-11-07 22:52:45 +02002232 if (ret)
2233 return ret;
2234
Raja Mani055bde42012-03-21 15:03:37 +05302235 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002236}
2237
2238static int ath6kl_wow_resume(struct ath6kl *ar)
2239{
2240 struct ath6kl_vif *vif;
2241 int ret;
2242
2243 vif = ath6kl_vif_first(ar);
2244 if (!vif)
2245 return -EIO;
2246
Raja Mani390a8c82012-03-07 11:35:04 +05302247 ar->state = ATH6KL_STATE_RESUMING;
2248
Raja Mani6cb3c712011-11-07 22:52:45 +02002249 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2250 ATH6KL_HOST_MODE_AWAKE);
Raja Mani390a8c82012-03-07 11:35:04 +05302251 if (ret) {
Kalle Valocdeb8602012-04-12 11:02:18 +03002252 ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n",
2253 ret);
Raja Mani390a8c82012-03-07 11:35:04 +05302254 ar->state = ATH6KL_STATE_WOW;
2255 return ret;
2256 }
2257
Raja Manice0dc0c2012-02-20 19:08:08 +05302258 if (vif->nw_type != AP_NETWORK) {
2259 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2260 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2261 if (ret)
2262 return ret;
2263
2264 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2265 vif->listen_intvl_t, 0);
2266 if (ret)
2267 return ret;
2268
2269 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2270 vif->bmiss_time_t, 0);
2271 if (ret)
2272 return ret;
2273 }
2274
Raja Mani390a8c82012-03-07 11:35:04 +05302275 ar->state = ATH6KL_STATE_ON;
2276
Naveen Gangadharan6821d4f2012-05-11 14:19:09 -07002277 if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) &&
2278 test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
2279 ar->fw_capabilities)) {
Naveen Gangadharan6251d802012-04-20 12:46:56 -07002280 ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
2281 vif->fw_vif_idx, true);
2282 if (ret)
2283 return ret;
2284 }
2285
Raja Mani390a8c82012-03-07 11:35:04 +05302286 netif_wake_queue(vif->ndev);
2287
2288 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002289}
2290
Raja Mani40abc2d2012-03-21 15:03:38 +05302291static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
2292{
2293 struct ath6kl_vif *vif;
2294 int ret;
2295
2296 vif = ath6kl_vif_first(ar);
2297 if (!vif)
2298 return -EIO;
2299
Ming Jiang48f27582012-04-13 21:09:25 +08002300 if (!test_bit(WMI_READY, &ar->flag)) {
2301 ath6kl_err("deepsleep failed as wmi is not ready\n");
Raja Mani40abc2d2012-03-21 15:03:38 +05302302 return -EIO;
Ming Jiang48f27582012-04-13 21:09:25 +08002303 }
Raja Mani40abc2d2012-03-21 15:03:38 +05302304
2305 ath6kl_cfg80211_stop_all(ar);
2306
2307 /* Save the current power mode before enabling power save */
2308 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2309
2310 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2311 if (ret)
2312 return ret;
2313
2314 /* Disable WOW mode */
2315 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2316 ATH6KL_WOW_MODE_DISABLE,
2317 0, 0);
2318 if (ret)
2319 return ret;
2320
2321 /* Flush all non control pkts in TX path */
2322 ath6kl_tx_data_cleanup(ar);
2323
2324 ret = ath6kl_cfg80211_host_sleep(ar, vif);
2325 if (ret)
2326 return ret;
2327
2328 return 0;
2329}
2330
2331static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
2332{
2333 struct ath6kl_vif *vif;
2334 int ret;
2335
2336 vif = ath6kl_vif_first(ar);
2337
2338 if (!vif)
2339 return -EIO;
2340
2341 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2342 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2343 ar->wmi->saved_pwr_mode);
2344 if (ret)
2345 return ret;
2346 }
2347
2348 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2349 ATH6KL_HOST_MODE_AWAKE);
2350 if (ret)
2351 return ret;
2352
2353 ar->state = ATH6KL_STATE_ON;
2354
2355 /* Reset scan parameter to default values */
2356 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2357 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2358 if (ret)
2359 return ret;
2360
2361 return 0;
2362}
2363
Kalle Valo52d81a62011-11-01 08:44:21 +02002364int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002365 enum ath6kl_cfg_suspend_mode mode,
2366 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002367{
Vivek Natarajan3d794992012-03-28 19:21:26 +05302368 struct ath6kl_vif *vif;
Raja Mani390a8c82012-03-07 11:35:04 +05302369 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002370 int ret;
2371
Kalle Valo52d81a62011-11-01 08:44:21 +02002372 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002373 case ATH6KL_CFG_SUSPEND_WOW:
2374
2375 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2376
2377 /* Flush all non control pkts in TX path */
2378 ath6kl_tx_data_cleanup(ar);
2379
Raja Mani390a8c82012-03-07 11:35:04 +05302380 prev_state = ar->state;
2381
Raja Manid7c44e02011-11-07 22:52:46 +02002382 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302383 if (ret) {
2384 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002385 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302386 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302387
Raja Manid7c44e02011-11-07 22:52:46 +02002388 ar->state = ATH6KL_STATE_WOW;
2389 break;
2390
Kalle Valo52d81a62011-11-01 08:44:21 +02002391 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002392
Raja Mani40abc2d2012-03-21 15:03:38 +05302393 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
Raja Mani524441e2011-11-07 22:52:46 +02002394
Raja Mani40abc2d2012-03-21 15:03:38 +05302395 ret = ath6kl_cfg80211_deepsleep_suspend(ar);
Kalle Valo52d81a62011-11-01 08:44:21 +02002396 if (ret) {
Raja Mani40abc2d2012-03-21 15:03:38 +05302397 ath6kl_err("deepsleep suspend failed: %d\n", ret);
2398 return ret;
Kalle Valo52d81a62011-11-01 08:44:21 +02002399 }
2400
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002401 ar->state = ATH6KL_STATE_DEEPSLEEP;
2402
Kalle Valo52d81a62011-11-01 08:44:21 +02002403 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002404
2405 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002406
Kalle Valo7125f012011-12-13 14:51:37 +02002407 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002408
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002409 if (ar->state == ATH6KL_STATE_OFF) {
2410 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2411 "suspend hw off, no action for cutpower\n");
2412 break;
2413 }
2414
2415 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2416
2417 ret = ath6kl_init_hw_stop(ar);
2418 if (ret) {
2419 ath6kl_warn("failed to stop hw during suspend: %d\n",
2420 ret);
2421 }
2422
2423 ar->state = ATH6KL_STATE_CUTPOWER;
2424
2425 break;
2426
Kalle Valo10509f92011-12-13 14:52:07 +02002427 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2428 /*
2429 * Nothing needed for schedule scan, firmware is already in
2430 * wow mode and sleeping most of the time.
2431 */
2432 break;
2433
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002434 default:
2435 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002436 }
2437
Vivek Natarajan3d794992012-03-28 19:21:26 +05302438 list_for_each_entry(vif, &ar->vif_list, list)
2439 ath6kl_cfg80211_scan_complete_event(vif, true);
2440
Kalle Valo52d81a62011-11-01 08:44:21 +02002441 return 0;
2442}
Kalle Valod6a434d2012-01-17 20:09:36 +02002443EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002444
2445int ath6kl_cfg80211_resume(struct ath6kl *ar)
2446{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002447 int ret;
2448
2449 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002450 case ATH6KL_STATE_WOW:
2451 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2452
2453 ret = ath6kl_wow_resume(ar);
2454 if (ret) {
2455 ath6kl_warn("wow mode resume failed: %d\n", ret);
2456 return ret;
2457 }
2458
Raja Manid7c44e02011-11-07 22:52:46 +02002459 break;
2460
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002461 case ATH6KL_STATE_DEEPSLEEP:
Raja Mani40abc2d2012-03-21 15:03:38 +05302462 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
2463
2464 ret = ath6kl_cfg80211_deepsleep_resume(ar);
2465 if (ret) {
2466 ath6kl_warn("deep sleep resume failed: %d\n", ret);
2467 return ret;
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002468 }
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002469 break;
2470
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002471 case ATH6KL_STATE_CUTPOWER:
2472 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2473
2474 ret = ath6kl_init_hw_start(ar);
2475 if (ret) {
2476 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2477 return ret;
2478 }
Raja Manid7c44e02011-11-07 22:52:46 +02002479 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002480
Kalle Valo10509f92011-12-13 14:52:07 +02002481 case ATH6KL_STATE_SCHED_SCAN:
2482 break;
2483
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002484 default:
2485 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002486 }
2487
2488 return 0;
2489}
Kalle Valod6a434d2012-01-17 20:09:36 +02002490EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002491
Kalle Valoabcb3442011-07-22 08:26:20 +03002492#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002493
2494/* hif layer decides what suspend mode to use */
2495static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002496 struct cfg80211_wowlan *wow)
2497{
2498 struct ath6kl *ar = wiphy_priv(wiphy);
2499
Raja Mani0f60e9f2011-11-07 22:52:45 +02002500 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002501}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002502
Kalle Valo52d81a62011-11-01 08:44:21 +02002503static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002504{
2505 struct ath6kl *ar = wiphy_priv(wiphy);
2506
2507 return ath6kl_hif_resume(ar);
2508}
Raja Mania918fb32011-11-07 22:52:46 +02002509
2510/*
2511 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2512 * both sdio irq wake up and keep power. The target pulls sdio data line to
2513 * wake up the host when WOW pattern matches. This causes sdio irq handler
2514 * is being called in the host side which internally hits ath6kl's RX path.
2515 *
2516 * Since sdio interrupt is not disabled, RX path executes even before
2517 * the host executes the actual resume operation from PM module.
2518 *
2519 * In the current scenario, WOW resume should happen before start processing
2520 * any data from the target. So It's required to perform WOW resume in RX path.
2521 * Ideally we should perform WOW resume only in the actual platform
2522 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2523 *
2524 * ath6kl_check_wow_status() is called from ath6kl_rx().
2525 */
2526void ath6kl_check_wow_status(struct ath6kl *ar)
2527{
Raja Mani390a8c82012-03-07 11:35:04 +05302528 if (ar->state == ATH6KL_STATE_SUSPENDING)
2529 return;
2530
Raja Mania918fb32011-11-07 22:52:46 +02002531 if (ar->state == ATH6KL_STATE_WOW)
2532 ath6kl_cfg80211_resume(ar);
2533}
2534
2535#else
2536
2537void ath6kl_check_wow_status(struct ath6kl *ar)
2538{
2539}
Kalle Valoabcb3442011-07-22 08:26:20 +03002540#endif
2541
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302542static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
2543 bool ht_enable)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002544{
Kiran Reddy67b3f122012-05-29 11:12:50 -07002545 struct ath6kl_htcap *htcap = &vif->htcap[band];
Sujith Manoharane68f6752011-12-22 12:15:27 +05302546
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302547 if (htcap->ht_enable == ht_enable)
2548 return 0;
Sujith Manoharane68f6752011-12-22 12:15:27 +05302549
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302550 if (ht_enable) {
2551 /* Set default ht capabilities */
2552 htcap->ht_enable = true;
2553 htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ?
2554 ath6kl_g_htcap : ath6kl_a_htcap;
2555 htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
2556 } else /* Disable ht */
2557 memset(htcap, 0, sizeof(*htcap));
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002558
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302559 return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx,
2560 band, htcap);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002561}
2562
Thomas Pedersen37a2f952012-04-19 15:31:57 -07002563static int ath6kl_restore_htcap(struct ath6kl_vif *vif)
2564{
2565 struct wiphy *wiphy = vif->ar->wiphy;
2566 int band, ret = 0;
2567
2568 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2569 if (!wiphy->bands[band])
2570 continue;
2571
2572 ret = ath6kl_set_htcap(vif, band,
2573 wiphy->bands[band]->ht_cap.ht_supported);
2574 if (ret)
2575 return ret;
2576 }
2577
2578 return ret;
2579}
2580
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002581static bool ath6kl_is_p2p_ie(const u8 *pos)
2582{
2583 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2584 pos[2] == 0x50 && pos[3] == 0x6f &&
2585 pos[4] == 0x9a && pos[5] == 0x09;
2586}
2587
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302588static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2589 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002590{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302591 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002592 const u8 *pos;
2593 u8 *buf = NULL;
2594 size_t len = 0;
2595 int ret;
2596
2597 /*
2598 * Filter out P2P IE(s) since they will be included depending on
2599 * the Probe Request frame in ath6kl_send_go_probe_resp().
2600 */
2601
2602 if (ies && ies_len) {
2603 buf = kmalloc(ies_len, GFP_KERNEL);
2604 if (buf == NULL)
2605 return -ENOMEM;
2606 pos = ies;
2607 while (pos + 1 < ies + ies_len) {
2608 if (pos + 2 + pos[1] > ies + ies_len)
2609 break;
2610 if (!ath6kl_is_p2p_ie(pos)) {
2611 memcpy(buf + len, pos, 2 + pos[1]);
2612 len += 2 + pos[1];
2613 }
2614 pos += 2 + pos[1];
2615 }
2616 }
2617
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302618 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2619 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002620 kfree(buf);
2621 return ret;
2622}
2623
Johannes Berg88600202012-02-13 15:17:18 +01002624static int ath6kl_set_ies(struct ath6kl_vif *vif,
2625 struct cfg80211_beacon_data *info)
2626{
2627 struct ath6kl *ar = vif->ar;
2628 int res;
2629
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002630 /* this also clears IE in fw if it's not set */
2631 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2632 WMI_FRAME_BEACON,
2633 info->beacon_ies,
2634 info->beacon_ies_len);
2635 if (res)
2636 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002637
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002638 /* this also clears IE in fw if it's not set */
2639 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2640 info->proberesp_ies_len);
2641 if (res)
2642 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002643
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002644 /* this also clears IE in fw if it's not set */
2645 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2646 WMI_FRAME_ASSOC_RESP,
2647 info->assocresp_ies,
2648 info->assocresp_ies_len);
2649 if (res)
2650 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002651
2652 return 0;
2653}
2654
Thomas Pedersenc422d52d2012-05-15 00:09:23 -07002655void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable)
2656{
2657 int err;
2658
2659 if (WARN_ON(!test_bit(WMI_READY, &vif->ar->flag)))
2660 return;
2661
2662 if (vif->nw_type != INFRA_NETWORK)
2663 return;
2664
2665 if (!test_bit(ATH6KL_FW_CAPABILITY_BMISS_ENHANCE,
2666 vif->ar->fw_capabilities))
2667 return;
2668
2669 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s fw bmiss enhance\n",
2670 enable ? "enable" : "disable");
2671
2672 err = ath6kl_wmi_sta_bmiss_enhance_cmd(vif->ar->wmi,
2673 vif->fw_vif_idx, enable);
2674 if (err)
2675 ath6kl_err("failed to %s enhanced bmiss detection: %d\n",
2676 enable ? "enable" : "disable", err);
2677}
2678
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302679static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
2680 u8 *rsn_capab)
2681{
2682 const u8 *rsn_ie;
2683 size_t rsn_ie_len;
2684 u16 cnt;
2685
2686 if (!beacon->tail)
2687 return -EINVAL;
2688
2689 rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len);
2690 if (!rsn_ie)
2691 return -EINVAL;
2692
2693 rsn_ie_len = *(rsn_ie + 1);
2694 /* skip element id and length */
2695 rsn_ie += 2;
2696
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302697 /* skip version */
2698 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302699 return -EINVAL;
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302700 rsn_ie += 2;
2701 rsn_ie_len -= 2;
2702
2703 /* skip group cipher suite */
2704 if (rsn_ie_len < 4)
2705 return 0;
2706 rsn_ie += 4;
2707 rsn_ie_len -= 4;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302708
2709 /* skip pairwise cipher suite */
2710 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302711 return 0;
Vasanthakumar Thiagarajan798985c2012-04-10 13:35:47 +05302712 cnt = get_unaligned_le16(rsn_ie);
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302713 rsn_ie += (2 + cnt * 4);
2714 rsn_ie_len -= (2 + cnt * 4);
2715
2716 /* skip akm suite */
2717 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302718 return 0;
Vasanthakumar Thiagarajan798985c2012-04-10 13:35:47 +05302719 cnt = get_unaligned_le16(rsn_ie);
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302720 rsn_ie += (2 + cnt * 4);
2721 rsn_ie_len -= (2 + cnt * 4);
2722
2723 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302724 return 0;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302725
2726 memcpy(rsn_capab, rsn_ie, 2);
2727
2728 return 0;
2729}
2730
Johannes Berg88600202012-02-13 15:17:18 +01002731static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2732 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002733{
2734 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302735 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002736 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002737 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002738 u8 *ies;
2739 int ies_len;
2740 struct wmi_connect_cmd p;
2741 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302742 int i, ret;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302743 u16 rsn_capab = 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002744
Johannes Berg88600202012-02-13 15:17:18 +01002745 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002746
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302747 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002748 return -EIO;
2749
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302750 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002751 return -EOPNOTSUPP;
2752
Johannes Berg88600202012-02-13 15:17:18 +01002753 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002754
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002755 ar->ap_mode_bkey.valid = false;
2756
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002757 /* TODO:
2758 * info->interval
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002759 */
2760
Etay Luzd154f322012-05-30 11:35:08 +03002761 ret = ath6kl_wmi_ap_set_dtim_cmd(ar->wmi, vif->fw_vif_idx,
2762 info->dtim_period);
2763
2764 /* ignore error, just print a warning and continue normally */
2765 if (ret)
2766 ath6kl_warn("Failed to set dtim_period in beacon: %d\n", ret);
2767
Johannes Berg88600202012-02-13 15:17:18 +01002768 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002769 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002770 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002771 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002772 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002773 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002774 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002775
2776 if (info->ssid == NULL)
2777 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302778 memcpy(vif->ssid, info->ssid, info->ssid_len);
2779 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002780 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002781 hidden = true;
2782
2783 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2784 if (res)
2785 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002786
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302787 ret = ath6kl_set_auth_type(vif, info->auth_type);
2788 if (ret)
2789 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002790
2791 memset(&p, 0, sizeof(p));
2792
2793 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2794 switch (info->crypto.akm_suites[i]) {
2795 case WLAN_AKM_SUITE_8021X:
2796 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2797 p.auth_mode |= WPA_AUTH;
2798 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2799 p.auth_mode |= WPA2_AUTH;
2800 break;
2801 case WLAN_AKM_SUITE_PSK:
2802 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2803 p.auth_mode |= WPA_PSK_AUTH;
2804 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2805 p.auth_mode |= WPA2_PSK_AUTH;
2806 break;
2807 }
2808 }
2809 if (p.auth_mode == 0)
2810 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302811 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002812
2813 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2814 switch (info->crypto.ciphers_pairwise[i]) {
2815 case WLAN_CIPHER_SUITE_WEP40:
2816 case WLAN_CIPHER_SUITE_WEP104:
2817 p.prwise_crypto_type |= WEP_CRYPT;
2818 break;
2819 case WLAN_CIPHER_SUITE_TKIP:
2820 p.prwise_crypto_type |= TKIP_CRYPT;
2821 break;
2822 case WLAN_CIPHER_SUITE_CCMP:
2823 p.prwise_crypto_type |= AES_CRYPT;
2824 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002825 case WLAN_CIPHER_SUITE_SMS4:
2826 p.prwise_crypto_type |= WAPI_CRYPT;
2827 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002828 }
2829 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002830 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002831 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302832 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002833 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302834 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002835
2836 switch (info->crypto.cipher_group) {
2837 case WLAN_CIPHER_SUITE_WEP40:
2838 case WLAN_CIPHER_SUITE_WEP104:
2839 p.grp_crypto_type = WEP_CRYPT;
2840 break;
2841 case WLAN_CIPHER_SUITE_TKIP:
2842 p.grp_crypto_type = TKIP_CRYPT;
2843 break;
2844 case WLAN_CIPHER_SUITE_CCMP:
2845 p.grp_crypto_type = AES_CRYPT;
2846 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002847 case WLAN_CIPHER_SUITE_SMS4:
2848 p.grp_crypto_type = WAPI_CRYPT;
2849 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002850 default:
2851 p.grp_crypto_type = NONE_CRYPT;
2852 break;
2853 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302854 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002855
2856 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302857 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002858
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302859 p.ssid_len = vif->ssid_len;
2860 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2861 p.dot11_auth_mode = vif->dot11_auth_mode;
Johannes Berg683b6d32012-11-08 21:25:48 +01002862 p.ch = cpu_to_le16(info->chandef.chan->center_freq);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002863
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302864 /* Enable uAPSD support by default */
2865 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2866 if (res < 0)
2867 return res;
2868
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002869 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2870 p.nw_subtype = SUBTYPE_P2PGO;
2871 } else {
2872 /*
2873 * Due to firmware limitation, it is not possible to
2874 * do P2P mgmt operations in AP mode
2875 */
2876 p.nw_subtype = SUBTYPE_NONE;
2877 }
2878
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05302879 if (info->inactivity_timeout) {
2880 res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
2881 info->inactivity_timeout);
2882 if (res < 0)
2883 return res;
2884 }
2885
Johannes Berg683b6d32012-11-08 21:25:48 +01002886 if (ath6kl_set_htcap(vif, info->chandef.chan->band,
2887 cfg80211_get_chandef_type(&info->chandef)
2888 != NL80211_CHAN_NO_HT))
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302889 return -EIO;
2890
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302891 /*
2892 * Get the PTKSA replay counter in the RSN IE. Supplicant
2893 * will use the RSN IE in M3 message and firmware has to
2894 * advertise the same in beacon/probe response. Send
2895 * the complete RSN IE capability field to firmware
2896 */
2897 if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) &&
2898 test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
2899 ar->fw_capabilities)) {
2900 res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
2901 WLAN_EID_RSN, WMI_RSN_IE_CAPB,
2902 (const u8 *) &rsn_capab,
2903 sizeof(rsn_capab));
2904 if (res < 0)
2905 return res;
2906 }
2907
Thomas Pedersenc4f78632012-04-06 13:35:48 -07002908 memcpy(&vif->profile, &p, sizeof(p));
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302909 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002910 if (res < 0)
2911 return res;
2912
2913 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002914}
2915
Johannes Berg88600202012-02-13 15:17:18 +01002916static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2917 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002918{
Johannes Berg88600202012-02-13 15:17:18 +01002919 struct ath6kl_vif *vif = netdev_priv(dev);
2920
2921 if (!ath6kl_cfg80211_ready(vif))
2922 return -EIO;
2923
2924 if (vif->next_mode != AP_NETWORK)
2925 return -EOPNOTSUPP;
2926
2927 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002928}
2929
Johannes Berg88600202012-02-13 15:17:18 +01002930static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002931{
2932 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302933 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002934
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302935 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002936 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302937 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002938 return -ENOTCONN;
2939
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302940 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302941 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002942
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302943 /* Restore ht setting in firmware */
Thomas Pedersen37a2f952012-04-19 15:31:57 -07002944 return ath6kl_restore_htcap(vif);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002945}
2946
Jouni Malinen33e53082011-12-27 11:02:56 +02002947static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2948
2949static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2950 u8 *mac)
2951{
2952 struct ath6kl *ar = ath6kl_priv(dev);
2953 struct ath6kl_vif *vif = netdev_priv(dev);
2954 const u8 *addr = mac ? mac : bcast_addr;
2955
2956 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2957 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2958}
2959
Jouni Malinen23875132011-08-30 21:57:53 +03002960static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2961 u8 *mac, struct station_parameters *params)
2962{
2963 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302964 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002965
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302966 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002967 return -EOPNOTSUPP;
2968
2969 /* Use this only for authorizing/unauthorizing a station */
2970 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2971 return -EOPNOTSUPP;
2972
2973 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302974 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2975 WMI_AP_MLME_AUTHORIZE, mac, 0);
2976 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2977 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002978}
2979
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002980static int ath6kl_remain_on_channel(struct wiphy *wiphy,
Johannes Berg71bbc992012-06-15 15:30:18 +02002981 struct wireless_dev *wdev,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002982 struct ieee80211_channel *chan,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002983 unsigned int duration,
2984 u64 *cookie)
2985{
Johannes Berg71bbc992012-06-15 15:30:18 +02002986 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
2987 struct ath6kl *ar = ath6kl_priv(vif->ndev);
Jouni Malinen10522612011-10-27 16:00:13 +03002988 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002989
2990 /* TODO: if already pending or ongoing remain-on-channel,
2991 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002992 id = ++vif->last_roc_id;
2993 if (id == 0) {
2994 /* Do not use 0 as the cookie value */
2995 id = ++vif->last_roc_id;
2996 }
2997 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002998
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302999 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
3000 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003001}
3002
3003static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
Johannes Berg71bbc992012-06-15 15:30:18 +02003004 struct wireless_dev *wdev,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003005 u64 cookie)
3006{
Johannes Berg71bbc992012-06-15 15:30:18 +02003007 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
3008 struct ath6kl *ar = ath6kl_priv(vif->ndev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003009
Jouni Malinen10522612011-10-27 16:00:13 +03003010 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003011 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03003012 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003013
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303014 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003015}
3016
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303017static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
3018 const u8 *buf, size_t len,
3019 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003020{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303021 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003022 const u8 *pos;
3023 u8 *p2p;
3024 int p2p_len;
3025 int ret;
3026 const struct ieee80211_mgmt *mgmt;
3027
3028 mgmt = (const struct ieee80211_mgmt *) buf;
3029
3030 /* Include P2P IE(s) from the frame generated in user space. */
3031
3032 p2p = kmalloc(len, GFP_KERNEL);
3033 if (p2p == NULL)
3034 return -ENOMEM;
3035 p2p_len = 0;
3036
3037 pos = mgmt->u.probe_resp.variable;
3038 while (pos + 1 < buf + len) {
3039 if (pos + 2 + pos[1] > buf + len)
3040 break;
3041 if (ath6kl_is_p2p_ie(pos)) {
3042 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
3043 p2p_len += 2 + pos[1];
3044 }
3045 pos += 2 + pos[1];
3046 }
3047
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303048 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
3049 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003050 kfree(p2p);
3051 return ret;
3052}
3053
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003054static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
3055 u32 id,
3056 u32 freq,
3057 u32 wait,
3058 const u8 *buf,
3059 size_t len,
3060 bool *more_data,
3061 bool no_cck)
3062{
3063 struct ieee80211_mgmt *mgmt;
3064 struct ath6kl_sta *conn;
3065 bool is_psq_empty = false;
3066 struct ath6kl_mgmt_buff *mgmt_buf;
3067 size_t mgmt_buf_size;
3068 struct ath6kl *ar = vif->ar;
3069
3070 mgmt = (struct ieee80211_mgmt *) buf;
3071 if (is_multicast_ether_addr(mgmt->da))
3072 return false;
3073
3074 conn = ath6kl_find_sta(vif, mgmt->da);
3075 if (!conn)
3076 return false;
3077
3078 if (conn->sta_flags & STA_PS_SLEEP) {
3079 if (!(conn->sta_flags & STA_PS_POLLED)) {
3080 /* Queue the frames if the STA is sleeping */
3081 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
3082 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
3083 if (!mgmt_buf)
3084 return false;
3085
3086 INIT_LIST_HEAD(&mgmt_buf->list);
3087 mgmt_buf->id = id;
3088 mgmt_buf->freq = freq;
3089 mgmt_buf->wait = wait;
3090 mgmt_buf->len = len;
3091 mgmt_buf->no_cck = no_cck;
3092 memcpy(mgmt_buf->buf, buf, len);
3093 spin_lock_bh(&conn->psq_lock);
3094 is_psq_empty = skb_queue_empty(&conn->psq) &&
3095 (conn->mgmt_psq_len == 0);
3096 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
3097 conn->mgmt_psq_len++;
3098 spin_unlock_bh(&conn->psq_lock);
3099
3100 /*
3101 * If this is the first pkt getting queued
3102 * for this STA, update the PVB for this
3103 * STA.
3104 */
3105 if (is_psq_empty)
3106 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
3107 conn->aid, 1);
3108 return true;
3109 }
3110
3111 /*
3112 * This tx is because of a PsPoll.
3113 * Determine if MoreData bit has to be set.
3114 */
3115 spin_lock_bh(&conn->psq_lock);
3116 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
3117 *more_data = true;
3118 spin_unlock_bh(&conn->psq_lock);
3119 }
3120
3121 return false;
3122}
3123
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003124/* Check if SSID length is greater than DIRECT- */
3125static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
3126{
3127 const struct ieee80211_mgmt *mgmt;
3128 mgmt = (const struct ieee80211_mgmt *) buf;
3129
3130 /* variable[1] contains the SSID tag length */
3131 if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
3132 (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
3133 return true;
3134 }
3135
3136 return false;
3137}
3138
Johannes Berg71bbc992012-06-15 15:30:18 +02003139static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003140 struct ieee80211_channel *chan, bool offchan,
Johannes Berg42d97a52012-11-08 18:31:02 +01003141 unsigned int wait, const u8 *buf, size_t len,
3142 bool no_cck, bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003143{
Johannes Berg71bbc992012-06-15 15:30:18 +02003144 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
3145 struct ath6kl *ar = ath6kl_priv(vif->ndev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003146 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003147 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003148 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003149
3150 mgmt = (const struct ieee80211_mgmt *) buf;
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003151 if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
3152 ieee80211_is_probe_resp(mgmt->frame_control) &&
3153 ath6kl_is_p2p_go_ssid(buf, len)) {
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003154 /*
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003155 * Send Probe Response frame in GO mode using a separate WMI
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003156 * command to allow the target to fill in the generic IEs.
3157 */
3158 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303159 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003160 chan->center_freq);
3161 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003162
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303163 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003164 if (id == 0) {
3165 /*
3166 * 0 is a reserved value in the WMI command and shall not be
3167 * used for the command.
3168 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303169 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003170 }
3171
3172 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003173
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003174 /* AP mode Power saving processing */
3175 if (vif->nw_type == AP_NETWORK) {
3176 queued = ath6kl_mgmt_powersave_ap(vif,
3177 id, chan->center_freq,
3178 wait, buf,
3179 len, &more_data, no_cck);
3180 if (queued)
3181 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003182 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003183
3184 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
3185 chan->center_freq, wait,
3186 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003187}
3188
Jouni Malinenae32c302011-08-30 21:58:01 +03003189static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
Johannes Berg71bbc992012-06-15 15:30:18 +02003190 struct wireless_dev *wdev,
Jouni Malinenae32c302011-08-30 21:58:01 +03003191 u16 frame_type, bool reg)
3192{
Johannes Berg71bbc992012-06-15 15:30:18 +02003193 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
Jouni Malinenae32c302011-08-30 21:58:01 +03003194
3195 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
3196 __func__, frame_type, reg);
3197 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
3198 /*
3199 * Note: This notification callback is not allowed to sleep, so
3200 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
3201 * hardcode target to report Probe Request frames all the time.
3202 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303203 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03003204 }
3205}
3206
Kalle Valo10509f92011-12-13 14:52:07 +02003207static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
3208 struct net_device *dev,
3209 struct cfg80211_sched_scan_request *request)
3210{
3211 struct ath6kl *ar = ath6kl_priv(dev);
3212 struct ath6kl_vif *vif = netdev_priv(dev);
3213 u16 interval;
3214 int ret;
Kalle Valo10509f92011-12-13 14:52:07 +02003215
3216 if (ar->state != ATH6KL_STATE_ON)
3217 return -EIO;
3218
3219 if (vif->sme_state != SME_DISCONNECTED)
3220 return -EBUSY;
3221
Thomas Pedersen77ed4e42012-04-19 16:29:19 -07003222 /* The FW currently can't support multi-vif WoW properly. */
3223 if (ar->num_vif > 1)
3224 return -EIO;
3225
Kalle Valob4d13d32012-03-21 10:01:09 +02003226 ath6kl_cfg80211_scan_complete_event(vif, true);
3227
Jouni Malinen3b8ffc62012-04-16 19:28:03 +03003228 ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
Naveen Singhdd45b752012-05-16 13:29:00 +03003229 request->n_ssids,
3230 request->match_sets,
3231 request->n_match_sets);
Jouni Malinen3b8ffc62012-04-16 19:28:03 +03003232 if (ret < 0)
3233 return ret;
Kalle Valo10509f92011-12-13 14:52:07 +02003234
Naveen Singhdd45b752012-05-16 13:29:00 +03003235 if (!request->n_match_sets) {
3236 ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
3237 ALL_BSS_FILTER, 0);
3238 if (ret < 0)
3239 return ret;
3240 } else {
3241 ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
3242 MATCHED_SSID_FILTER, 0);
3243 if (ret < 0)
3244 return ret;
3245 }
3246
Kalle Valo10509f92011-12-13 14:52:07 +02003247 /* fw uses seconds, also make sure that it's >0 */
3248 interval = max_t(u16, 1, request->interval / 1000);
3249
3250 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
3251 interval, interval,
Subramania Sharma Thandaveswarand472b5e2012-04-16 16:09:57 +05303252 vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
Kalle Valo10509f92011-12-13 14:52:07 +02003253
Kalle Valo10509f92011-12-13 14:52:07 +02003254 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
3255 ATH6KL_WOW_MODE_ENABLE,
3256 WOW_FILTER_SSID,
3257 WOW_HOST_REQ_DELAY);
3258 if (ret) {
3259 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
3260 return ret;
3261 }
3262
3263 /* this also clears IE in fw if it's not set */
3264 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
3265 WMI_FRAME_PROBE_REQ,
3266 request->ie, request->ie_len);
3267 if (ret) {
Joe Perchesf1ff32e2012-05-30 01:58:39 -07003268 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d\n",
Kalle Valo10509f92011-12-13 14:52:07 +02003269 ret);
3270 return ret;
3271 }
3272
3273 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
3274 ATH6KL_HOST_MODE_ASLEEP);
3275 if (ret) {
3276 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
3277 ret);
3278 return ret;
3279 }
3280
3281 ar->state = ATH6KL_STATE_SCHED_SCAN;
3282
3283 return ret;
3284}
3285
3286static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
3287 struct net_device *dev)
3288{
3289 struct ath6kl_vif *vif = netdev_priv(dev);
3290 bool stopped;
3291
3292 stopped = __ath6kl_cfg80211_sscan_stop(vif);
3293
3294 if (!stopped)
3295 return -EIO;
3296
3297 return 0;
3298}
3299
Bala Shanmugam06e360a2012-05-22 13:23:12 +05303300static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy,
3301 struct net_device *dev,
3302 const u8 *addr,
3303 const struct cfg80211_bitrate_mask *mask)
3304{
3305 struct ath6kl *ar = ath6kl_priv(dev);
3306 struct ath6kl_vif *vif = netdev_priv(dev);
3307
3308 return ath6kl_wmi_set_bitrate_mask(ar->wmi, vif->fw_vif_idx,
3309 mask);
3310}
3311
Jouni Malinenf80574a2011-08-30 21:58:04 +03003312static const struct ieee80211_txrx_stypes
3313ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
3314 [NL80211_IFTYPE_STATION] = {
3315 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3316 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3317 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3318 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3319 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02003320 [NL80211_IFTYPE_AP] = {
3321 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3322 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3323 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3324 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3325 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03003326 [NL80211_IFTYPE_P2P_CLIENT] = {
3327 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3328 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3329 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3330 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3331 },
3332 [NL80211_IFTYPE_P2P_GO] = {
3333 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3334 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3335 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3336 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3337 },
3338};
3339
Kalle Valobdcd8172011-07-18 00:22:30 +03003340static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303341 .add_virtual_intf = ath6kl_cfg80211_add_iface,
3342 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03003343 .change_virtual_intf = ath6kl_cfg80211_change_iface,
3344 .scan = ath6kl_cfg80211_scan,
3345 .connect = ath6kl_cfg80211_connect,
3346 .disconnect = ath6kl_cfg80211_disconnect,
3347 .add_key = ath6kl_cfg80211_add_key,
3348 .get_key = ath6kl_cfg80211_get_key,
3349 .del_key = ath6kl_cfg80211_del_key,
3350 .set_default_key = ath6kl_cfg80211_set_default_key,
3351 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
3352 .set_tx_power = ath6kl_cfg80211_set_txpower,
3353 .get_tx_power = ath6kl_cfg80211_get_txpower,
3354 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
3355 .join_ibss = ath6kl_cfg80211_join_ibss,
3356 .leave_ibss = ath6kl_cfg80211_leave_ibss,
3357 .get_station = ath6kl_get_station,
3358 .set_pmksa = ath6kl_set_pmksa,
3359 .del_pmksa = ath6kl_del_pmksa,
3360 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03003361 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03003362#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02003363 .suspend = __ath6kl_cfg80211_suspend,
3364 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03003365#endif
Johannes Berg88600202012-02-13 15:17:18 +01003366 .start_ap = ath6kl_start_ap,
3367 .change_beacon = ath6kl_change_beacon,
3368 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02003369 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03003370 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003371 .remain_on_channel = ath6kl_remain_on_channel,
3372 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003373 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03003374 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02003375 .sched_scan_start = ath6kl_cfg80211_sscan_start,
3376 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Bala Shanmugam06e360a2012-05-22 13:23:12 +05303377 .set_bitrate_mask = ath6kl_cfg80211_set_bitrate,
Kalle Valobdcd8172011-07-18 00:22:30 +03003378};
3379
Kalle Valo7125f012011-12-13 14:51:37 +02003380void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02003381{
Kalle Valo10509f92011-12-13 14:52:07 +02003382 ath6kl_cfg80211_sscan_disable(vif);
3383
Kalle Valoec4b7f62011-11-01 08:44:04 +02003384 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02003385 case SME_DISCONNECTED:
3386 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02003387 case SME_CONNECTING:
3388 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
3389 NULL, 0,
3390 WLAN_STATUS_UNSPECIFIED_FAILURE,
3391 GFP_KERNEL);
3392 break;
3393 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02003394 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
3395 break;
3396 }
3397
3398 if (test_bit(CONNECTED, &vif->flags) ||
3399 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02003400 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003401
3402 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02003403 clear_bit(CONNECTED, &vif->flags);
3404 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003405
3406 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02003407 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
3408 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
3409 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003410
3411 ath6kl_cfg80211_scan_complete_event(vif, true);
3412}
3413
Kalle Valo7125f012011-12-13 14:51:37 +02003414void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3415{
3416 struct ath6kl_vif *vif;
3417
3418 vif = ath6kl_vif_first(ar);
3419 if (!vif) {
3420 /* save the current power mode before enabling power save */
3421 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3422
3423 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
Kalle Valocdeb8602012-04-12 11:02:18 +03003424 ath6kl_warn("ath6kl_deep_sleep_enable: wmi_powermode_cmd failed\n");
Kalle Valo7125f012011-12-13 14:51:37 +02003425 return;
3426 }
3427
3428 /*
3429 * FIXME: we should take ar->list_lock to protect changes in the
3430 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3431 * sleeps.
3432 */
3433 list_for_each_entry(vif, &ar->vif_list, list)
3434 ath6kl_cfg80211_stop(vif);
3435}
3436
Kalle Valoc25889e2012-01-17 20:08:27 +02003437static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003438{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303439 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303440 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303441 ath6kl_err("failed to initialize aggr\n");
3442 return -ENOMEM;
3443 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003444
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303445 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303446 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003447 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3448 (unsigned long) vif);
3449
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303450 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303451 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303452
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303453 INIT_LIST_HEAD(&vif->mc_filter);
3454
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303455 return 0;
3456}
3457
Kalle Valoc25889e2012-01-17 20:08:27 +02003458void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303459{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303460 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303461 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303462
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303463 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303464
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303465 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3466
3467 if (vif->nw_type == ADHOC_NETWORK)
3468 ar->ibss_if_active = false;
3469
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303470 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3471 list_del(&mc_filter->list);
3472 kfree(mc_filter);
3473 }
3474
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303475 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303476
3477 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303478}
3479
Johannes Berg552bff02012-09-19 09:26:06 +02003480struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
Johannes Berg84efbb82012-06-16 00:00:26 +02003481 enum nl80211_iftype type,
3482 u8 fw_vif_idx, u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303483{
3484 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303485 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303486
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303487 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303488 if (!ndev)
3489 return NULL;
3490
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303491 vif = netdev_priv(ndev);
3492 ndev->ieee80211_ptr = &vif->wdev;
3493 vif->wdev.wiphy = ar->wiphy;
3494 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303495 vif->ndev = ndev;
3496 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3497 vif->wdev.netdev = ndev;
3498 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303499 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003500 vif->nw_type = nw_type;
3501 vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303502 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303503 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Raja Manieb389872012-04-16 16:09:56 +05303504 vif->bg_scan_period = 0;
Kiran Reddy67b3f122012-05-29 11:12:50 -07003505 vif->htcap[IEEE80211_BAND_2GHZ].ht_enable = true;
3506 vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303507
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303508 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
3509 if (fw_vif_idx != 0)
3510 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3511 0x2;
3512
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303513 init_netdev(ndev);
3514
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303515 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303516
Kalle Valoc25889e2012-01-17 20:08:27 +02003517 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303518 goto err;
3519
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303520 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303521 goto err;
3522
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303523 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303524 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303525 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303526 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303527 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303528
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303529 if (type == NL80211_IFTYPE_ADHOC)
3530 ar->ibss_if_active = true;
3531
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303532 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303533 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303534 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303535
Johannes Berg84efbb82012-06-16 00:00:26 +02003536 return &vif->wdev;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303537
3538err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303539 aggr_module_destroy(vif->aggr_cntxt);
3540 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303541 return NULL;
3542}
3543
Kalle Valo46d33a22012-01-17 20:08:40 +02003544int ath6kl_cfg80211_init(struct ath6kl *ar)
3545{
3546 struct wiphy *wiphy = ar->wiphy;
Thomas Pedersend92917e2012-04-19 15:31:56 -07003547 bool band_2gig = false, band_5gig = false, ht = false;
Kalle Valo46d33a22012-01-17 20:08:40 +02003548 int ret;
3549
3550 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3551
3552 wiphy->max_remain_on_channel_duration = 5000;
3553
3554 /* set device pointer for wiphy */
3555 set_wiphy_dev(wiphy, ar->dev);
3556
3557 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3558 BIT(NL80211_IFTYPE_ADHOC) |
3559 BIT(NL80211_IFTYPE_AP);
3560 if (ar->p2p) {
3561 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3562 BIT(NL80211_IFTYPE_P2P_CLIENT);
3563 }
3564
3565 /* max num of ssids that can be probed during scanning */
Jouni Malinen8ab54152012-05-09 22:14:51 +03003566 wiphy->max_scan_ssids = MAX_PROBED_SSIDS;
Naveen Singhdd45b752012-05-16 13:29:00 +03003567
3568 /* max num of ssids that can be matched after scan */
3569 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST,
3570 ar->fw_capabilities))
3571 wiphy->max_match_sets = MAX_PROBED_SSIDS;
3572
Kalle Valo46d33a22012-01-17 20:08:40 +02003573 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
Thomas Pedersend92917e2012-04-19 15:31:56 -07003574 switch (ar->hw.cap) {
3575 case WMI_11AN_CAP:
3576 ht = true;
3577 case WMI_11A_CAP:
3578 band_5gig = true;
3579 break;
3580 case WMI_11GN_CAP:
3581 ht = true;
3582 case WMI_11G_CAP:
3583 band_2gig = true;
3584 break;
3585 case WMI_11AGN_CAP:
3586 ht = true;
3587 case WMI_11AG_CAP:
3588 band_2gig = true;
3589 band_5gig = true;
3590 break;
3591 default:
3592 ath6kl_err("invalid phy capability!\n");
3593 return -EINVAL;
3594 }
3595
Vasanthakumar Thiagarajan7fd1ce72012-04-25 12:38:18 +05303596 /*
3597 * Even if the fw has HT support, advertise HT cap only when
3598 * the firmware has support to override RSN capability, otherwise
3599 * 4-way handshake would fail.
3600 */
3601 if (!(ht &&
3602 test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
3603 ar->fw_capabilities))) {
Thomas Pedersend92917e2012-04-19 15:31:56 -07003604 ath6kl_band_2ghz.ht_cap.cap = 0;
3605 ath6kl_band_2ghz.ht_cap.ht_supported = false;
3606 ath6kl_band_5ghz.ht_cap.cap = 0;
3607 ath6kl_band_5ghz.ht_cap.ht_supported = false;
3608 }
Bala Shanmugam06e360a2012-05-22 13:23:12 +05303609
3610 if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) {
3611 ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3612 ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3613 ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
3614 ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff;
3615 } else {
3616 ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3617 ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3618 }
3619
Thomas Pedersend92917e2012-04-19 15:31:56 -07003620 if (band_2gig)
3621 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3622 if (band_5gig)
3623 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3624
Kalle Valo46d33a22012-01-17 20:08:40 +02003625 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3626
3627 wiphy->cipher_suites = cipher_suites;
3628 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3629
Johannes Bergdfb89c52012-06-27 09:23:48 +02003630#ifdef CONFIG_PM
Kalle Valo46d33a22012-01-17 20:08:40 +02003631 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3632 WIPHY_WOWLAN_DISCONNECT |
3633 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3634 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3635 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3636 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3637 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3638 wiphy->wowlan.pattern_min_len = 1;
3639 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
Johannes Bergdfb89c52012-06-27 09:23:48 +02003640#endif
Kalle Valo46d33a22012-01-17 20:08:40 +02003641
Jouni Malinen8ab54152012-05-09 22:14:51 +03003642 wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS;
Kalle Valo46d33a22012-01-17 20:08:40 +02003643
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303644 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3645 WIPHY_FLAG_HAVE_AP_SME |
3646 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3647 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3648
3649 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3650 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3651
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303652 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
3653 ar->fw_capabilities))
Johannes Bergb2922192012-10-12 10:55:53 +02003654 ar->wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303655
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303656 ar->wiphy->probe_resp_offload =
3657 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3658 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
Jouni Malinena432e7c2012-04-16 19:25:35 +03003659 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303660
Kalle Valo46d33a22012-01-17 20:08:40 +02003661 ret = wiphy_register(wiphy);
3662 if (ret < 0) {
3663 ath6kl_err("couldn't register wiphy device\n");
3664 return ret;
3665 }
3666
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303667 ar->wiphy_registered = true;
3668
Kalle Valo46d33a22012-01-17 20:08:40 +02003669 return 0;
3670}
3671
3672void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303673{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303674 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303675
3676 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003677}
Kalle Valo46d33a22012-01-17 20:08:40 +02003678
Kalle Valo45eaa782012-01-17 20:09:05 +02003679struct ath6kl *ath6kl_cfg80211_create(void)
3680{
3681 struct ath6kl *ar;
3682 struct wiphy *wiphy;
3683
3684 /* create a new wiphy for use with cfg80211 */
3685 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3686
3687 if (!wiphy) {
3688 ath6kl_err("couldn't allocate wiphy device\n");
3689 return NULL;
3690 }
3691
3692 ar = wiphy_priv(wiphy);
3693 ar->wiphy = wiphy;
3694
3695 return ar;
3696}
3697
3698/* Note: ar variable must not be accessed after calling this! */
3699void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3700{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303701 int i;
3702
3703 for (i = 0; i < AP_MAX_NUM_STA; i++)
3704 kfree(ar->sta_list[i].aggr_conn);
3705
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303706 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003707}
Kalle Valo45eaa782012-01-17 20:09:05 +02003708