blob: 75ddfafb11b77d0912564a50711e0460a30a219f [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
Kalle Valo11f0bfc2012-07-19 16:00:48 +03001034 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
1035 WMI_LONG_SCAN, force_fg_scan,
1036 false, 0,
1037 ATH6KL_FG_SCAN_INTERVAL,
1038 n_channels, channels,
1039 request->no_cck,
1040 request->rates);
Raja Mani5b35dff2012-03-28 18:50:35 +05301041 if (ret) {
Kalle Valo11f0bfc2012-07-19 16:00:48 +03001042 ath6kl_err("failed to start scan: %d\n", ret);
Raja Mani5b35dff2012-03-28 18:50:35 +05301043 vif->scan_req = NULL;
1044 }
Kalle Valobdcd8172011-07-18 00:22:30 +03001045
Edward Lu1276c9e2011-08-30 21:58:00 +03001046 kfree(channels);
1047
Kalle Valobdcd8172011-07-18 00:22:30 +03001048 return ret;
1049}
1050
Kalle Valo1c17d312011-11-01 08:43:56 +02001051void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +03001052{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301053 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001054 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +03001055
Kalle Valo1c17d312011-11-01 08:43:56 +02001056 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
1057 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +03001058
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301059 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001060 return;
Kalle Valobdcd8172011-07-18 00:22:30 +03001061
Kalle Valo1c17d312011-11-01 08:43:56 +02001062 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001063 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001064
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301065 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
1066 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301067 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
1068 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001069 0, NULL);
1070 }
1071 }
1072
1073out:
Kalle Valocb938212011-10-27 18:47:46 +03001074 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301075 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001076}
1077
Thomas Pedersenc4f78632012-04-06 13:35:48 -07001078void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
1079 enum wmi_phy_mode mode)
1080{
1081 enum nl80211_channel_type type;
1082
1083 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1084 "channel switch notify nw_type %d freq %d mode %d\n",
1085 vif->nw_type, freq, mode);
1086
1087 type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT;
1088
1089 cfg80211_ch_switch_notify(vif->ndev, freq, type);
1090}
1091
Kalle Valobdcd8172011-07-18 00:22:30 +03001092static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1093 u8 key_index, bool pairwise,
1094 const u8 *mac_addr,
1095 struct key_params *params)
1096{
Kalle Valod6d5c062011-11-25 13:17:37 +02001097 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301098 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001099 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301100 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001101 u8 key_usage;
1102 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001103
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301104 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001105 return -EIO;
1106
Jouni Malinen837cb972011-10-11 17:31:57 +03001107 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
1108 if (params->key_len != WMI_KRK_LEN)
1109 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301110 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
1111 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +03001112 }
1113
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301114 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001115 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1116 "%s: key index %d out of bounds\n", __func__,
1117 key_index);
1118 return -ENOENT;
1119 }
1120
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301121 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001122 memset(key, 0, sizeof(struct ath6kl_key));
1123
1124 if (pairwise)
1125 key_usage = PAIRWISE_USAGE;
1126 else
1127 key_usage = GROUP_USAGE;
1128
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301129 seq_len = params->seq_len;
1130 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1131 seq_len > ATH6KL_KEY_SEQ_LEN) {
1132 /* Only first half of the WPI PN is configured */
1133 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001134 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301135 if (params->key_len > WLAN_MAX_KEY_LEN ||
1136 seq_len > sizeof(key->seq))
1137 return -EINVAL;
1138
1139 key->key_len = params->key_len;
1140 memcpy(key->key, params->key, key->key_len);
1141 key->seq_len = seq_len;
1142 memcpy(key->seq, params->seq, key->seq_len);
1143 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001144
1145 switch (key->cipher) {
1146 case WLAN_CIPHER_SUITE_WEP40:
1147 case WLAN_CIPHER_SUITE_WEP104:
1148 key_type = WEP_CRYPT;
1149 break;
1150
1151 case WLAN_CIPHER_SUITE_TKIP:
1152 key_type = TKIP_CRYPT;
1153 break;
1154
1155 case WLAN_CIPHER_SUITE_CCMP:
1156 key_type = AES_CRYPT;
1157 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001158 case WLAN_CIPHER_SUITE_SMS4:
1159 key_type = WAPI_CRYPT;
1160 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001161
1162 default:
1163 return -ENOTSUPP;
1164 }
1165
Kalle Valoddc3d772012-03-07 20:03:58 +02001166 if (((vif->auth_mode == WPA_PSK_AUTH) ||
1167 (vif->auth_mode == WPA2_PSK_AUTH)) &&
1168 (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301169 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001170
1171 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1172 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1173 __func__, key_index, key->key_len, key_type,
1174 key_usage, key->seq_len);
1175
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301176 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001177 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
Vasanthakumar Thiagarajancc4d6232012-02-14 20:33:00 +05301178 key_type == WAPI_CRYPT)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001179 ar->ap_mode_bkey.valid = true;
1180 ar->ap_mode_bkey.key_index = key_index;
1181 ar->ap_mode_bkey.key_type = key_type;
1182 ar->ap_mode_bkey.key_len = key->key_len;
1183 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301184 if (!test_bit(CONNECTED, &vif->flags)) {
Kalle Valocdeb8602012-04-12 11:02:18 +03001185 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1186 "Delay initial group key configuration until AP mode has been started\n");
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001187 /*
1188 * The key will be set in ath6kl_connect_ap_mode() once
1189 * the connected event is received from the target.
1190 */
1191 return 0;
1192 }
1193 }
1194
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301195 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301196 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001197 /*
1198 * Store the key locally so that it can be re-configured after
1199 * the AP mode has properly started
1200 * (ath6kl_install_statioc_wep_keys).
1201 */
Kalle Valocdeb8602012-04-12 11:02:18 +03001202 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1203 "Delay WEP key configuration until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301204 vif->wep_key_list[key_index].key_len = key->key_len;
1205 memcpy(vif->wep_key_list[key_index].key, key->key,
1206 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001207 return 0;
1208 }
1209
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301210 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001211 key_type, key_usage, key->key_len,
1212 key->seq, key->seq_len, key->key,
1213 KEY_OP_INIT_VAL,
1214 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001215}
1216
1217static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1218 u8 key_index, bool pairwise,
1219 const u8 *mac_addr)
1220{
Kalle Valod6d5c062011-11-25 13:17:37 +02001221 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301222 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001223
1224 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1225
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301226 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001227 return -EIO;
1228
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301229 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001230 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1231 "%s: key index %d out of bounds\n", __func__,
1232 key_index);
1233 return -ENOENT;
1234 }
1235
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301236 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001237 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1238 "%s: index %d is empty\n", __func__, key_index);
1239 return 0;
1240 }
1241
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301242 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001243
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301244 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001245}
1246
1247static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1248 u8 key_index, bool pairwise,
1249 const u8 *mac_addr, void *cookie,
1250 void (*callback) (void *cookie,
1251 struct key_params *))
1252{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301253 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001254 struct ath6kl_key *key = NULL;
1255 struct key_params params;
1256
1257 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1258
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301259 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001260 return -EIO;
1261
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301262 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001263 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1264 "%s: key index %d out of bounds\n", __func__,
1265 key_index);
1266 return -ENOENT;
1267 }
1268
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301269 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001270 memset(&params, 0, sizeof(params));
1271 params.cipher = key->cipher;
1272 params.key_len = key->key_len;
1273 params.seq_len = key->seq_len;
1274 params.seq = key->seq;
1275 params.key = key->key;
1276
1277 callback(cookie, &params);
1278
1279 return key->key_len ? 0 : -ENOENT;
1280}
1281
1282static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1283 struct net_device *ndev,
1284 u8 key_index, bool unicast,
1285 bool multicast)
1286{
Kalle Valod6d5c062011-11-25 13:17:37 +02001287 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301288 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001289 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001290 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001291 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001292
1293 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1294
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301295 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001296 return -EIO;
1297
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301298 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001299 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1300 "%s: key index %d out of bounds\n",
1301 __func__, key_index);
1302 return -ENOENT;
1303 }
1304
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301305 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001306 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1307 __func__, key_index);
1308 return -EINVAL;
1309 }
1310
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301311 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301312 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001313 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301314 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001315 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001316 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301317 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001318 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301319 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001320
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301321 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001322 return 0; /* Delay until AP mode has been started */
1323
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001324 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1325 vif->def_txkey_index,
1326 key_type, key_usage,
1327 key->key_len, key->seq, key->seq_len,
1328 key->key,
1329 KEY_OP_INIT_VAL, NULL,
1330 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001331}
1332
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301333void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001334 bool ismcast)
1335{
1336 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1337 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1338
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301339 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001340 (ismcast ? NL80211_KEYTYPE_GROUP :
1341 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1342 GFP_KERNEL);
1343}
1344
1345static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1346{
1347 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301348 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001349 int ret;
1350
1351 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1352 changed);
1353
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301354 vif = ath6kl_vif_first(ar);
1355 if (!vif)
1356 return -EIO;
1357
1358 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001359 return -EIO;
1360
1361 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1362 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1363 if (ret != 0) {
1364 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1365 return -EIO;
1366 }
1367 }
1368
1369 return 0;
1370}
1371
1372/*
1373 * The type nl80211_tx_power_setting replaces the following
1374 * data type from 2.6.36 onwards
1375*/
1376static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1377 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001378 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001379{
1380 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301381 struct ath6kl_vif *vif;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001382 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001383
1384 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1385 type, dbm);
1386
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301387 vif = ath6kl_vif_first(ar);
1388 if (!vif)
1389 return -EIO;
1390
1391 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001392 return -EIO;
1393
1394 switch (type) {
1395 case NL80211_TX_POWER_AUTOMATIC:
1396 return 0;
1397 case NL80211_TX_POWER_LIMITED:
Kalle Valod0d670a2012-03-07 20:03:58 +02001398 ar->tx_pwr = dbm;
Kalle Valobdcd8172011-07-18 00:22:30 +03001399 break;
1400 default:
1401 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1402 __func__, type);
1403 return -EOPNOTSUPP;
1404 }
1405
Kalle Valod0d670a2012-03-07 20:03:58 +02001406 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001407
1408 return 0;
1409}
1410
1411static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1412{
1413 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301414 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001415
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301416 vif = ath6kl_vif_first(ar);
1417 if (!vif)
1418 return -EIO;
1419
1420 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001421 return -EIO;
1422
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301423 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001424 ar->tx_pwr = 0;
1425
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301426 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001427 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1428 return -EIO;
1429 }
1430
1431 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1432 5 * HZ);
1433
1434 if (signal_pending(current)) {
1435 ath6kl_err("target did not respond\n");
1436 return -EINTR;
1437 }
1438 }
1439
1440 *dbm = ar->tx_pwr;
1441 return 0;
1442}
1443
1444static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1445 struct net_device *dev,
1446 bool pmgmt, int timeout)
1447{
1448 struct ath6kl *ar = ath6kl_priv(dev);
1449 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301450 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001451
1452 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1453 __func__, pmgmt, timeout);
1454
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301455 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001456 return -EIO;
1457
1458 if (pmgmt) {
1459 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1460 mode.pwr_mode = REC_POWER;
1461 } else {
1462 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1463 mode.pwr_mode = MAX_PERF_POWER;
1464 }
1465
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301466 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001467 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001468 ath6kl_err("wmi_powermode_cmd failed\n");
1469 return -EIO;
1470 }
1471
1472 return 0;
1473}
1474
Johannes Berg84efbb82012-06-16 00:00:26 +02001475static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
Johannes Berg552bff02012-09-19 09:26:06 +02001476 const char *name,
Johannes Berg84efbb82012-06-16 00:00:26 +02001477 enum nl80211_iftype type,
1478 u32 *flags,
1479 struct vif_params *params)
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301480{
1481 struct ath6kl *ar = wiphy_priv(wiphy);
Johannes Berg84efbb82012-06-16 00:00:26 +02001482 struct wireless_dev *wdev;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301483 u8 if_idx, nw_type;
1484
Kalle Valo71f96ee2011-11-14 19:31:30 +02001485 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301486 ath6kl_err("Reached maximum number of supported vif\n");
1487 return ERR_PTR(-EINVAL);
1488 }
1489
1490 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1491 ath6kl_err("Not a supported interface type\n");
1492 return ERR_PTR(-EINVAL);
1493 }
1494
Johannes Berg84efbb82012-06-16 00:00:26 +02001495 wdev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1496 if (!wdev)
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301497 return ERR_PTR(-ENOMEM);
1498
1499 ar->num_vif++;
1500
Johannes Berg84efbb82012-06-16 00:00:26 +02001501 return wdev;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301502}
1503
1504static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
Johannes Berg84efbb82012-06-16 00:00:26 +02001505 struct wireless_dev *wdev)
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301506{
1507 struct ath6kl *ar = wiphy_priv(wiphy);
Johannes Berg84efbb82012-06-16 00:00:26 +02001508 struct ath6kl_vif *vif = netdev_priv(wdev->netdev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301509
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301510 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301511 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301512 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301513
1514 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1515
Kalle Valoc25889e2012-01-17 20:08:27 +02001516 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301517
1518 return 0;
1519}
1520
Kalle Valobdcd8172011-07-18 00:22:30 +03001521static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1522 struct net_device *ndev,
1523 enum nl80211_iftype type, u32 *flags,
1524 struct vif_params *params)
1525{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301526 struct ath6kl_vif *vif = netdev_priv(ndev);
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301527 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +03001528
1529 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1530
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301531 /*
1532 * Don't bring up p2p on an interface which is not initialized
1533 * for p2p operation where fw does not have capability to switch
1534 * dynamically between non-p2p and p2p type interface.
1535 */
1536 if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
1537 vif->ar->fw_capabilities) &&
1538 (type == NL80211_IFTYPE_P2P_CLIENT ||
1539 type == NL80211_IFTYPE_P2P_GO)) {
1540 if (vif->ar->vif_max == 1) {
1541 if (vif->fw_vif_idx != 0)
1542 return -EINVAL;
1543 else
1544 goto set_iface_type;
1545 }
1546
1547 for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) {
1548 if (i == vif->fw_vif_idx)
1549 break;
1550 }
1551
1552 if (i == vif->ar->vif_max) {
1553 ath6kl_err("Invalid interface to bring up P2P\n");
1554 return -EINVAL;
1555 }
1556 }
1557
Thomas Pedersenc422d52d2012-05-15 00:09:23 -07001558 /* need to clean up enhanced bmiss detection fw state */
1559 ath6kl_cfg80211_sta_bmiss_enhance(vif, false);
1560
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301561set_iface_type:
Kalle Valobdcd8172011-07-18 00:22:30 +03001562 switch (type) {
1563 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301564 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001565 break;
1566 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301567 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001568 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001569 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301570 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001571 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001572 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301573 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001574 break;
1575 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301576 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001577 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001578 default:
1579 ath6kl_err("invalid interface type %u\n", type);
1580 return -EOPNOTSUPP;
1581 }
1582
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301583 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001584
1585 return 0;
1586}
1587
1588static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1589 struct net_device *dev,
1590 struct cfg80211_ibss_params *ibss_param)
1591{
1592 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301593 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001594 int status;
1595
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301596 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001597 return -EIO;
1598
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301599 vif->ssid_len = ibss_param->ssid_len;
1600 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001601
1602 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301603 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001604
1605 if (ibss_param->channel_fixed) {
1606 /*
1607 * TODO: channel_fixed: The channel should be fixed, do not
1608 * search for IBSSs to join on other channels. Target
1609 * firmware does not support this feature, needs to be
1610 * updated.
1611 */
1612 return -EOPNOTSUPP;
1613 }
1614
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301615 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001616 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301617 memcpy(vif->req_bssid, ibss_param->bssid,
1618 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001619
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301620 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001621
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301622 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001623 if (status)
1624 return status;
1625
1626 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301627 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1628 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001629 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301630 ath6kl_set_cipher(vif, 0, true);
1631 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001632 }
1633
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301634 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001635
1636 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1637 "%s: connect called with authmode %d dot11 auth %d"
1638 " PW crypto %d PW crypto len %d GRP crypto %d"
1639 " GRP crypto len %d channel hint %u\n",
1640 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301641 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1642 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301643 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001644
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301645 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301646 vif->dot11_auth_mode, vif->auth_mode,
1647 vif->prwise_crypto,
1648 vif->prwise_crypto_len,
1649 vif->grp_crypto, vif->grp_crypto_len,
1650 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301651 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001652 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301653 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001654
1655 return 0;
1656}
1657
1658static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1659 struct net_device *dev)
1660{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301661 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001662
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301663 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001664 return -EIO;
1665
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301666 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301667 memset(vif->ssid, 0, sizeof(vif->ssid));
1668 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001669
1670 return 0;
1671}
1672
1673static const u32 cipher_suites[] = {
1674 WLAN_CIPHER_SUITE_WEP40,
1675 WLAN_CIPHER_SUITE_WEP104,
1676 WLAN_CIPHER_SUITE_TKIP,
1677 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001678 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001679 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001680};
1681
1682static bool is_rate_legacy(s32 rate)
1683{
1684 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1685 6000, 9000, 12000, 18000, 24000,
1686 36000, 48000, 54000
1687 };
1688 u8 i;
1689
1690 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1691 if (rate == legacy[i])
1692 return true;
1693
1694 return false;
1695}
1696
1697static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1698{
1699 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1700 52000, 58500, 65000, 72200
1701 };
1702 u8 i;
1703
1704 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1705 if (rate == ht20[i]) {
1706 if (i == ARRAY_SIZE(ht20) - 1)
1707 /* last rate uses sgi */
1708 *sgi = true;
1709 else
1710 *sgi = false;
1711
1712 *mcs = i;
1713 return true;
1714 }
1715 }
1716 return false;
1717}
1718
1719static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1720{
1721 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1722 81000, 108000, 121500, 135000,
1723 150000
1724 };
1725 u8 i;
1726
1727 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1728 if (rate == ht40[i]) {
1729 if (i == ARRAY_SIZE(ht40) - 1)
1730 /* last rate uses sgi */
1731 *sgi = true;
1732 else
1733 *sgi = false;
1734
1735 *mcs = i;
1736 return true;
1737 }
1738 }
1739
1740 return false;
1741}
1742
1743static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1744 u8 *mac, struct station_info *sinfo)
1745{
1746 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301747 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001748 long left;
1749 bool sgi;
1750 s32 rate;
1751 int ret;
1752 u8 mcs;
1753
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301754 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001755 return -ENOENT;
1756
1757 if (down_interruptible(&ar->sem))
1758 return -EBUSY;
1759
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301760 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001761
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301762 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001763
1764 if (ret != 0) {
1765 up(&ar->sem);
1766 return -EIO;
1767 }
1768
1769 left = wait_event_interruptible_timeout(ar->event_wq,
1770 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301771 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001772 WMI_TIMEOUT);
1773
1774 up(&ar->sem);
1775
1776 if (left == 0)
1777 return -ETIMEDOUT;
1778 else if (left < 0)
1779 return left;
1780
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301781 if (vif->target_stats.rx_byte) {
1782 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001783 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301784 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001785 sinfo->filled |= STATION_INFO_RX_PACKETS;
1786 }
1787
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301788 if (vif->target_stats.tx_byte) {
1789 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001790 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301791 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001792 sinfo->filled |= STATION_INFO_TX_PACKETS;
1793 }
1794
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301795 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001796 sinfo->filled |= STATION_INFO_SIGNAL;
1797
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301798 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001799
1800 if (is_rate_legacy(rate)) {
1801 sinfo->txrate.legacy = rate / 100;
1802 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1803 if (sgi) {
1804 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1805 sinfo->txrate.mcs = mcs - 1;
1806 } else {
1807 sinfo->txrate.mcs = mcs;
1808 }
1809
1810 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1811 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1812 if (sgi) {
1813 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1814 sinfo->txrate.mcs = mcs - 1;
1815 } else {
1816 sinfo->txrate.mcs = mcs;
1817 }
1818
1819 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1820 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1821 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001822 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1823 "invalid rate from stats: %d\n", rate);
1824 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001825 return 0;
1826 }
1827
1828 sinfo->filled |= STATION_INFO_TX_BITRATE;
1829
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301830 if (test_bit(CONNECTED, &vif->flags) &&
1831 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301832 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001833 sinfo->filled |= STATION_INFO_BSS_PARAM;
1834 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301835 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1836 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001837 }
1838
Kalle Valobdcd8172011-07-18 00:22:30 +03001839 return 0;
1840}
1841
1842static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1843 struct cfg80211_pmksa *pmksa)
1844{
1845 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301846 struct ath6kl_vif *vif = netdev_priv(netdev);
1847
1848 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001849 pmksa->pmkid, true);
1850}
1851
1852static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1853 struct cfg80211_pmksa *pmksa)
1854{
1855 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301856 struct ath6kl_vif *vif = netdev_priv(netdev);
1857
1858 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001859 pmksa->pmkid, false);
1860}
1861
1862static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1863{
1864 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301865 struct ath6kl_vif *vif = netdev_priv(netdev);
1866
1867 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301868 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1869 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001870 return 0;
1871}
1872
Raja Manid91e8ee2012-01-30 17:13:10 +05301873static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1874 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001875{
Raja Manid91e8ee2012-01-30 17:13:10 +05301876 int ret, pos;
1877 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001878 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001879
Raja Manid91e8ee2012-01-30 17:13:10 +05301880 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001881 for (i = 0; i < wow->n_patterns; i++) {
1882
1883 /*
1884 * Convert given nl80211 specific mask value to equivalent
1885 * driver specific mask value and send it to the chip along
1886 * with patterns. For example, If the mask value defined in
1887 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1888 * then equivalent driver specific mask value is
1889 * "0xFF 0x00 0xFF 0x00".
1890 */
1891 memset(&mask, 0, sizeof(mask));
1892 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1893 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1894 mask[pos] = 0xFF;
1895 }
1896 /*
1897 * Note: Pattern's offset is not passed as part of wowlan
1898 * parameter from CFG layer. So it's always passed as ZERO
1899 * to the firmware. It means, given WOW patterns are always
1900 * matched from the first byte of received pkt in the firmware.
1901 */
1902 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301903 vif->fw_vif_idx, WOW_LIST_ID,
1904 wow->patterns[i].pattern_len,
1905 0 /* pattern offset */,
1906 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001907 if (ret)
1908 return ret;
1909 }
1910
Raja Manid91e8ee2012-01-30 17:13:10 +05301911 if (wow->disconnect)
1912 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1913
1914 if (wow->magic_pkt)
1915 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1916
1917 if (wow->gtk_rekey_failure)
1918 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1919
1920 if (wow->eap_identity_req)
1921 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1922
1923 if (wow->four_way_handshake)
1924 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1925
1926 return 0;
1927}
1928
1929static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1930{
1931 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1934 0x00, 0x08 };
1935 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1938 0x00, 0x7f };
1939 u8 unicst_offset = 0;
1940 static const u8 arp_pattern[] = { 0x08, 0x06 };
1941 static const u8 arp_mask[] = { 0xff, 0xff };
1942 u8 arp_offset = 20;
1943 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1944 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1945 u8 discvr_offset = 38;
1946 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1947 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1951 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1952 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1953 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1954 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1955 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1956 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1957 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1958 u8 dhcp_offset = 0;
1959 int ret;
1960
1961 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1962 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1963 vif->fw_vif_idx, WOW_LIST_ID,
1964 sizeof(unicst_pattern), unicst_offset,
1965 unicst_pattern, unicst_mask);
1966 if (ret) {
1967 ath6kl_err("failed to add WOW unicast IP pattern\n");
1968 return ret;
1969 }
1970
1971 /* Setup all ARP pkt pattern */
1972 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1973 vif->fw_vif_idx, WOW_LIST_ID,
1974 sizeof(arp_pattern), arp_offset,
1975 arp_pattern, arp_mask);
1976 if (ret) {
1977 ath6kl_err("failed to add WOW ARP pattern\n");
1978 return ret;
1979 }
1980
1981 /*
1982 * Setup multicast pattern for mDNS 224.0.0.251,
1983 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1984 */
1985 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1986 vif->fw_vif_idx, WOW_LIST_ID,
1987 sizeof(discvr_pattern), discvr_offset,
1988 discvr_pattern, discvr_mask);
1989 if (ret) {
1990 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1991 return ret;
1992 }
1993
1994 /* Setup all DHCP broadcast pkt pattern */
1995 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1996 vif->fw_vif_idx, WOW_LIST_ID,
1997 sizeof(dhcp_pattern), dhcp_offset,
1998 dhcp_pattern, dhcp_mask);
1999 if (ret) {
2000 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
2001 return ret;
2002 }
2003
2004 return 0;
2005}
2006
2007static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
2008{
2009 struct net_device *ndev = vif->ndev;
2010 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
2011 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
2012 u8 discvr_offset = 38;
2013 u8 mac_mask[ETH_ALEN];
2014 int ret;
2015
2016 /* Setup unicast pkt pattern */
2017 memset(mac_mask, 0xff, ETH_ALEN);
2018 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
2019 vif->fw_vif_idx, WOW_LIST_ID,
2020 ETH_ALEN, 0, ndev->dev_addr,
2021 mac_mask);
2022 if (ret) {
2023 ath6kl_err("failed to add WOW unicast pattern\n");
2024 return ret;
2025 }
2026
2027 /*
2028 * Setup multicast pattern for mDNS 224.0.0.251,
2029 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
2030 */
2031 if ((ndev->flags & IFF_ALLMULTI) ||
2032 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
2033 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
2034 vif->fw_vif_idx, WOW_LIST_ID,
2035 sizeof(discvr_pattern), discvr_offset,
2036 discvr_pattern, discvr_mask);
2037 if (ret) {
Kalle Valocdeb8602012-04-12 11:02:18 +03002038 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
Raja Manid91e8ee2012-01-30 17:13:10 +05302039 return ret;
2040 }
2041 }
2042
2043 return 0;
2044}
2045
Raja Mani055bde42012-03-21 15:03:37 +05302046static int is_hsleep_mode_procsed(struct ath6kl_vif *vif)
2047{
2048 return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
2049}
2050
2051static bool is_ctrl_ep_empty(struct ath6kl *ar)
2052{
2053 return !ar->tx_pending[ar->ctrl_ep];
2054}
2055
2056static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
2057{
2058 int ret, left;
2059
2060 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
2061
2062 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2063 ATH6KL_HOST_MODE_ASLEEP);
2064 if (ret)
2065 return ret;
2066
2067 left = wait_event_interruptible_timeout(ar->event_wq,
2068 is_hsleep_mode_procsed(vif),
2069 WMI_TIMEOUT);
2070 if (left == 0) {
2071 ath6kl_warn("timeout, didn't get host sleep cmd processed event\n");
2072 ret = -ETIMEDOUT;
2073 } else if (left < 0) {
2074 ath6kl_warn("error while waiting for host sleep cmd processed event %d\n",
2075 left);
2076 ret = left;
2077 }
2078
2079 if (ar->tx_pending[ar->ctrl_ep]) {
2080 left = wait_event_interruptible_timeout(ar->event_wq,
2081 is_ctrl_ep_empty(ar),
2082 WMI_TIMEOUT);
2083 if (left == 0) {
2084 ath6kl_warn("clear wmi ctrl data timeout\n");
2085 ret = -ETIMEDOUT;
2086 } else if (left < 0) {
2087 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
2088 ret = left;
2089 }
2090 }
2091
2092 return ret;
2093}
2094
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002095static int ath6kl_wow_suspend_vif(struct ath6kl_vif *vif,
2096 struct cfg80211_wowlan *wow, u32 *filter)
Raja Manid91e8ee2012-01-30 17:13:10 +05302097{
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002098 struct ath6kl *ar = vif->ar;
Raja Manid91e8ee2012-01-30 17:13:10 +05302099 struct in_device *in_dev;
2100 struct in_ifaddr *ifa;
Raja Mani055bde42012-03-21 15:03:37 +05302101 int ret;
Raja Manice0dc0c2012-02-20 19:08:08 +05302102 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05302103 __be32 ips[MAX_IP_ADDRS];
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002104 u8 index = 0;
Raja Manid91e8ee2012-01-30 17:13:10 +05302105
Naveen Gangadharan6821d4f2012-05-11 14:19:09 -07002106 if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) &&
2107 test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
2108 ar->fw_capabilities)) {
Naveen Gangadharan6251d802012-04-20 12:46:56 -07002109 ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
2110 vif->fw_vif_idx, false);
2111 if (ret)
2112 return ret;
2113 }
2114
Raja Manid91e8ee2012-01-30 17:13:10 +05302115 /* Clear existing WOW patterns */
2116 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
2117 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
2118 WOW_LIST_ID, i);
2119
2120 /*
2121 * Skip the default WOW pattern configuration
2122 * if the driver receives any WOW patterns from
2123 * the user.
2124 */
2125 if (wow)
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002126 ret = ath6kl_wow_usr(ar, vif, wow, filter);
Raja Manid91e8ee2012-01-30 17:13:10 +05302127 else if (vif->nw_type == AP_NETWORK)
2128 ret = ath6kl_wow_ap(ar, vif);
2129 else
2130 ret = ath6kl_wow_sta(ar, vif);
2131
2132 if (ret)
2133 return ret;
2134
Raja Mani390a8c82012-03-07 11:35:04 +05302135 netif_stop_queue(vif->ndev);
2136
Raja Manice0dc0c2012-02-20 19:08:08 +05302137 if (vif->nw_type != AP_NETWORK) {
2138 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2139 ATH6KL_MAX_WOW_LISTEN_INTL,
2140 0);
2141 if (ret)
2142 return ret;
2143
2144 /* Set listen interval x 15 times as bmiss time */
2145 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
2146 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
2147 bmiss_time = ATH6KL_MAX_BMISS_TIME;
2148
2149 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2150 bmiss_time, 0);
2151 if (ret)
2152 return ret;
2153
2154 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2155 0xFFFF, 0, 0xFFFF, 0, 0, 0,
2156 0, 0, 0, 0);
2157 if (ret)
2158 return ret;
2159 }
2160
Raja Manic08631c2011-12-16 14:24:24 +05302161 /* Setup own IP addr for ARP agent. */
2162 in_dev = __in_dev_get_rtnl(vif->ndev);
2163 if (!in_dev)
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002164 return 0;
Raja Manic08631c2011-12-16 14:24:24 +05302165
2166 ifa = in_dev->ifa_list;
2167 memset(&ips, 0, sizeof(ips));
2168
2169 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2170 while (index < MAX_IP_ADDRS && ifa) {
2171 ips[index] = ifa->ifa_local;
2172 ifa = ifa->ifa_next;
2173 index++;
2174 }
2175
2176 if (ifa) {
2177 ath6kl_err("total IP addr count is exceeding fw limit\n");
2178 return -EINVAL;
2179 }
2180
2181 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2182 if (ret) {
2183 ath6kl_err("fail to setup ip for arp agent\n");
2184 return ret;
2185 }
2186
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002187 return ret;
2188}
2189
2190static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
2191{
2192 struct ath6kl_vif *first_vif, *vif;
2193 int ret = 0;
2194 u32 filter = 0;
2195 bool connected = false;
2196
2197 /* enter / leave wow suspend on first vif always */
2198 first_vif = ath6kl_vif_first(ar);
2199 if (WARN_ON(unlikely(!first_vif)) ||
2200 !ath6kl_cfg80211_ready(first_vif))
2201 return -EIO;
2202
2203 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
2204 return -EINVAL;
2205
2206 /* install filters for each connected vif */
2207 spin_lock_bh(&ar->list_lock);
2208 list_for_each_entry(vif, &ar->vif_list, list) {
2209 if (!test_bit(CONNECTED, &vif->flags) ||
2210 !ath6kl_cfg80211_ready(vif))
2211 continue;
2212 connected = true;
2213
2214 ret = ath6kl_wow_suspend_vif(vif, wow, &filter);
2215 if (ret)
2216 break;
2217 }
2218 spin_unlock_bh(&ar->list_lock);
2219
2220 if (!connected)
2221 return -ENOTCONN;
2222 else if (ret)
2223 return ret;
2224
2225 ar->state = ATH6KL_STATE_SUSPENDING;
2226
2227 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, first_vif->fw_vif_idx,
Raja Mani6cb3c712011-11-07 22:52:45 +02002228 ATH6KL_WOW_MODE_ENABLE,
2229 filter,
2230 WOW_HOST_REQ_DELAY);
2231 if (ret)
2232 return ret;
2233
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002234 return ath6kl_cfg80211_host_sleep(ar, first_vif);
Raja Mani6cb3c712011-11-07 22:52:45 +02002235}
2236
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002237static int ath6kl_wow_resume_vif(struct ath6kl_vif *vif)
Raja Mani6cb3c712011-11-07 22:52:45 +02002238{
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002239 struct ath6kl *ar = vif->ar;
Raja Mani6cb3c712011-11-07 22:52:45 +02002240 int ret;
2241
Raja Manice0dc0c2012-02-20 19:08:08 +05302242 if (vif->nw_type != AP_NETWORK) {
2243 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2244 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2245 if (ret)
2246 return ret;
2247
2248 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2249 vif->listen_intvl_t, 0);
2250 if (ret)
2251 return ret;
2252
2253 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2254 vif->bmiss_time_t, 0);
2255 if (ret)
2256 return ret;
2257 }
2258
Naveen Gangadharan6821d4f2012-05-11 14:19:09 -07002259 if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) &&
2260 test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
2261 ar->fw_capabilities)) {
Naveen Gangadharan6251d802012-04-20 12:46:56 -07002262 ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002263 vif->fw_vif_idx, true);
Naveen Gangadharan6251d802012-04-20 12:46:56 -07002264 if (ret)
2265 return ret;
2266 }
2267
Raja Mani390a8c82012-03-07 11:35:04 +05302268 netif_wake_queue(vif->ndev);
2269
2270 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002271}
2272
Thomas Pedersenfd4377b2012-08-09 17:32:33 -07002273static int ath6kl_wow_resume(struct ath6kl *ar)
2274{
2275 struct ath6kl_vif *vif;
2276 int ret;
2277
2278 vif = ath6kl_vif_first(ar);
2279 if (WARN_ON(unlikely(!vif)) ||
2280 !ath6kl_cfg80211_ready(vif))
2281 return -EIO;
2282
2283 ar->state = ATH6KL_STATE_RESUMING;
2284
2285 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2286 ATH6KL_HOST_MODE_AWAKE);
2287 if (ret) {
2288 ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n",
2289 ret);
2290 goto cleanup;
2291 }
2292
2293 spin_lock_bh(&ar->list_lock);
2294 list_for_each_entry(vif, &ar->vif_list, list) {
2295 if (!test_bit(CONNECTED, &vif->flags) ||
2296 !ath6kl_cfg80211_ready(vif))
2297 continue;
2298 ret = ath6kl_wow_resume_vif(vif);
2299 if (ret)
2300 break;
2301 }
2302 spin_unlock_bh(&ar->list_lock);
2303
2304 if (ret)
2305 goto cleanup;
2306
2307 ar->state = ATH6KL_STATE_ON;
2308 return 0;
2309
2310cleanup:
2311 ar->state = ATH6KL_STATE_WOW;
2312 return ret;
2313}
2314
Raja Mani40abc2d2012-03-21 15:03:38 +05302315static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
2316{
2317 struct ath6kl_vif *vif;
2318 int ret;
2319
2320 vif = ath6kl_vif_first(ar);
2321 if (!vif)
2322 return -EIO;
2323
Ming Jiang48f27582012-04-13 21:09:25 +08002324 if (!test_bit(WMI_READY, &ar->flag)) {
2325 ath6kl_err("deepsleep failed as wmi is not ready\n");
Raja Mani40abc2d2012-03-21 15:03:38 +05302326 return -EIO;
Ming Jiang48f27582012-04-13 21:09:25 +08002327 }
Raja Mani40abc2d2012-03-21 15:03:38 +05302328
2329 ath6kl_cfg80211_stop_all(ar);
2330
2331 /* Save the current power mode before enabling power save */
2332 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2333
2334 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2335 if (ret)
2336 return ret;
2337
2338 /* Disable WOW mode */
2339 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2340 ATH6KL_WOW_MODE_DISABLE,
2341 0, 0);
2342 if (ret)
2343 return ret;
2344
2345 /* Flush all non control pkts in TX path */
2346 ath6kl_tx_data_cleanup(ar);
2347
2348 ret = ath6kl_cfg80211_host_sleep(ar, vif);
2349 if (ret)
2350 return ret;
2351
2352 return 0;
2353}
2354
2355static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
2356{
2357 struct ath6kl_vif *vif;
2358 int ret;
2359
2360 vif = ath6kl_vif_first(ar);
2361
2362 if (!vif)
2363 return -EIO;
2364
2365 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2366 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2367 ar->wmi->saved_pwr_mode);
2368 if (ret)
2369 return ret;
2370 }
2371
2372 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2373 ATH6KL_HOST_MODE_AWAKE);
2374 if (ret)
2375 return ret;
2376
2377 ar->state = ATH6KL_STATE_ON;
2378
2379 /* Reset scan parameter to default values */
2380 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2381 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2382 if (ret)
2383 return ret;
2384
2385 return 0;
2386}
2387
Kalle Valo52d81a62011-11-01 08:44:21 +02002388int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002389 enum ath6kl_cfg_suspend_mode mode,
2390 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002391{
Vivek Natarajan3d794992012-03-28 19:21:26 +05302392 struct ath6kl_vif *vif;
Raja Mani390a8c82012-03-07 11:35:04 +05302393 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002394 int ret;
2395
Kalle Valo52d81a62011-11-01 08:44:21 +02002396 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002397 case ATH6KL_CFG_SUSPEND_WOW:
2398
2399 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2400
2401 /* Flush all non control pkts in TX path */
2402 ath6kl_tx_data_cleanup(ar);
2403
Raja Mani390a8c82012-03-07 11:35:04 +05302404 prev_state = ar->state;
2405
Raja Manid7c44e02011-11-07 22:52:46 +02002406 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302407 if (ret) {
2408 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002409 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302410 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302411
Raja Manid7c44e02011-11-07 22:52:46 +02002412 ar->state = ATH6KL_STATE_WOW;
2413 break;
2414
Kalle Valo52d81a62011-11-01 08:44:21 +02002415 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002416
Raja Mani40abc2d2012-03-21 15:03:38 +05302417 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
Raja Mani524441e2011-11-07 22:52:46 +02002418
Raja Mani40abc2d2012-03-21 15:03:38 +05302419 ret = ath6kl_cfg80211_deepsleep_suspend(ar);
Kalle Valo52d81a62011-11-01 08:44:21 +02002420 if (ret) {
Raja Mani40abc2d2012-03-21 15:03:38 +05302421 ath6kl_err("deepsleep suspend failed: %d\n", ret);
2422 return ret;
Kalle Valo52d81a62011-11-01 08:44:21 +02002423 }
2424
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002425 ar->state = ATH6KL_STATE_DEEPSLEEP;
2426
Kalle Valo52d81a62011-11-01 08:44:21 +02002427 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002428
2429 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002430
Kalle Valo7125f012011-12-13 14:51:37 +02002431 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002432
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002433 if (ar->state == ATH6KL_STATE_OFF) {
2434 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2435 "suspend hw off, no action for cutpower\n");
2436 break;
2437 }
2438
2439 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2440
2441 ret = ath6kl_init_hw_stop(ar);
2442 if (ret) {
2443 ath6kl_warn("failed to stop hw during suspend: %d\n",
2444 ret);
2445 }
2446
2447 ar->state = ATH6KL_STATE_CUTPOWER;
2448
2449 break;
2450
Kalle Valo10509f92011-12-13 14:52:07 +02002451 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2452 /*
2453 * Nothing needed for schedule scan, firmware is already in
2454 * wow mode and sleeping most of the time.
2455 */
2456 break;
2457
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002458 default:
2459 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002460 }
2461
Vivek Natarajan3d794992012-03-28 19:21:26 +05302462 list_for_each_entry(vif, &ar->vif_list, list)
2463 ath6kl_cfg80211_scan_complete_event(vif, true);
2464
Kalle Valo52d81a62011-11-01 08:44:21 +02002465 return 0;
2466}
Kalle Valod6a434d2012-01-17 20:09:36 +02002467EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002468
2469int ath6kl_cfg80211_resume(struct ath6kl *ar)
2470{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002471 int ret;
2472
2473 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002474 case ATH6KL_STATE_WOW:
2475 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2476
2477 ret = ath6kl_wow_resume(ar);
2478 if (ret) {
2479 ath6kl_warn("wow mode resume failed: %d\n", ret);
2480 return ret;
2481 }
2482
Raja Manid7c44e02011-11-07 22:52:46 +02002483 break;
2484
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002485 case ATH6KL_STATE_DEEPSLEEP:
Raja Mani40abc2d2012-03-21 15:03:38 +05302486 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
2487
2488 ret = ath6kl_cfg80211_deepsleep_resume(ar);
2489 if (ret) {
2490 ath6kl_warn("deep sleep resume failed: %d\n", ret);
2491 return ret;
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002492 }
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002493 break;
2494
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002495 case ATH6KL_STATE_CUTPOWER:
2496 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2497
2498 ret = ath6kl_init_hw_start(ar);
2499 if (ret) {
2500 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2501 return ret;
2502 }
Raja Manid7c44e02011-11-07 22:52:46 +02002503 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002504
Kalle Valo10509f92011-12-13 14:52:07 +02002505 case ATH6KL_STATE_SCHED_SCAN:
2506 break;
2507
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002508 default:
2509 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002510 }
2511
2512 return 0;
2513}
Kalle Valod6a434d2012-01-17 20:09:36 +02002514EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002515
Kalle Valoabcb3442011-07-22 08:26:20 +03002516#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002517
2518/* hif layer decides what suspend mode to use */
2519static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002520 struct cfg80211_wowlan *wow)
2521{
2522 struct ath6kl *ar = wiphy_priv(wiphy);
2523
Raja Mani0f60e9f2011-11-07 22:52:45 +02002524 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002525}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002526
Kalle Valo52d81a62011-11-01 08:44:21 +02002527static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002528{
2529 struct ath6kl *ar = wiphy_priv(wiphy);
2530
2531 return ath6kl_hif_resume(ar);
2532}
Raja Mania918fb32011-11-07 22:52:46 +02002533
2534/*
2535 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2536 * both sdio irq wake up and keep power. The target pulls sdio data line to
2537 * wake up the host when WOW pattern matches. This causes sdio irq handler
2538 * is being called in the host side which internally hits ath6kl's RX path.
2539 *
2540 * Since sdio interrupt is not disabled, RX path executes even before
2541 * the host executes the actual resume operation from PM module.
2542 *
2543 * In the current scenario, WOW resume should happen before start processing
2544 * any data from the target. So It's required to perform WOW resume in RX path.
2545 * Ideally we should perform WOW resume only in the actual platform
2546 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2547 *
2548 * ath6kl_check_wow_status() is called from ath6kl_rx().
2549 */
2550void ath6kl_check_wow_status(struct ath6kl *ar)
2551{
Raja Mani390a8c82012-03-07 11:35:04 +05302552 if (ar->state == ATH6KL_STATE_SUSPENDING)
2553 return;
2554
Raja Mania918fb32011-11-07 22:52:46 +02002555 if (ar->state == ATH6KL_STATE_WOW)
2556 ath6kl_cfg80211_resume(ar);
2557}
2558
2559#else
2560
2561void ath6kl_check_wow_status(struct ath6kl *ar)
2562{
2563}
Kalle Valoabcb3442011-07-22 08:26:20 +03002564#endif
2565
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302566static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
2567 bool ht_enable)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002568{
Kiran Reddy67b3f122012-05-29 11:12:50 -07002569 struct ath6kl_htcap *htcap = &vif->htcap[band];
Sujith Manoharane68f6752011-12-22 12:15:27 +05302570
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302571 if (htcap->ht_enable == ht_enable)
2572 return 0;
Sujith Manoharane68f6752011-12-22 12:15:27 +05302573
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302574 if (ht_enable) {
2575 /* Set default ht capabilities */
2576 htcap->ht_enable = true;
2577 htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ?
2578 ath6kl_g_htcap : ath6kl_a_htcap;
2579 htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
2580 } else /* Disable ht */
2581 memset(htcap, 0, sizeof(*htcap));
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002582
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302583 return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx,
2584 band, htcap);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002585}
2586
Thomas Pedersen37a2f952012-04-19 15:31:57 -07002587static int ath6kl_restore_htcap(struct ath6kl_vif *vif)
2588{
2589 struct wiphy *wiphy = vif->ar->wiphy;
2590 int band, ret = 0;
2591
2592 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2593 if (!wiphy->bands[band])
2594 continue;
2595
2596 ret = ath6kl_set_htcap(vif, band,
2597 wiphy->bands[band]->ht_cap.ht_supported);
2598 if (ret)
2599 return ret;
2600 }
2601
2602 return ret;
2603}
2604
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002605static bool ath6kl_is_p2p_ie(const u8 *pos)
2606{
2607 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2608 pos[2] == 0x50 && pos[3] == 0x6f &&
2609 pos[4] == 0x9a && pos[5] == 0x09;
2610}
2611
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302612static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2613 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002614{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302615 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002616 const u8 *pos;
2617 u8 *buf = NULL;
2618 size_t len = 0;
2619 int ret;
2620
2621 /*
2622 * Filter out P2P IE(s) since they will be included depending on
2623 * the Probe Request frame in ath6kl_send_go_probe_resp().
2624 */
2625
2626 if (ies && ies_len) {
2627 buf = kmalloc(ies_len, GFP_KERNEL);
2628 if (buf == NULL)
2629 return -ENOMEM;
2630 pos = ies;
2631 while (pos + 1 < ies + ies_len) {
2632 if (pos + 2 + pos[1] > ies + ies_len)
2633 break;
2634 if (!ath6kl_is_p2p_ie(pos)) {
2635 memcpy(buf + len, pos, 2 + pos[1]);
2636 len += 2 + pos[1];
2637 }
2638 pos += 2 + pos[1];
2639 }
2640 }
2641
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302642 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2643 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002644 kfree(buf);
2645 return ret;
2646}
2647
Johannes Berg88600202012-02-13 15:17:18 +01002648static int ath6kl_set_ies(struct ath6kl_vif *vif,
2649 struct cfg80211_beacon_data *info)
2650{
2651 struct ath6kl *ar = vif->ar;
2652 int res;
2653
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002654 /* this also clears IE in fw if it's not set */
2655 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2656 WMI_FRAME_BEACON,
2657 info->beacon_ies,
2658 info->beacon_ies_len);
2659 if (res)
2660 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002661
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002662 /* this also clears IE in fw if it's not set */
2663 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2664 info->proberesp_ies_len);
2665 if (res)
2666 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002667
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002668 /* this also clears IE in fw if it's not set */
2669 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2670 WMI_FRAME_ASSOC_RESP,
2671 info->assocresp_ies,
2672 info->assocresp_ies_len);
2673 if (res)
2674 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002675
2676 return 0;
2677}
2678
Thomas Pedersenc422d52d2012-05-15 00:09:23 -07002679void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable)
2680{
2681 int err;
2682
2683 if (WARN_ON(!test_bit(WMI_READY, &vif->ar->flag)))
2684 return;
2685
2686 if (vif->nw_type != INFRA_NETWORK)
2687 return;
2688
2689 if (!test_bit(ATH6KL_FW_CAPABILITY_BMISS_ENHANCE,
2690 vif->ar->fw_capabilities))
2691 return;
2692
2693 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s fw bmiss enhance\n",
2694 enable ? "enable" : "disable");
2695
2696 err = ath6kl_wmi_sta_bmiss_enhance_cmd(vif->ar->wmi,
2697 vif->fw_vif_idx, enable);
2698 if (err)
2699 ath6kl_err("failed to %s enhanced bmiss detection: %d\n",
2700 enable ? "enable" : "disable", err);
2701}
2702
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302703static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
2704 u8 *rsn_capab)
2705{
2706 const u8 *rsn_ie;
2707 size_t rsn_ie_len;
2708 u16 cnt;
2709
2710 if (!beacon->tail)
2711 return -EINVAL;
2712
2713 rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len);
2714 if (!rsn_ie)
2715 return -EINVAL;
2716
2717 rsn_ie_len = *(rsn_ie + 1);
2718 /* skip element id and length */
2719 rsn_ie += 2;
2720
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302721 /* skip version */
2722 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302723 return -EINVAL;
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302724 rsn_ie += 2;
2725 rsn_ie_len -= 2;
2726
2727 /* skip group cipher suite */
2728 if (rsn_ie_len < 4)
2729 return 0;
2730 rsn_ie += 4;
2731 rsn_ie_len -= 4;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302732
2733 /* skip pairwise cipher suite */
2734 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302735 return 0;
Vasanthakumar Thiagarajan798985c2012-04-10 13:35:47 +05302736 cnt = get_unaligned_le16(rsn_ie);
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302737 rsn_ie += (2 + cnt * 4);
2738 rsn_ie_len -= (2 + cnt * 4);
2739
2740 /* skip akm suite */
2741 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302742 return 0;
Vasanthakumar Thiagarajan798985c2012-04-10 13:35:47 +05302743 cnt = get_unaligned_le16(rsn_ie);
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302744 rsn_ie += (2 + cnt * 4);
2745 rsn_ie_len -= (2 + cnt * 4);
2746
2747 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302748 return 0;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302749
2750 memcpy(rsn_capab, rsn_ie, 2);
2751
2752 return 0;
2753}
2754
Johannes Berg88600202012-02-13 15:17:18 +01002755static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2756 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002757{
2758 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302759 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002760 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002761 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002762 u8 *ies;
2763 int ies_len;
2764 struct wmi_connect_cmd p;
2765 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302766 int i, ret;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302767 u16 rsn_capab = 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002768
Johannes Berg88600202012-02-13 15:17:18 +01002769 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002770
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302771 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002772 return -EIO;
2773
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302774 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002775 return -EOPNOTSUPP;
2776
Johannes Berg88600202012-02-13 15:17:18 +01002777 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002778
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002779 ar->ap_mode_bkey.valid = false;
2780
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002781 /* TODO:
2782 * info->interval
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002783 */
2784
Etay Luzd154f322012-05-30 11:35:08 +03002785 ret = ath6kl_wmi_ap_set_dtim_cmd(ar->wmi, vif->fw_vif_idx,
2786 info->dtim_period);
2787
2788 /* ignore error, just print a warning and continue normally */
2789 if (ret)
2790 ath6kl_warn("Failed to set dtim_period in beacon: %d\n", ret);
2791
Johannes Berg88600202012-02-13 15:17:18 +01002792 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002793 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002794 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002795 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002796 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002797 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002798 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002799
2800 if (info->ssid == NULL)
2801 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302802 memcpy(vif->ssid, info->ssid, info->ssid_len);
2803 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002804 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002805 hidden = true;
2806
2807 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2808 if (res)
2809 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002810
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302811 ret = ath6kl_set_auth_type(vif, info->auth_type);
2812 if (ret)
2813 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002814
2815 memset(&p, 0, sizeof(p));
2816
2817 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2818 switch (info->crypto.akm_suites[i]) {
2819 case WLAN_AKM_SUITE_8021X:
2820 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2821 p.auth_mode |= WPA_AUTH;
2822 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2823 p.auth_mode |= WPA2_AUTH;
2824 break;
2825 case WLAN_AKM_SUITE_PSK:
2826 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2827 p.auth_mode |= WPA_PSK_AUTH;
2828 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2829 p.auth_mode |= WPA2_PSK_AUTH;
2830 break;
2831 }
2832 }
2833 if (p.auth_mode == 0)
2834 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302835 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002836
2837 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2838 switch (info->crypto.ciphers_pairwise[i]) {
2839 case WLAN_CIPHER_SUITE_WEP40:
2840 case WLAN_CIPHER_SUITE_WEP104:
2841 p.prwise_crypto_type |= WEP_CRYPT;
2842 break;
2843 case WLAN_CIPHER_SUITE_TKIP:
2844 p.prwise_crypto_type |= TKIP_CRYPT;
2845 break;
2846 case WLAN_CIPHER_SUITE_CCMP:
2847 p.prwise_crypto_type |= AES_CRYPT;
2848 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002849 case WLAN_CIPHER_SUITE_SMS4:
2850 p.prwise_crypto_type |= WAPI_CRYPT;
2851 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002852 }
2853 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002854 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002855 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302856 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002857 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302858 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002859
2860 switch (info->crypto.cipher_group) {
2861 case WLAN_CIPHER_SUITE_WEP40:
2862 case WLAN_CIPHER_SUITE_WEP104:
2863 p.grp_crypto_type = WEP_CRYPT;
2864 break;
2865 case WLAN_CIPHER_SUITE_TKIP:
2866 p.grp_crypto_type = TKIP_CRYPT;
2867 break;
2868 case WLAN_CIPHER_SUITE_CCMP:
2869 p.grp_crypto_type = AES_CRYPT;
2870 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002871 case WLAN_CIPHER_SUITE_SMS4:
2872 p.grp_crypto_type = WAPI_CRYPT;
2873 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002874 default:
2875 p.grp_crypto_type = NONE_CRYPT;
2876 break;
2877 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302878 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002879
2880 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302881 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002882
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302883 p.ssid_len = vif->ssid_len;
2884 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2885 p.dot11_auth_mode = vif->dot11_auth_mode;
Johannes Bergaa430da2012-05-16 23:50:18 +02002886 p.ch = cpu_to_le16(info->channel->center_freq);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002887
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302888 /* Enable uAPSD support by default */
2889 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2890 if (res < 0)
2891 return res;
2892
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002893 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2894 p.nw_subtype = SUBTYPE_P2PGO;
2895 } else {
2896 /*
2897 * Due to firmware limitation, it is not possible to
2898 * do P2P mgmt operations in AP mode
2899 */
2900 p.nw_subtype = SUBTYPE_NONE;
2901 }
2902
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05302903 if (info->inactivity_timeout) {
2904 res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
2905 info->inactivity_timeout);
2906 if (res < 0)
2907 return res;
2908 }
2909
Johannes Bergaa430da2012-05-16 23:50:18 +02002910 if (ath6kl_set_htcap(vif, info->channel->band,
2911 info->channel_type != NL80211_CHAN_NO_HT))
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302912 return -EIO;
2913
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302914 /*
2915 * Get the PTKSA replay counter in the RSN IE. Supplicant
2916 * will use the RSN IE in M3 message and firmware has to
2917 * advertise the same in beacon/probe response. Send
2918 * the complete RSN IE capability field to firmware
2919 */
2920 if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) &&
2921 test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
2922 ar->fw_capabilities)) {
2923 res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
2924 WLAN_EID_RSN, WMI_RSN_IE_CAPB,
2925 (const u8 *) &rsn_capab,
2926 sizeof(rsn_capab));
Thomas Pedersenf21243a2012-07-27 18:13:27 -07002927 vif->rsn_capab = rsn_capab;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302928 if (res < 0)
2929 return res;
2930 }
2931
Thomas Pedersenc4f78632012-04-06 13:35:48 -07002932 memcpy(&vif->profile, &p, sizeof(p));
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302933 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002934 if (res < 0)
2935 return res;
2936
2937 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002938}
2939
Johannes Berg88600202012-02-13 15:17:18 +01002940static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2941 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002942{
Johannes Berg88600202012-02-13 15:17:18 +01002943 struct ath6kl_vif *vif = netdev_priv(dev);
2944
2945 if (!ath6kl_cfg80211_ready(vif))
2946 return -EIO;
2947
2948 if (vif->next_mode != AP_NETWORK)
2949 return -EOPNOTSUPP;
2950
2951 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002952}
2953
Johannes Berg88600202012-02-13 15:17:18 +01002954static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002955{
2956 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302957 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002958
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302959 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002960 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302961 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002962 return -ENOTCONN;
2963
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302964 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302965 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002966
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302967 /* Restore ht setting in firmware */
Thomas Pedersen37a2f952012-04-19 15:31:57 -07002968 return ath6kl_restore_htcap(vif);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002969}
2970
Jouni Malinen33e53082011-12-27 11:02:56 +02002971static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2972
2973static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2974 u8 *mac)
2975{
2976 struct ath6kl *ar = ath6kl_priv(dev);
2977 struct ath6kl_vif *vif = netdev_priv(dev);
2978 const u8 *addr = mac ? mac : bcast_addr;
2979
2980 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2981 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2982}
2983
Jouni Malinen23875132011-08-30 21:57:53 +03002984static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2985 u8 *mac, struct station_parameters *params)
2986{
2987 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302988 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002989
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302990 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002991 return -EOPNOTSUPP;
2992
2993 /* Use this only for authorizing/unauthorizing a station */
2994 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2995 return -EOPNOTSUPP;
2996
2997 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302998 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2999 WMI_AP_MLME_AUTHORIZE, mac, 0);
3000 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
3001 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03003002}
3003
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003004static int ath6kl_remain_on_channel(struct wiphy *wiphy,
Johannes Berg71bbc992012-06-15 15:30:18 +02003005 struct wireless_dev *wdev,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003006 struct ieee80211_channel *chan,
3007 enum nl80211_channel_type channel_type,
3008 unsigned int duration,
3009 u64 *cookie)
3010{
Johannes Berg71bbc992012-06-15 15:30:18 +02003011 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
3012 struct ath6kl *ar = ath6kl_priv(vif->ndev);
Jouni Malinen10522612011-10-27 16:00:13 +03003013 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003014
3015 /* TODO: if already pending or ongoing remain-on-channel,
3016 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03003017 id = ++vif->last_roc_id;
3018 if (id == 0) {
3019 /* Do not use 0 as the cookie value */
3020 id = ++vif->last_roc_id;
3021 }
3022 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003023
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303024 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
3025 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003026}
3027
3028static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
Johannes Berg71bbc992012-06-15 15:30:18 +02003029 struct wireless_dev *wdev,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003030 u64 cookie)
3031{
Johannes Berg71bbc992012-06-15 15:30:18 +02003032 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
3033 struct ath6kl *ar = ath6kl_priv(vif->ndev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003034
Jouni Malinen10522612011-10-27 16:00:13 +03003035 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003036 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03003037 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003038
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303039 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003040}
3041
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303042static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
3043 const u8 *buf, size_t len,
3044 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003045{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303046 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003047 const u8 *pos;
3048 u8 *p2p;
3049 int p2p_len;
3050 int ret;
3051 const struct ieee80211_mgmt *mgmt;
3052
3053 mgmt = (const struct ieee80211_mgmt *) buf;
3054
3055 /* Include P2P IE(s) from the frame generated in user space. */
3056
3057 p2p = kmalloc(len, GFP_KERNEL);
3058 if (p2p == NULL)
3059 return -ENOMEM;
3060 p2p_len = 0;
3061
3062 pos = mgmt->u.probe_resp.variable;
3063 while (pos + 1 < buf + len) {
3064 if (pos + 2 + pos[1] > buf + len)
3065 break;
3066 if (ath6kl_is_p2p_ie(pos)) {
3067 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
3068 p2p_len += 2 + pos[1];
3069 }
3070 pos += 2 + pos[1];
3071 }
3072
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303073 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
3074 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003075 kfree(p2p);
3076 return ret;
3077}
3078
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003079static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
3080 u32 id,
3081 u32 freq,
3082 u32 wait,
3083 const u8 *buf,
3084 size_t len,
3085 bool *more_data,
3086 bool no_cck)
3087{
3088 struct ieee80211_mgmt *mgmt;
3089 struct ath6kl_sta *conn;
3090 bool is_psq_empty = false;
3091 struct ath6kl_mgmt_buff *mgmt_buf;
3092 size_t mgmt_buf_size;
3093 struct ath6kl *ar = vif->ar;
3094
3095 mgmt = (struct ieee80211_mgmt *) buf;
3096 if (is_multicast_ether_addr(mgmt->da))
3097 return false;
3098
3099 conn = ath6kl_find_sta(vif, mgmt->da);
3100 if (!conn)
3101 return false;
3102
3103 if (conn->sta_flags & STA_PS_SLEEP) {
3104 if (!(conn->sta_flags & STA_PS_POLLED)) {
3105 /* Queue the frames if the STA is sleeping */
3106 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
3107 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
3108 if (!mgmt_buf)
3109 return false;
3110
3111 INIT_LIST_HEAD(&mgmt_buf->list);
3112 mgmt_buf->id = id;
3113 mgmt_buf->freq = freq;
3114 mgmt_buf->wait = wait;
3115 mgmt_buf->len = len;
3116 mgmt_buf->no_cck = no_cck;
3117 memcpy(mgmt_buf->buf, buf, len);
3118 spin_lock_bh(&conn->psq_lock);
3119 is_psq_empty = skb_queue_empty(&conn->psq) &&
3120 (conn->mgmt_psq_len == 0);
3121 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
3122 conn->mgmt_psq_len++;
3123 spin_unlock_bh(&conn->psq_lock);
3124
3125 /*
3126 * If this is the first pkt getting queued
3127 * for this STA, update the PVB for this
3128 * STA.
3129 */
3130 if (is_psq_empty)
3131 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
3132 conn->aid, 1);
3133 return true;
3134 }
3135
3136 /*
3137 * This tx is because of a PsPoll.
3138 * Determine if MoreData bit has to be set.
3139 */
3140 spin_lock_bh(&conn->psq_lock);
3141 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
3142 *more_data = true;
3143 spin_unlock_bh(&conn->psq_lock);
3144 }
3145
3146 return false;
3147}
3148
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003149/* Check if SSID length is greater than DIRECT- */
3150static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
3151{
3152 const struct ieee80211_mgmt *mgmt;
3153 mgmt = (const struct ieee80211_mgmt *) buf;
3154
3155 /* variable[1] contains the SSID tag length */
3156 if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
3157 (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
3158 return true;
3159 }
3160
3161 return false;
3162}
3163
Johannes Berg71bbc992012-06-15 15:30:18 +02003164static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003165 struct ieee80211_channel *chan, bool offchan,
3166 enum nl80211_channel_type channel_type,
3167 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01003168 const u8 *buf, size_t len, bool no_cck,
3169 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003170{
Johannes Berg71bbc992012-06-15 15:30:18 +02003171 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
3172 struct ath6kl *ar = ath6kl_priv(vif->ndev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003173 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003174 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003175 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003176
3177 mgmt = (const struct ieee80211_mgmt *) buf;
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003178 if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
3179 ieee80211_is_probe_resp(mgmt->frame_control) &&
3180 ath6kl_is_p2p_go_ssid(buf, len)) {
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003181 /*
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003182 * Send Probe Response frame in GO mode using a separate WMI
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003183 * command to allow the target to fill in the generic IEs.
3184 */
3185 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303186 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003187 chan->center_freq);
3188 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003189
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303190 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003191 if (id == 0) {
3192 /*
3193 * 0 is a reserved value in the WMI command and shall not be
3194 * used for the command.
3195 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303196 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003197 }
3198
3199 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003200
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003201 /* AP mode Power saving processing */
3202 if (vif->nw_type == AP_NETWORK) {
3203 queued = ath6kl_mgmt_powersave_ap(vif,
3204 id, chan->center_freq,
3205 wait, buf,
3206 len, &more_data, no_cck);
3207 if (queued)
3208 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003209 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003210
3211 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
3212 chan->center_freq, wait,
3213 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003214}
3215
Jouni Malinenae32c302011-08-30 21:58:01 +03003216static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
Johannes Berg71bbc992012-06-15 15:30:18 +02003217 struct wireless_dev *wdev,
Jouni Malinenae32c302011-08-30 21:58:01 +03003218 u16 frame_type, bool reg)
3219{
Johannes Berg71bbc992012-06-15 15:30:18 +02003220 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
Jouni Malinenae32c302011-08-30 21:58:01 +03003221
3222 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
3223 __func__, frame_type, reg);
3224 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
3225 /*
3226 * Note: This notification callback is not allowed to sleep, so
3227 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
3228 * hardcode target to report Probe Request frames all the time.
3229 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303230 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03003231 }
3232}
3233
Kalle Valo10509f92011-12-13 14:52:07 +02003234static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
3235 struct net_device *dev,
3236 struct cfg80211_sched_scan_request *request)
3237{
3238 struct ath6kl *ar = ath6kl_priv(dev);
3239 struct ath6kl_vif *vif = netdev_priv(dev);
3240 u16 interval;
Thomas Pedersen85b20fc2012-06-21 12:50:08 -07003241 int ret, rssi_thold;
Kalle Valo10509f92011-12-13 14:52:07 +02003242
3243 if (ar->state != ATH6KL_STATE_ON)
3244 return -EIO;
3245
3246 if (vif->sme_state != SME_DISCONNECTED)
3247 return -EBUSY;
3248
Thomas Pedersen77ed4e42012-04-19 16:29:19 -07003249 /* The FW currently can't support multi-vif WoW properly. */
3250 if (ar->num_vif > 1)
3251 return -EIO;
3252
Kalle Valob4d13d32012-03-21 10:01:09 +02003253 ath6kl_cfg80211_scan_complete_event(vif, true);
3254
Jouni Malinen3b8ffc62012-04-16 19:28:03 +03003255 ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
Naveen Singhdd45b752012-05-16 13:29:00 +03003256 request->n_ssids,
3257 request->match_sets,
3258 request->n_match_sets);
Jouni Malinen3b8ffc62012-04-16 19:28:03 +03003259 if (ret < 0)
3260 return ret;
Kalle Valo10509f92011-12-13 14:52:07 +02003261
Naveen Singhdd45b752012-05-16 13:29:00 +03003262 if (!request->n_match_sets) {
3263 ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
3264 ALL_BSS_FILTER, 0);
3265 if (ret < 0)
3266 return ret;
3267 } else {
3268 ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
3269 MATCHED_SSID_FILTER, 0);
3270 if (ret < 0)
3271 return ret;
3272 }
3273
Thomas Pedersen85b20fc2012-06-21 12:50:08 -07003274 if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD,
3275 ar->fw_capabilities)) {
3276 if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
3277 rssi_thold = 0;
3278 else if (request->rssi_thold < -127)
3279 rssi_thold = -127;
3280 else
3281 rssi_thold = request->rssi_thold;
3282
3283 ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx,
3284 rssi_thold);
3285 if (ret) {
3286 ath6kl_err("failed to set RSSI threshold for scan\n");
3287 return ret;
3288 }
3289 }
3290
Kalle Valo10509f92011-12-13 14:52:07 +02003291 /* fw uses seconds, also make sure that it's >0 */
3292 interval = max_t(u16, 1, request->interval / 1000);
3293
3294 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
3295 interval, interval,
Subramania Sharma Thandaveswarand472b5e2012-04-16 16:09:57 +05303296 vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
Kalle Valo10509f92011-12-13 14:52:07 +02003297
Kalle Valo10509f92011-12-13 14:52:07 +02003298 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
3299 ATH6KL_WOW_MODE_ENABLE,
3300 WOW_FILTER_SSID,
3301 WOW_HOST_REQ_DELAY);
3302 if (ret) {
3303 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
3304 return ret;
3305 }
3306
3307 /* this also clears IE in fw if it's not set */
3308 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
3309 WMI_FRAME_PROBE_REQ,
3310 request->ie, request->ie_len);
3311 if (ret) {
Joe Perchesf1ff32e2012-05-30 01:58:39 -07003312 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d\n",
Kalle Valo10509f92011-12-13 14:52:07 +02003313 ret);
3314 return ret;
3315 }
3316
3317 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
3318 ATH6KL_HOST_MODE_ASLEEP);
3319 if (ret) {
3320 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
3321 ret);
3322 return ret;
3323 }
3324
3325 ar->state = ATH6KL_STATE_SCHED_SCAN;
3326
3327 return ret;
3328}
3329
3330static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
3331 struct net_device *dev)
3332{
3333 struct ath6kl_vif *vif = netdev_priv(dev);
3334 bool stopped;
3335
3336 stopped = __ath6kl_cfg80211_sscan_stop(vif);
3337
3338 if (!stopped)
3339 return -EIO;
3340
3341 return 0;
3342}
3343
Bala Shanmugam06e360a2012-05-22 13:23:12 +05303344static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy,
3345 struct net_device *dev,
3346 const u8 *addr,
3347 const struct cfg80211_bitrate_mask *mask)
3348{
3349 struct ath6kl *ar = ath6kl_priv(dev);
3350 struct ath6kl_vif *vif = netdev_priv(dev);
3351
3352 return ath6kl_wmi_set_bitrate_mask(ar->wmi, vif->fw_vif_idx,
3353 mask);
3354}
3355
Thomas Pedersen279b2862012-07-17 19:39:55 -07003356static int ath6kl_cfg80211_set_txe_config(struct wiphy *wiphy,
3357 struct net_device *dev,
3358 u32 rate, u32 pkts, u32 intvl)
3359{
3360 struct ath6kl *ar = ath6kl_priv(dev);
3361 struct ath6kl_vif *vif = netdev_priv(dev);
3362
3363 if (vif->nw_type != INFRA_NETWORK ||
3364 !test_bit(ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, ar->fw_capabilities))
3365 return -EOPNOTSUPP;
3366
3367 if (vif->sme_state != SME_CONNECTED)
3368 return -ENOTCONN;
3369
3370 /* save this since the firmware won't report the interval */
3371 vif->txe_intvl = intvl;
3372
3373 return ath6kl_wmi_set_txe_notify(ar->wmi, vif->fw_vif_idx,
3374 rate, pkts, intvl);
3375}
3376
Jouni Malinenf80574a2011-08-30 21:58:04 +03003377static const struct ieee80211_txrx_stypes
3378ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
3379 [NL80211_IFTYPE_STATION] = {
3380 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3381 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3382 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3383 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3384 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02003385 [NL80211_IFTYPE_AP] = {
3386 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3387 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3388 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3389 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3390 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03003391 [NL80211_IFTYPE_P2P_CLIENT] = {
3392 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3393 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3394 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3395 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3396 },
3397 [NL80211_IFTYPE_P2P_GO] = {
3398 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3399 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3400 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3401 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3402 },
3403};
3404
Kalle Valobdcd8172011-07-18 00:22:30 +03003405static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303406 .add_virtual_intf = ath6kl_cfg80211_add_iface,
3407 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03003408 .change_virtual_intf = ath6kl_cfg80211_change_iface,
3409 .scan = ath6kl_cfg80211_scan,
3410 .connect = ath6kl_cfg80211_connect,
3411 .disconnect = ath6kl_cfg80211_disconnect,
3412 .add_key = ath6kl_cfg80211_add_key,
3413 .get_key = ath6kl_cfg80211_get_key,
3414 .del_key = ath6kl_cfg80211_del_key,
3415 .set_default_key = ath6kl_cfg80211_set_default_key,
3416 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
3417 .set_tx_power = ath6kl_cfg80211_set_txpower,
3418 .get_tx_power = ath6kl_cfg80211_get_txpower,
3419 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
3420 .join_ibss = ath6kl_cfg80211_join_ibss,
3421 .leave_ibss = ath6kl_cfg80211_leave_ibss,
3422 .get_station = ath6kl_get_station,
3423 .set_pmksa = ath6kl_set_pmksa,
3424 .del_pmksa = ath6kl_del_pmksa,
3425 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03003426 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03003427#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02003428 .suspend = __ath6kl_cfg80211_suspend,
3429 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03003430#endif
Johannes Berg88600202012-02-13 15:17:18 +01003431 .start_ap = ath6kl_start_ap,
3432 .change_beacon = ath6kl_change_beacon,
3433 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02003434 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03003435 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003436 .remain_on_channel = ath6kl_remain_on_channel,
3437 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003438 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03003439 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02003440 .sched_scan_start = ath6kl_cfg80211_sscan_start,
3441 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Bala Shanmugam06e360a2012-05-22 13:23:12 +05303442 .set_bitrate_mask = ath6kl_cfg80211_set_bitrate,
Thomas Pedersen279b2862012-07-17 19:39:55 -07003443 .set_cqm_txe_config = ath6kl_cfg80211_set_txe_config,
Kalle Valobdcd8172011-07-18 00:22:30 +03003444};
3445
Kalle Valo7125f012011-12-13 14:51:37 +02003446void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02003447{
Kalle Valo10509f92011-12-13 14:52:07 +02003448 ath6kl_cfg80211_sscan_disable(vif);
3449
Kalle Valoec4b7f62011-11-01 08:44:04 +02003450 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02003451 case SME_DISCONNECTED:
3452 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02003453 case SME_CONNECTING:
3454 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
3455 NULL, 0,
3456 WLAN_STATUS_UNSPECIFIED_FAILURE,
3457 GFP_KERNEL);
3458 break;
3459 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02003460 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
3461 break;
3462 }
3463
3464 if (test_bit(CONNECTED, &vif->flags) ||
3465 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02003466 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003467
3468 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02003469 clear_bit(CONNECTED, &vif->flags);
3470 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003471
3472 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02003473 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
3474 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
3475 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003476
3477 ath6kl_cfg80211_scan_complete_event(vif, true);
3478}
3479
Kalle Valo7125f012011-12-13 14:51:37 +02003480void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3481{
3482 struct ath6kl_vif *vif;
3483
3484 vif = ath6kl_vif_first(ar);
3485 if (!vif) {
3486 /* save the current power mode before enabling power save */
3487 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3488
3489 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
Kalle Valocdeb8602012-04-12 11:02:18 +03003490 ath6kl_warn("ath6kl_deep_sleep_enable: wmi_powermode_cmd failed\n");
Kalle Valo7125f012011-12-13 14:51:37 +02003491 return;
3492 }
3493
3494 /*
3495 * FIXME: we should take ar->list_lock to protect changes in the
3496 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3497 * sleeps.
3498 */
3499 list_for_each_entry(vif, &ar->vif_list, list)
3500 ath6kl_cfg80211_stop(vif);
3501}
3502
Kalle Valo84841ba2012-07-19 16:00:56 +03003503static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy,
3504 struct regulatory_request *request)
3505{
3506 struct ath6kl *ar = wiphy_priv(wiphy);
3507 u32 rates[IEEE80211_NUM_BANDS];
3508 int ret, i;
3509
3510 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
3511 "cfg reg_notify %c%c%s%s initiator %d\n",
3512 request->alpha2[0], request->alpha2[1],
3513 request->intersect ? " intersect" : "",
3514 request->processed ? " processed" : "",
3515 request->initiator);
3516
3517 ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2);
3518 if (ret) {
3519 ath6kl_err("failed to set regdomain: %d\n", ret);
3520 return ret;
3521 }
3522
3523 /*
3524 * Firmware will apply the regdomain change only after a scan is
3525 * issued and it will send a WMI_REGDOMAIN_EVENTID when it has been
3526 * changed.
3527 */
3528
3529 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
3530 if (wiphy->bands[i])
3531 rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
3532
3533
3534 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, 0, WMI_LONG_SCAN, false,
3535 false, 0, ATH6KL_FG_SCAN_INTERVAL,
3536 0, NULL, false, rates);
3537 if (ret) {
3538 ath6kl_err("failed to start scan for a regdomain change: %d\n",
3539 ret);
3540 return ret;
3541 }
3542
3543 return 0;
3544}
3545
Kalle Valoc25889e2012-01-17 20:08:27 +02003546static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003547{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303548 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303549 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303550 ath6kl_err("failed to initialize aggr\n");
3551 return -ENOMEM;
3552 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003553
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303554 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303555 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003556 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3557 (unsigned long) vif);
3558
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303559 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303560 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303561
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303562 INIT_LIST_HEAD(&vif->mc_filter);
3563
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303564 return 0;
3565}
3566
Kalle Valoc25889e2012-01-17 20:08:27 +02003567void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303568{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303569 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303570 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303571
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303572 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303573
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303574 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3575
3576 if (vif->nw_type == ADHOC_NETWORK)
3577 ar->ibss_if_active = false;
3578
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303579 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3580 list_del(&mc_filter->list);
3581 kfree(mc_filter);
3582 }
3583
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303584 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303585
3586 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303587}
3588
Johannes Berg552bff02012-09-19 09:26:06 +02003589struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
Johannes Berg84efbb82012-06-16 00:00:26 +02003590 enum nl80211_iftype type,
3591 u8 fw_vif_idx, u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303592{
3593 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303594 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303595
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303596 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303597 if (!ndev)
3598 return NULL;
3599
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303600 vif = netdev_priv(ndev);
3601 ndev->ieee80211_ptr = &vif->wdev;
3602 vif->wdev.wiphy = ar->wiphy;
3603 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303604 vif->ndev = ndev;
3605 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3606 vif->wdev.netdev = ndev;
3607 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303608 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003609 vif->nw_type = nw_type;
3610 vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303611 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303612 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Raja Manieb389872012-04-16 16:09:56 +05303613 vif->bg_scan_period = 0;
Kiran Reddy67b3f122012-05-29 11:12:50 -07003614 vif->htcap[IEEE80211_BAND_2GHZ].ht_enable = true;
3615 vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303616
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303617 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
Aarthi Thiruvengadamc95dcb52012-07-10 13:20:40 -07003618 if (fw_vif_idx != 0) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303619 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3620 0x2;
Aarthi Thiruvengadamc95dcb52012-07-10 13:20:40 -07003621 if (test_bit(ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR,
3622 ar->fw_capabilities))
3623 ndev->dev_addr[4] ^= 0x80;
3624 }
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303625
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303626 init_netdev(ndev);
3627
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303628 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303629
Kalle Valoc25889e2012-01-17 20:08:27 +02003630 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303631 goto err;
3632
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303633 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303634 goto err;
3635
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303636 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303637 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303638 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303639 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303640 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303641
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303642 if (type == NL80211_IFTYPE_ADHOC)
3643 ar->ibss_if_active = true;
3644
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303645 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303646 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303647 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303648
Johannes Berg84efbb82012-06-16 00:00:26 +02003649 return &vif->wdev;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303650
3651err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303652 aggr_module_destroy(vif->aggr_cntxt);
3653 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303654 return NULL;
3655}
3656
Kalle Valo46d33a22012-01-17 20:08:40 +02003657int ath6kl_cfg80211_init(struct ath6kl *ar)
3658{
3659 struct wiphy *wiphy = ar->wiphy;
Thomas Pedersend92917e2012-04-19 15:31:56 -07003660 bool band_2gig = false, band_5gig = false, ht = false;
Kalle Valo46d33a22012-01-17 20:08:40 +02003661 int ret;
3662
3663 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3664
3665 wiphy->max_remain_on_channel_duration = 5000;
3666
3667 /* set device pointer for wiphy */
3668 set_wiphy_dev(wiphy, ar->dev);
3669
3670 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3671 BIT(NL80211_IFTYPE_ADHOC) |
3672 BIT(NL80211_IFTYPE_AP);
3673 if (ar->p2p) {
3674 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3675 BIT(NL80211_IFTYPE_P2P_CLIENT);
3676 }
3677
Kalle Valo84841ba2012-07-19 16:00:56 +03003678 if (config_enabled(CONFIG_ATH6KL_REGDOMAIN) &&
3679 test_bit(ATH6KL_FW_CAPABILITY_REGDOMAIN, ar->fw_capabilities))
3680 wiphy->reg_notifier = ath6kl_cfg80211_reg_notify;
3681
Kalle Valo46d33a22012-01-17 20:08:40 +02003682 /* max num of ssids that can be probed during scanning */
Jouni Malinen8ab54152012-05-09 22:14:51 +03003683 wiphy->max_scan_ssids = MAX_PROBED_SSIDS;
Naveen Singhdd45b752012-05-16 13:29:00 +03003684
3685 /* max num of ssids that can be matched after scan */
3686 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST,
3687 ar->fw_capabilities))
3688 wiphy->max_match_sets = MAX_PROBED_SSIDS;
3689
Kalle Valo46d33a22012-01-17 20:08:40 +02003690 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
Thomas Pedersend92917e2012-04-19 15:31:56 -07003691 switch (ar->hw.cap) {
3692 case WMI_11AN_CAP:
3693 ht = true;
3694 case WMI_11A_CAP:
3695 band_5gig = true;
3696 break;
3697 case WMI_11GN_CAP:
3698 ht = true;
3699 case WMI_11G_CAP:
3700 band_2gig = true;
3701 break;
3702 case WMI_11AGN_CAP:
3703 ht = true;
3704 case WMI_11AG_CAP:
3705 band_2gig = true;
3706 band_5gig = true;
3707 break;
3708 default:
3709 ath6kl_err("invalid phy capability!\n");
3710 return -EINVAL;
3711 }
3712
Vasanthakumar Thiagarajan7fd1ce72012-04-25 12:38:18 +05303713 /*
3714 * Even if the fw has HT support, advertise HT cap only when
3715 * the firmware has support to override RSN capability, otherwise
3716 * 4-way handshake would fail.
3717 */
3718 if (!(ht &&
3719 test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
3720 ar->fw_capabilities))) {
Thomas Pedersend92917e2012-04-19 15:31:56 -07003721 ath6kl_band_2ghz.ht_cap.cap = 0;
3722 ath6kl_band_2ghz.ht_cap.ht_supported = false;
3723 ath6kl_band_5ghz.ht_cap.cap = 0;
3724 ath6kl_band_5ghz.ht_cap.ht_supported = false;
3725 }
Bala Shanmugam06e360a2012-05-22 13:23:12 +05303726
3727 if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) {
3728 ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3729 ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3730 ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
3731 ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff;
3732 } else {
3733 ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3734 ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3735 }
3736
Thomas Pedersend92917e2012-04-19 15:31:56 -07003737 if (band_2gig)
3738 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3739 if (band_5gig)
3740 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3741
Kalle Valo46d33a22012-01-17 20:08:40 +02003742 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3743
3744 wiphy->cipher_suites = cipher_suites;
3745 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3746
Johannes Bergdfb89c52012-06-27 09:23:48 +02003747#ifdef CONFIG_PM
Kalle Valo46d33a22012-01-17 20:08:40 +02003748 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3749 WIPHY_WOWLAN_DISCONNECT |
3750 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3751 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3752 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3753 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3754 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3755 wiphy->wowlan.pattern_min_len = 1;
3756 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
Johannes Bergdfb89c52012-06-27 09:23:48 +02003757#endif
Kalle Valo46d33a22012-01-17 20:08:40 +02003758
Jouni Malinen8ab54152012-05-09 22:14:51 +03003759 wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS;
Kalle Valo46d33a22012-01-17 20:08:40 +02003760
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303761 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3762 WIPHY_FLAG_HAVE_AP_SME |
3763 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3764 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3765
3766 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3767 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3768
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303769 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
3770 ar->fw_capabilities))
Johannes Bergb2922192012-10-12 10:55:53 +02003771 ar->wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303772
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303773 ar->wiphy->probe_resp_offload =
3774 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3775 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
Jouni Malinena432e7c2012-04-16 19:25:35 +03003776 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303777
Kalle Valo46d33a22012-01-17 20:08:40 +02003778 ret = wiphy_register(wiphy);
3779 if (ret < 0) {
3780 ath6kl_err("couldn't register wiphy device\n");
3781 return ret;
3782 }
3783
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303784 ar->wiphy_registered = true;
3785
Kalle Valo46d33a22012-01-17 20:08:40 +02003786 return 0;
3787}
3788
3789void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303790{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303791 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303792
3793 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003794}
Kalle Valo46d33a22012-01-17 20:08:40 +02003795
Kalle Valo45eaa782012-01-17 20:09:05 +02003796struct ath6kl *ath6kl_cfg80211_create(void)
3797{
3798 struct ath6kl *ar;
3799 struct wiphy *wiphy;
3800
3801 /* create a new wiphy for use with cfg80211 */
3802 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3803
3804 if (!wiphy) {
3805 ath6kl_err("couldn't allocate wiphy device\n");
3806 return NULL;
3807 }
3808
3809 ar = wiphy_priv(wiphy);
3810 ar->wiphy = wiphy;
3811
3812 return ar;
3813}
3814
3815/* Note: ar variable must not be accessed after calling this! */
3816void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3817{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303818 int i;
3819
3820 for (i = 0; i < AP_MAX_NUM_STA; i++)
3821 kfree(ar->sta_list[i].aggr_conn);
3822
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303823 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003824}
Kalle Valo45eaa782012-01-17 20:09:05 +02003825