blob: f620af5e0dbb34e9b991aa3a655c0fa42e4cae58 [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
Raja Manid91e8ee2012-01-30 17:13:10 +05302095static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
2096{
2097 struct in_device *in_dev;
2098 struct in_ifaddr *ifa;
2099 struct ath6kl_vif *vif;
Raja Mani055bde42012-03-21 15:03:37 +05302100 int ret;
Raja Manid91e8ee2012-01-30 17:13:10 +05302101 u32 filter = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +05302102 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05302103 u8 index = 0;
2104 __be32 ips[MAX_IP_ADDRS];
2105
Thomas Pedersen77ed4e42012-04-19 16:29:19 -07002106 /* The FW currently can't support multi-vif WoW properly. */
2107 if (ar->num_vif > 1)
2108 return -EIO;
2109
Raja Manid91e8ee2012-01-30 17:13:10 +05302110 vif = ath6kl_vif_first(ar);
2111 if (!vif)
2112 return -EIO;
2113
2114 if (!ath6kl_cfg80211_ready(vif))
2115 return -EIO;
2116
2117 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05302118 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05302119
2120 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
2121 return -EINVAL;
2122
Naveen Gangadharan6821d4f2012-05-11 14:19:09 -07002123 if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) &&
2124 test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
2125 ar->fw_capabilities)) {
Naveen Gangadharan6251d802012-04-20 12:46:56 -07002126 ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
2127 vif->fw_vif_idx, false);
2128 if (ret)
2129 return ret;
2130 }
2131
Raja Manid91e8ee2012-01-30 17:13:10 +05302132 /* Clear existing WOW patterns */
2133 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
2134 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
2135 WOW_LIST_ID, i);
2136
2137 /*
2138 * Skip the default WOW pattern configuration
2139 * if the driver receives any WOW patterns from
2140 * the user.
2141 */
2142 if (wow)
2143 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
2144 else if (vif->nw_type == AP_NETWORK)
2145 ret = ath6kl_wow_ap(ar, vif);
2146 else
2147 ret = ath6kl_wow_sta(ar, vif);
2148
2149 if (ret)
2150 return ret;
2151
Raja Mani390a8c82012-03-07 11:35:04 +05302152 netif_stop_queue(vif->ndev);
2153
Raja Manice0dc0c2012-02-20 19:08:08 +05302154 if (vif->nw_type != AP_NETWORK) {
2155 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2156 ATH6KL_MAX_WOW_LISTEN_INTL,
2157 0);
2158 if (ret)
2159 return ret;
2160
2161 /* Set listen interval x 15 times as bmiss time */
2162 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
2163 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
2164 bmiss_time = ATH6KL_MAX_BMISS_TIME;
2165
2166 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2167 bmiss_time, 0);
2168 if (ret)
2169 return ret;
2170
2171 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2172 0xFFFF, 0, 0xFFFF, 0, 0, 0,
2173 0, 0, 0, 0);
2174 if (ret)
2175 return ret;
2176 }
2177
Raja Mani390a8c82012-03-07 11:35:04 +05302178 ar->state = ATH6KL_STATE_SUSPENDING;
2179
Raja Manic08631c2011-12-16 14:24:24 +05302180 /* Setup own IP addr for ARP agent. */
2181 in_dev = __in_dev_get_rtnl(vif->ndev);
2182 if (!in_dev)
2183 goto skip_arp;
2184
2185 ifa = in_dev->ifa_list;
2186 memset(&ips, 0, sizeof(ips));
2187
2188 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2189 while (index < MAX_IP_ADDRS && ifa) {
2190 ips[index] = ifa->ifa_local;
2191 ifa = ifa->ifa_next;
2192 index++;
2193 }
2194
2195 if (ifa) {
2196 ath6kl_err("total IP addr count is exceeding fw limit\n");
2197 return -EINVAL;
2198 }
2199
2200 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2201 if (ret) {
2202 ath6kl_err("fail to setup ip for arp agent\n");
2203 return ret;
2204 }
2205
2206skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02002207 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2208 ATH6KL_WOW_MODE_ENABLE,
2209 filter,
2210 WOW_HOST_REQ_DELAY);
2211 if (ret)
2212 return ret;
2213
Raja Mani055bde42012-03-21 15:03:37 +05302214 ret = ath6kl_cfg80211_host_sleep(ar, vif);
Raja Mani6cb3c712011-11-07 22:52:45 +02002215 if (ret)
2216 return ret;
2217
Raja Mani055bde42012-03-21 15:03:37 +05302218 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002219}
2220
2221static int ath6kl_wow_resume(struct ath6kl *ar)
2222{
2223 struct ath6kl_vif *vif;
2224 int ret;
2225
2226 vif = ath6kl_vif_first(ar);
2227 if (!vif)
2228 return -EIO;
2229
Raja Mani390a8c82012-03-07 11:35:04 +05302230 ar->state = ATH6KL_STATE_RESUMING;
2231
Raja Mani6cb3c712011-11-07 22:52:45 +02002232 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2233 ATH6KL_HOST_MODE_AWAKE);
Raja Mani390a8c82012-03-07 11:35:04 +05302234 if (ret) {
Kalle Valocdeb8602012-04-12 11:02:18 +03002235 ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n",
2236 ret);
Raja Mani390a8c82012-03-07 11:35:04 +05302237 ar->state = ATH6KL_STATE_WOW;
2238 return ret;
2239 }
2240
Raja Manice0dc0c2012-02-20 19:08:08 +05302241 if (vif->nw_type != AP_NETWORK) {
2242 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2243 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2244 if (ret)
2245 return ret;
2246
2247 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2248 vif->listen_intvl_t, 0);
2249 if (ret)
2250 return ret;
2251
2252 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2253 vif->bmiss_time_t, 0);
2254 if (ret)
2255 return ret;
2256 }
2257
Raja Mani390a8c82012-03-07 11:35:04 +05302258 ar->state = ATH6KL_STATE_ON;
2259
Naveen Gangadharan6821d4f2012-05-11 14:19:09 -07002260 if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) &&
2261 test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
2262 ar->fw_capabilities)) {
Naveen Gangadharan6251d802012-04-20 12:46:56 -07002263 ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
2264 vif->fw_vif_idx, true);
2265 if (ret)
2266 return ret;
2267 }
2268
Raja Mani390a8c82012-03-07 11:35:04 +05302269 netif_wake_queue(vif->ndev);
2270
2271 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002272}
2273
Raja Mani40abc2d2012-03-21 15:03:38 +05302274static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
2275{
2276 struct ath6kl_vif *vif;
2277 int ret;
2278
2279 vif = ath6kl_vif_first(ar);
2280 if (!vif)
2281 return -EIO;
2282
Ming Jiang48f27582012-04-13 21:09:25 +08002283 if (!test_bit(WMI_READY, &ar->flag)) {
2284 ath6kl_err("deepsleep failed as wmi is not ready\n");
Raja Mani40abc2d2012-03-21 15:03:38 +05302285 return -EIO;
Ming Jiang48f27582012-04-13 21:09:25 +08002286 }
Raja Mani40abc2d2012-03-21 15:03:38 +05302287
2288 ath6kl_cfg80211_stop_all(ar);
2289
2290 /* Save the current power mode before enabling power save */
2291 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2292
2293 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2294 if (ret)
2295 return ret;
2296
2297 /* Disable WOW mode */
2298 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2299 ATH6KL_WOW_MODE_DISABLE,
2300 0, 0);
2301 if (ret)
2302 return ret;
2303
2304 /* Flush all non control pkts in TX path */
2305 ath6kl_tx_data_cleanup(ar);
2306
2307 ret = ath6kl_cfg80211_host_sleep(ar, vif);
2308 if (ret)
2309 return ret;
2310
2311 return 0;
2312}
2313
2314static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
2315{
2316 struct ath6kl_vif *vif;
2317 int ret;
2318
2319 vif = ath6kl_vif_first(ar);
2320
2321 if (!vif)
2322 return -EIO;
2323
2324 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2325 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2326 ar->wmi->saved_pwr_mode);
2327 if (ret)
2328 return ret;
2329 }
2330
2331 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2332 ATH6KL_HOST_MODE_AWAKE);
2333 if (ret)
2334 return ret;
2335
2336 ar->state = ATH6KL_STATE_ON;
2337
2338 /* Reset scan parameter to default values */
2339 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2340 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2341 if (ret)
2342 return ret;
2343
2344 return 0;
2345}
2346
Kalle Valo52d81a62011-11-01 08:44:21 +02002347int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002348 enum ath6kl_cfg_suspend_mode mode,
2349 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002350{
Vivek Natarajan3d794992012-03-28 19:21:26 +05302351 struct ath6kl_vif *vif;
Raja Mani390a8c82012-03-07 11:35:04 +05302352 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002353 int ret;
2354
Kalle Valo52d81a62011-11-01 08:44:21 +02002355 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002356 case ATH6KL_CFG_SUSPEND_WOW:
2357
2358 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2359
2360 /* Flush all non control pkts in TX path */
2361 ath6kl_tx_data_cleanup(ar);
2362
Raja Mani390a8c82012-03-07 11:35:04 +05302363 prev_state = ar->state;
2364
Raja Manid7c44e02011-11-07 22:52:46 +02002365 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302366 if (ret) {
2367 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002368 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302369 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302370
Raja Manid7c44e02011-11-07 22:52:46 +02002371 ar->state = ATH6KL_STATE_WOW;
2372 break;
2373
Kalle Valo52d81a62011-11-01 08:44:21 +02002374 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002375
Raja Mani40abc2d2012-03-21 15:03:38 +05302376 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
Raja Mani524441e2011-11-07 22:52:46 +02002377
Raja Mani40abc2d2012-03-21 15:03:38 +05302378 ret = ath6kl_cfg80211_deepsleep_suspend(ar);
Kalle Valo52d81a62011-11-01 08:44:21 +02002379 if (ret) {
Raja Mani40abc2d2012-03-21 15:03:38 +05302380 ath6kl_err("deepsleep suspend failed: %d\n", ret);
2381 return ret;
Kalle Valo52d81a62011-11-01 08:44:21 +02002382 }
2383
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002384 ar->state = ATH6KL_STATE_DEEPSLEEP;
2385
Kalle Valo52d81a62011-11-01 08:44:21 +02002386 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002387
2388 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002389
Kalle Valo7125f012011-12-13 14:51:37 +02002390 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002391
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002392 if (ar->state == ATH6KL_STATE_OFF) {
2393 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2394 "suspend hw off, no action for cutpower\n");
2395 break;
2396 }
2397
2398 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2399
2400 ret = ath6kl_init_hw_stop(ar);
2401 if (ret) {
2402 ath6kl_warn("failed to stop hw during suspend: %d\n",
2403 ret);
2404 }
2405
2406 ar->state = ATH6KL_STATE_CUTPOWER;
2407
2408 break;
2409
Kalle Valo10509f92011-12-13 14:52:07 +02002410 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2411 /*
2412 * Nothing needed for schedule scan, firmware is already in
2413 * wow mode and sleeping most of the time.
2414 */
2415 break;
2416
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002417 default:
2418 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002419 }
2420
Vivek Natarajan3d794992012-03-28 19:21:26 +05302421 list_for_each_entry(vif, &ar->vif_list, list)
2422 ath6kl_cfg80211_scan_complete_event(vif, true);
2423
Kalle Valo52d81a62011-11-01 08:44:21 +02002424 return 0;
2425}
Kalle Valod6a434d2012-01-17 20:09:36 +02002426EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002427
2428int ath6kl_cfg80211_resume(struct ath6kl *ar)
2429{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002430 int ret;
2431
2432 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002433 case ATH6KL_STATE_WOW:
2434 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2435
2436 ret = ath6kl_wow_resume(ar);
2437 if (ret) {
2438 ath6kl_warn("wow mode resume failed: %d\n", ret);
2439 return ret;
2440 }
2441
Raja Manid7c44e02011-11-07 22:52:46 +02002442 break;
2443
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002444 case ATH6KL_STATE_DEEPSLEEP:
Raja Mani40abc2d2012-03-21 15:03:38 +05302445 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
2446
2447 ret = ath6kl_cfg80211_deepsleep_resume(ar);
2448 if (ret) {
2449 ath6kl_warn("deep sleep resume failed: %d\n", ret);
2450 return ret;
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002451 }
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002452 break;
2453
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002454 case ATH6KL_STATE_CUTPOWER:
2455 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2456
2457 ret = ath6kl_init_hw_start(ar);
2458 if (ret) {
2459 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2460 return ret;
2461 }
Raja Manid7c44e02011-11-07 22:52:46 +02002462 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002463
Kalle Valo10509f92011-12-13 14:52:07 +02002464 case ATH6KL_STATE_SCHED_SCAN:
2465 break;
2466
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002467 default:
2468 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002469 }
2470
2471 return 0;
2472}
Kalle Valod6a434d2012-01-17 20:09:36 +02002473EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002474
Kalle Valoabcb3442011-07-22 08:26:20 +03002475#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002476
2477/* hif layer decides what suspend mode to use */
2478static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002479 struct cfg80211_wowlan *wow)
2480{
2481 struct ath6kl *ar = wiphy_priv(wiphy);
2482
Raja Mani0f60e9f2011-11-07 22:52:45 +02002483 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002484}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002485
Kalle Valo52d81a62011-11-01 08:44:21 +02002486static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002487{
2488 struct ath6kl *ar = wiphy_priv(wiphy);
2489
2490 return ath6kl_hif_resume(ar);
2491}
Raja Mania918fb32011-11-07 22:52:46 +02002492
2493/*
2494 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2495 * both sdio irq wake up and keep power. The target pulls sdio data line to
2496 * wake up the host when WOW pattern matches. This causes sdio irq handler
2497 * is being called in the host side which internally hits ath6kl's RX path.
2498 *
2499 * Since sdio interrupt is not disabled, RX path executes even before
2500 * the host executes the actual resume operation from PM module.
2501 *
2502 * In the current scenario, WOW resume should happen before start processing
2503 * any data from the target. So It's required to perform WOW resume in RX path.
2504 * Ideally we should perform WOW resume only in the actual platform
2505 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2506 *
2507 * ath6kl_check_wow_status() is called from ath6kl_rx().
2508 */
2509void ath6kl_check_wow_status(struct ath6kl *ar)
2510{
Raja Mani390a8c82012-03-07 11:35:04 +05302511 if (ar->state == ATH6KL_STATE_SUSPENDING)
2512 return;
2513
Raja Mania918fb32011-11-07 22:52:46 +02002514 if (ar->state == ATH6KL_STATE_WOW)
2515 ath6kl_cfg80211_resume(ar);
2516}
2517
2518#else
2519
2520void ath6kl_check_wow_status(struct ath6kl *ar)
2521{
2522}
Kalle Valoabcb3442011-07-22 08:26:20 +03002523#endif
2524
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302525static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
2526 bool ht_enable)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002527{
Kiran Reddy67b3f122012-05-29 11:12:50 -07002528 struct ath6kl_htcap *htcap = &vif->htcap[band];
Sujith Manoharane68f6752011-12-22 12:15:27 +05302529
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302530 if (htcap->ht_enable == ht_enable)
2531 return 0;
Sujith Manoharane68f6752011-12-22 12:15:27 +05302532
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302533 if (ht_enable) {
2534 /* Set default ht capabilities */
2535 htcap->ht_enable = true;
2536 htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ?
2537 ath6kl_g_htcap : ath6kl_a_htcap;
2538 htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
2539 } else /* Disable ht */
2540 memset(htcap, 0, sizeof(*htcap));
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002541
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302542 return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx,
2543 band, htcap);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002544}
2545
Thomas Pedersen37a2f952012-04-19 15:31:57 -07002546static int ath6kl_restore_htcap(struct ath6kl_vif *vif)
2547{
2548 struct wiphy *wiphy = vif->ar->wiphy;
2549 int band, ret = 0;
2550
2551 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2552 if (!wiphy->bands[band])
2553 continue;
2554
2555 ret = ath6kl_set_htcap(vif, band,
2556 wiphy->bands[band]->ht_cap.ht_supported);
2557 if (ret)
2558 return ret;
2559 }
2560
2561 return ret;
2562}
2563
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002564static bool ath6kl_is_p2p_ie(const u8 *pos)
2565{
2566 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2567 pos[2] == 0x50 && pos[3] == 0x6f &&
2568 pos[4] == 0x9a && pos[5] == 0x09;
2569}
2570
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302571static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2572 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002573{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302574 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002575 const u8 *pos;
2576 u8 *buf = NULL;
2577 size_t len = 0;
2578 int ret;
2579
2580 /*
2581 * Filter out P2P IE(s) since they will be included depending on
2582 * the Probe Request frame in ath6kl_send_go_probe_resp().
2583 */
2584
2585 if (ies && ies_len) {
2586 buf = kmalloc(ies_len, GFP_KERNEL);
2587 if (buf == NULL)
2588 return -ENOMEM;
2589 pos = ies;
2590 while (pos + 1 < ies + ies_len) {
2591 if (pos + 2 + pos[1] > ies + ies_len)
2592 break;
2593 if (!ath6kl_is_p2p_ie(pos)) {
2594 memcpy(buf + len, pos, 2 + pos[1]);
2595 len += 2 + pos[1];
2596 }
2597 pos += 2 + pos[1];
2598 }
2599 }
2600
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302601 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2602 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002603 kfree(buf);
2604 return ret;
2605}
2606
Johannes Berg88600202012-02-13 15:17:18 +01002607static int ath6kl_set_ies(struct ath6kl_vif *vif,
2608 struct cfg80211_beacon_data *info)
2609{
2610 struct ath6kl *ar = vif->ar;
2611 int res;
2612
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002613 /* this also clears IE in fw if it's not set */
2614 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2615 WMI_FRAME_BEACON,
2616 info->beacon_ies,
2617 info->beacon_ies_len);
2618 if (res)
2619 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002620
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002621 /* this also clears IE in fw if it's not set */
2622 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2623 info->proberesp_ies_len);
2624 if (res)
2625 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002626
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002627 /* this also clears IE in fw if it's not set */
2628 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2629 WMI_FRAME_ASSOC_RESP,
2630 info->assocresp_ies,
2631 info->assocresp_ies_len);
2632 if (res)
2633 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002634
2635 return 0;
2636}
2637
Thomas Pedersenc422d52d2012-05-15 00:09:23 -07002638void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable)
2639{
2640 int err;
2641
2642 if (WARN_ON(!test_bit(WMI_READY, &vif->ar->flag)))
2643 return;
2644
2645 if (vif->nw_type != INFRA_NETWORK)
2646 return;
2647
2648 if (!test_bit(ATH6KL_FW_CAPABILITY_BMISS_ENHANCE,
2649 vif->ar->fw_capabilities))
2650 return;
2651
2652 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s fw bmiss enhance\n",
2653 enable ? "enable" : "disable");
2654
2655 err = ath6kl_wmi_sta_bmiss_enhance_cmd(vif->ar->wmi,
2656 vif->fw_vif_idx, enable);
2657 if (err)
2658 ath6kl_err("failed to %s enhanced bmiss detection: %d\n",
2659 enable ? "enable" : "disable", err);
2660}
2661
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302662static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
2663 u8 *rsn_capab)
2664{
2665 const u8 *rsn_ie;
2666 size_t rsn_ie_len;
2667 u16 cnt;
2668
2669 if (!beacon->tail)
2670 return -EINVAL;
2671
2672 rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len);
2673 if (!rsn_ie)
2674 return -EINVAL;
2675
2676 rsn_ie_len = *(rsn_ie + 1);
2677 /* skip element id and length */
2678 rsn_ie += 2;
2679
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302680 /* skip version */
2681 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302682 return -EINVAL;
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302683 rsn_ie += 2;
2684 rsn_ie_len -= 2;
2685
2686 /* skip group cipher suite */
2687 if (rsn_ie_len < 4)
2688 return 0;
2689 rsn_ie += 4;
2690 rsn_ie_len -= 4;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302691
2692 /* skip pairwise cipher suite */
2693 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302694 return 0;
Vasanthakumar Thiagarajan798985c2012-04-10 13:35:47 +05302695 cnt = get_unaligned_le16(rsn_ie);
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302696 rsn_ie += (2 + cnt * 4);
2697 rsn_ie_len -= (2 + cnt * 4);
2698
2699 /* skip akm suite */
2700 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302701 return 0;
Vasanthakumar Thiagarajan798985c2012-04-10 13:35:47 +05302702 cnt = get_unaligned_le16(rsn_ie);
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302703 rsn_ie += (2 + cnt * 4);
2704 rsn_ie_len -= (2 + cnt * 4);
2705
2706 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302707 return 0;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302708
2709 memcpy(rsn_capab, rsn_ie, 2);
2710
2711 return 0;
2712}
2713
Johannes Berg88600202012-02-13 15:17:18 +01002714static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2715 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002716{
2717 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302718 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002719 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002720 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002721 u8 *ies;
2722 int ies_len;
2723 struct wmi_connect_cmd p;
2724 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302725 int i, ret;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302726 u16 rsn_capab = 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002727
Johannes Berg88600202012-02-13 15:17:18 +01002728 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002729
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302730 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002731 return -EIO;
2732
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302733 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002734 return -EOPNOTSUPP;
2735
Johannes Berg88600202012-02-13 15:17:18 +01002736 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002737
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002738 ar->ap_mode_bkey.valid = false;
2739
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002740 /* TODO:
2741 * info->interval
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002742 */
2743
Etay Luzd154f322012-05-30 11:35:08 +03002744 ret = ath6kl_wmi_ap_set_dtim_cmd(ar->wmi, vif->fw_vif_idx,
2745 info->dtim_period);
2746
2747 /* ignore error, just print a warning and continue normally */
2748 if (ret)
2749 ath6kl_warn("Failed to set dtim_period in beacon: %d\n", ret);
2750
Johannes Berg88600202012-02-13 15:17:18 +01002751 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002752 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002753 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002754 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002755 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002756 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002757 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002758
2759 if (info->ssid == NULL)
2760 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302761 memcpy(vif->ssid, info->ssid, info->ssid_len);
2762 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002763 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002764 hidden = true;
2765
2766 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2767 if (res)
2768 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002769
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302770 ret = ath6kl_set_auth_type(vif, info->auth_type);
2771 if (ret)
2772 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002773
2774 memset(&p, 0, sizeof(p));
2775
2776 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2777 switch (info->crypto.akm_suites[i]) {
2778 case WLAN_AKM_SUITE_8021X:
2779 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2780 p.auth_mode |= WPA_AUTH;
2781 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2782 p.auth_mode |= WPA2_AUTH;
2783 break;
2784 case WLAN_AKM_SUITE_PSK:
2785 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2786 p.auth_mode |= WPA_PSK_AUTH;
2787 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2788 p.auth_mode |= WPA2_PSK_AUTH;
2789 break;
2790 }
2791 }
2792 if (p.auth_mode == 0)
2793 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302794 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002795
2796 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2797 switch (info->crypto.ciphers_pairwise[i]) {
2798 case WLAN_CIPHER_SUITE_WEP40:
2799 case WLAN_CIPHER_SUITE_WEP104:
2800 p.prwise_crypto_type |= WEP_CRYPT;
2801 break;
2802 case WLAN_CIPHER_SUITE_TKIP:
2803 p.prwise_crypto_type |= TKIP_CRYPT;
2804 break;
2805 case WLAN_CIPHER_SUITE_CCMP:
2806 p.prwise_crypto_type |= AES_CRYPT;
2807 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002808 case WLAN_CIPHER_SUITE_SMS4:
2809 p.prwise_crypto_type |= WAPI_CRYPT;
2810 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002811 }
2812 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002813 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002814 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302815 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002816 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302817 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002818
2819 switch (info->crypto.cipher_group) {
2820 case WLAN_CIPHER_SUITE_WEP40:
2821 case WLAN_CIPHER_SUITE_WEP104:
2822 p.grp_crypto_type = WEP_CRYPT;
2823 break;
2824 case WLAN_CIPHER_SUITE_TKIP:
2825 p.grp_crypto_type = TKIP_CRYPT;
2826 break;
2827 case WLAN_CIPHER_SUITE_CCMP:
2828 p.grp_crypto_type = AES_CRYPT;
2829 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002830 case WLAN_CIPHER_SUITE_SMS4:
2831 p.grp_crypto_type = WAPI_CRYPT;
2832 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002833 default:
2834 p.grp_crypto_type = NONE_CRYPT;
2835 break;
2836 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302837 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002838
2839 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302840 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002841
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302842 p.ssid_len = vif->ssid_len;
2843 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2844 p.dot11_auth_mode = vif->dot11_auth_mode;
Johannes Bergaa430da2012-05-16 23:50:18 +02002845 p.ch = cpu_to_le16(info->channel->center_freq);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002846
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302847 /* Enable uAPSD support by default */
2848 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2849 if (res < 0)
2850 return res;
2851
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002852 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2853 p.nw_subtype = SUBTYPE_P2PGO;
2854 } else {
2855 /*
2856 * Due to firmware limitation, it is not possible to
2857 * do P2P mgmt operations in AP mode
2858 */
2859 p.nw_subtype = SUBTYPE_NONE;
2860 }
2861
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05302862 if (info->inactivity_timeout) {
2863 res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
2864 info->inactivity_timeout);
2865 if (res < 0)
2866 return res;
2867 }
2868
Johannes Bergaa430da2012-05-16 23:50:18 +02002869 if (ath6kl_set_htcap(vif, info->channel->band,
2870 info->channel_type != NL80211_CHAN_NO_HT))
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302871 return -EIO;
2872
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302873 /*
2874 * Get the PTKSA replay counter in the RSN IE. Supplicant
2875 * will use the RSN IE in M3 message and firmware has to
2876 * advertise the same in beacon/probe response. Send
2877 * the complete RSN IE capability field to firmware
2878 */
2879 if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) &&
2880 test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
2881 ar->fw_capabilities)) {
2882 res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
2883 WLAN_EID_RSN, WMI_RSN_IE_CAPB,
2884 (const u8 *) &rsn_capab,
2885 sizeof(rsn_capab));
2886 if (res < 0)
2887 return res;
2888 }
2889
Thomas Pedersenc4f78632012-04-06 13:35:48 -07002890 memcpy(&vif->profile, &p, sizeof(p));
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302891 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002892 if (res < 0)
2893 return res;
2894
2895 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002896}
2897
Johannes Berg88600202012-02-13 15:17:18 +01002898static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2899 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002900{
Johannes Berg88600202012-02-13 15:17:18 +01002901 struct ath6kl_vif *vif = netdev_priv(dev);
2902
2903 if (!ath6kl_cfg80211_ready(vif))
2904 return -EIO;
2905
2906 if (vif->next_mode != AP_NETWORK)
2907 return -EOPNOTSUPP;
2908
2909 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002910}
2911
Johannes Berg88600202012-02-13 15:17:18 +01002912static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002913{
2914 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302915 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002916
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302917 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002918 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302919 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002920 return -ENOTCONN;
2921
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302922 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302923 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002924
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302925 /* Restore ht setting in firmware */
Thomas Pedersen37a2f952012-04-19 15:31:57 -07002926 return ath6kl_restore_htcap(vif);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002927}
2928
Jouni Malinen33e53082011-12-27 11:02:56 +02002929static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2930
2931static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2932 u8 *mac)
2933{
2934 struct ath6kl *ar = ath6kl_priv(dev);
2935 struct ath6kl_vif *vif = netdev_priv(dev);
2936 const u8 *addr = mac ? mac : bcast_addr;
2937
2938 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2939 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2940}
2941
Jouni Malinen23875132011-08-30 21:57:53 +03002942static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2943 u8 *mac, struct station_parameters *params)
2944{
2945 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302946 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002947
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302948 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002949 return -EOPNOTSUPP;
2950
2951 /* Use this only for authorizing/unauthorizing a station */
2952 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2953 return -EOPNOTSUPP;
2954
2955 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302956 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2957 WMI_AP_MLME_AUTHORIZE, mac, 0);
2958 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2959 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002960}
2961
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002962static int ath6kl_remain_on_channel(struct wiphy *wiphy,
Johannes Berg71bbc992012-06-15 15:30:18 +02002963 struct wireless_dev *wdev,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002964 struct ieee80211_channel *chan,
2965 enum nl80211_channel_type channel_type,
2966 unsigned int duration,
2967 u64 *cookie)
2968{
Johannes Berg71bbc992012-06-15 15:30:18 +02002969 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
2970 struct ath6kl *ar = ath6kl_priv(vif->ndev);
Jouni Malinen10522612011-10-27 16:00:13 +03002971 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002972
2973 /* TODO: if already pending or ongoing remain-on-channel,
2974 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002975 id = ++vif->last_roc_id;
2976 if (id == 0) {
2977 /* Do not use 0 as the cookie value */
2978 id = ++vif->last_roc_id;
2979 }
2980 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002981
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302982 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2983 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002984}
2985
2986static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
Johannes Berg71bbc992012-06-15 15:30:18 +02002987 struct wireless_dev *wdev,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002988 u64 cookie)
2989{
Johannes Berg71bbc992012-06-15 15:30:18 +02002990 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
2991 struct ath6kl *ar = ath6kl_priv(vif->ndev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002992
Jouni Malinen10522612011-10-27 16:00:13 +03002993 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002994 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002995 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002996
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302997 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002998}
2999
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303000static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
3001 const u8 *buf, size_t len,
3002 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003003{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303004 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003005 const u8 *pos;
3006 u8 *p2p;
3007 int p2p_len;
3008 int ret;
3009 const struct ieee80211_mgmt *mgmt;
3010
3011 mgmt = (const struct ieee80211_mgmt *) buf;
3012
3013 /* Include P2P IE(s) from the frame generated in user space. */
3014
3015 p2p = kmalloc(len, GFP_KERNEL);
3016 if (p2p == NULL)
3017 return -ENOMEM;
3018 p2p_len = 0;
3019
3020 pos = mgmt->u.probe_resp.variable;
3021 while (pos + 1 < buf + len) {
3022 if (pos + 2 + pos[1] > buf + len)
3023 break;
3024 if (ath6kl_is_p2p_ie(pos)) {
3025 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
3026 p2p_len += 2 + pos[1];
3027 }
3028 pos += 2 + pos[1];
3029 }
3030
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303031 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
3032 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003033 kfree(p2p);
3034 return ret;
3035}
3036
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003037static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
3038 u32 id,
3039 u32 freq,
3040 u32 wait,
3041 const u8 *buf,
3042 size_t len,
3043 bool *more_data,
3044 bool no_cck)
3045{
3046 struct ieee80211_mgmt *mgmt;
3047 struct ath6kl_sta *conn;
3048 bool is_psq_empty = false;
3049 struct ath6kl_mgmt_buff *mgmt_buf;
3050 size_t mgmt_buf_size;
3051 struct ath6kl *ar = vif->ar;
3052
3053 mgmt = (struct ieee80211_mgmt *) buf;
3054 if (is_multicast_ether_addr(mgmt->da))
3055 return false;
3056
3057 conn = ath6kl_find_sta(vif, mgmt->da);
3058 if (!conn)
3059 return false;
3060
3061 if (conn->sta_flags & STA_PS_SLEEP) {
3062 if (!(conn->sta_flags & STA_PS_POLLED)) {
3063 /* Queue the frames if the STA is sleeping */
3064 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
3065 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
3066 if (!mgmt_buf)
3067 return false;
3068
3069 INIT_LIST_HEAD(&mgmt_buf->list);
3070 mgmt_buf->id = id;
3071 mgmt_buf->freq = freq;
3072 mgmt_buf->wait = wait;
3073 mgmt_buf->len = len;
3074 mgmt_buf->no_cck = no_cck;
3075 memcpy(mgmt_buf->buf, buf, len);
3076 spin_lock_bh(&conn->psq_lock);
3077 is_psq_empty = skb_queue_empty(&conn->psq) &&
3078 (conn->mgmt_psq_len == 0);
3079 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
3080 conn->mgmt_psq_len++;
3081 spin_unlock_bh(&conn->psq_lock);
3082
3083 /*
3084 * If this is the first pkt getting queued
3085 * for this STA, update the PVB for this
3086 * STA.
3087 */
3088 if (is_psq_empty)
3089 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
3090 conn->aid, 1);
3091 return true;
3092 }
3093
3094 /*
3095 * This tx is because of a PsPoll.
3096 * Determine if MoreData bit has to be set.
3097 */
3098 spin_lock_bh(&conn->psq_lock);
3099 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
3100 *more_data = true;
3101 spin_unlock_bh(&conn->psq_lock);
3102 }
3103
3104 return false;
3105}
3106
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003107/* Check if SSID length is greater than DIRECT- */
3108static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
3109{
3110 const struct ieee80211_mgmt *mgmt;
3111 mgmt = (const struct ieee80211_mgmt *) buf;
3112
3113 /* variable[1] contains the SSID tag length */
3114 if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
3115 (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
3116 return true;
3117 }
3118
3119 return false;
3120}
3121
Johannes Berg71bbc992012-06-15 15:30:18 +02003122static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003123 struct ieee80211_channel *chan, bool offchan,
3124 enum nl80211_channel_type channel_type,
3125 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01003126 const u8 *buf, size_t len, bool no_cck,
3127 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003128{
Johannes Berg71bbc992012-06-15 15:30:18 +02003129 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
3130 struct ath6kl *ar = ath6kl_priv(vif->ndev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003131 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003132 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003133 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003134
3135 mgmt = (const struct ieee80211_mgmt *) buf;
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003136 if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
3137 ieee80211_is_probe_resp(mgmt->frame_control) &&
3138 ath6kl_is_p2p_go_ssid(buf, len)) {
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003139 /*
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003140 * Send Probe Response frame in GO mode using a separate WMI
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003141 * command to allow the target to fill in the generic IEs.
3142 */
3143 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303144 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003145 chan->center_freq);
3146 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003147
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303148 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003149 if (id == 0) {
3150 /*
3151 * 0 is a reserved value in the WMI command and shall not be
3152 * used for the command.
3153 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303154 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003155 }
3156
3157 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003158
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003159 /* AP mode Power saving processing */
3160 if (vif->nw_type == AP_NETWORK) {
3161 queued = ath6kl_mgmt_powersave_ap(vif,
3162 id, chan->center_freq,
3163 wait, buf,
3164 len, &more_data, no_cck);
3165 if (queued)
3166 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003167 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003168
3169 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
3170 chan->center_freq, wait,
3171 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003172}
3173
Jouni Malinenae32c302011-08-30 21:58:01 +03003174static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
Johannes Berg71bbc992012-06-15 15:30:18 +02003175 struct wireless_dev *wdev,
Jouni Malinenae32c302011-08-30 21:58:01 +03003176 u16 frame_type, bool reg)
3177{
Johannes Berg71bbc992012-06-15 15:30:18 +02003178 struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
Jouni Malinenae32c302011-08-30 21:58:01 +03003179
3180 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
3181 __func__, frame_type, reg);
3182 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
3183 /*
3184 * Note: This notification callback is not allowed to sleep, so
3185 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
3186 * hardcode target to report Probe Request frames all the time.
3187 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303188 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03003189 }
3190}
3191
Kalle Valo10509f92011-12-13 14:52:07 +02003192static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
3193 struct net_device *dev,
3194 struct cfg80211_sched_scan_request *request)
3195{
3196 struct ath6kl *ar = ath6kl_priv(dev);
3197 struct ath6kl_vif *vif = netdev_priv(dev);
3198 u16 interval;
Thomas Pedersen85b20fc2012-06-21 12:50:08 -07003199 int ret, rssi_thold;
Kalle Valo10509f92011-12-13 14:52:07 +02003200
3201 if (ar->state != ATH6KL_STATE_ON)
3202 return -EIO;
3203
3204 if (vif->sme_state != SME_DISCONNECTED)
3205 return -EBUSY;
3206
Thomas Pedersen77ed4e42012-04-19 16:29:19 -07003207 /* The FW currently can't support multi-vif WoW properly. */
3208 if (ar->num_vif > 1)
3209 return -EIO;
3210
Kalle Valob4d13d32012-03-21 10:01:09 +02003211 ath6kl_cfg80211_scan_complete_event(vif, true);
3212
Jouni Malinen3b8ffc62012-04-16 19:28:03 +03003213 ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
Naveen Singhdd45b752012-05-16 13:29:00 +03003214 request->n_ssids,
3215 request->match_sets,
3216 request->n_match_sets);
Jouni Malinen3b8ffc62012-04-16 19:28:03 +03003217 if (ret < 0)
3218 return ret;
Kalle Valo10509f92011-12-13 14:52:07 +02003219
Naveen Singhdd45b752012-05-16 13:29:00 +03003220 if (!request->n_match_sets) {
3221 ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
3222 ALL_BSS_FILTER, 0);
3223 if (ret < 0)
3224 return ret;
3225 } else {
3226 ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
3227 MATCHED_SSID_FILTER, 0);
3228 if (ret < 0)
3229 return ret;
3230 }
3231
Thomas Pedersen85b20fc2012-06-21 12:50:08 -07003232 if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD,
3233 ar->fw_capabilities)) {
3234 if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
3235 rssi_thold = 0;
3236 else if (request->rssi_thold < -127)
3237 rssi_thold = -127;
3238 else
3239 rssi_thold = request->rssi_thold;
3240
3241 ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx,
3242 rssi_thold);
3243 if (ret) {
3244 ath6kl_err("failed to set RSSI threshold for scan\n");
3245 return ret;
3246 }
3247 }
3248
Kalle Valo10509f92011-12-13 14:52:07 +02003249 /* fw uses seconds, also make sure that it's >0 */
3250 interval = max_t(u16, 1, request->interval / 1000);
3251
3252 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
3253 interval, interval,
Subramania Sharma Thandaveswarand472b5e2012-04-16 16:09:57 +05303254 vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
Kalle Valo10509f92011-12-13 14:52:07 +02003255
Kalle Valo10509f92011-12-13 14:52:07 +02003256 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
3257 ATH6KL_WOW_MODE_ENABLE,
3258 WOW_FILTER_SSID,
3259 WOW_HOST_REQ_DELAY);
3260 if (ret) {
3261 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
3262 return ret;
3263 }
3264
3265 /* this also clears IE in fw if it's not set */
3266 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
3267 WMI_FRAME_PROBE_REQ,
3268 request->ie, request->ie_len);
3269 if (ret) {
Joe Perchesf1ff32e2012-05-30 01:58:39 -07003270 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d\n",
Kalle Valo10509f92011-12-13 14:52:07 +02003271 ret);
3272 return ret;
3273 }
3274
3275 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
3276 ATH6KL_HOST_MODE_ASLEEP);
3277 if (ret) {
3278 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
3279 ret);
3280 return ret;
3281 }
3282
3283 ar->state = ATH6KL_STATE_SCHED_SCAN;
3284
3285 return ret;
3286}
3287
3288static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
3289 struct net_device *dev)
3290{
3291 struct ath6kl_vif *vif = netdev_priv(dev);
3292 bool stopped;
3293
3294 stopped = __ath6kl_cfg80211_sscan_stop(vif);
3295
3296 if (!stopped)
3297 return -EIO;
3298
3299 return 0;
3300}
3301
Bala Shanmugam06e360a2012-05-22 13:23:12 +05303302static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy,
3303 struct net_device *dev,
3304 const u8 *addr,
3305 const struct cfg80211_bitrate_mask *mask)
3306{
3307 struct ath6kl *ar = ath6kl_priv(dev);
3308 struct ath6kl_vif *vif = netdev_priv(dev);
3309
3310 return ath6kl_wmi_set_bitrate_mask(ar->wmi, vif->fw_vif_idx,
3311 mask);
3312}
3313
Thomas Pedersen279b2862012-07-17 19:39:55 -07003314static int ath6kl_cfg80211_set_txe_config(struct wiphy *wiphy,
3315 struct net_device *dev,
3316 u32 rate, u32 pkts, u32 intvl)
3317{
3318 struct ath6kl *ar = ath6kl_priv(dev);
3319 struct ath6kl_vif *vif = netdev_priv(dev);
3320
3321 if (vif->nw_type != INFRA_NETWORK ||
3322 !test_bit(ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, ar->fw_capabilities))
3323 return -EOPNOTSUPP;
3324
3325 if (vif->sme_state != SME_CONNECTED)
3326 return -ENOTCONN;
3327
3328 /* save this since the firmware won't report the interval */
3329 vif->txe_intvl = intvl;
3330
3331 return ath6kl_wmi_set_txe_notify(ar->wmi, vif->fw_vif_idx,
3332 rate, pkts, intvl);
3333}
3334
Jouni Malinenf80574a2011-08-30 21:58:04 +03003335static const struct ieee80211_txrx_stypes
3336ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
3337 [NL80211_IFTYPE_STATION] = {
3338 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3339 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3340 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3341 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3342 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02003343 [NL80211_IFTYPE_AP] = {
3344 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3345 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3346 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3347 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3348 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03003349 [NL80211_IFTYPE_P2P_CLIENT] = {
3350 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3351 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3352 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3353 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3354 },
3355 [NL80211_IFTYPE_P2P_GO] = {
3356 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3357 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3358 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3359 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3360 },
3361};
3362
Kalle Valobdcd8172011-07-18 00:22:30 +03003363static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303364 .add_virtual_intf = ath6kl_cfg80211_add_iface,
3365 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03003366 .change_virtual_intf = ath6kl_cfg80211_change_iface,
3367 .scan = ath6kl_cfg80211_scan,
3368 .connect = ath6kl_cfg80211_connect,
3369 .disconnect = ath6kl_cfg80211_disconnect,
3370 .add_key = ath6kl_cfg80211_add_key,
3371 .get_key = ath6kl_cfg80211_get_key,
3372 .del_key = ath6kl_cfg80211_del_key,
3373 .set_default_key = ath6kl_cfg80211_set_default_key,
3374 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
3375 .set_tx_power = ath6kl_cfg80211_set_txpower,
3376 .get_tx_power = ath6kl_cfg80211_get_txpower,
3377 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
3378 .join_ibss = ath6kl_cfg80211_join_ibss,
3379 .leave_ibss = ath6kl_cfg80211_leave_ibss,
3380 .get_station = ath6kl_get_station,
3381 .set_pmksa = ath6kl_set_pmksa,
3382 .del_pmksa = ath6kl_del_pmksa,
3383 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03003384 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03003385#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02003386 .suspend = __ath6kl_cfg80211_suspend,
3387 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03003388#endif
Johannes Berg88600202012-02-13 15:17:18 +01003389 .start_ap = ath6kl_start_ap,
3390 .change_beacon = ath6kl_change_beacon,
3391 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02003392 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03003393 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003394 .remain_on_channel = ath6kl_remain_on_channel,
3395 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003396 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03003397 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02003398 .sched_scan_start = ath6kl_cfg80211_sscan_start,
3399 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Bala Shanmugam06e360a2012-05-22 13:23:12 +05303400 .set_bitrate_mask = ath6kl_cfg80211_set_bitrate,
Thomas Pedersen279b2862012-07-17 19:39:55 -07003401 .set_cqm_txe_config = ath6kl_cfg80211_set_txe_config,
Kalle Valobdcd8172011-07-18 00:22:30 +03003402};
3403
Kalle Valo7125f012011-12-13 14:51:37 +02003404void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02003405{
Kalle Valo10509f92011-12-13 14:52:07 +02003406 ath6kl_cfg80211_sscan_disable(vif);
3407
Kalle Valoec4b7f62011-11-01 08:44:04 +02003408 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02003409 case SME_DISCONNECTED:
3410 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02003411 case SME_CONNECTING:
3412 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
3413 NULL, 0,
3414 WLAN_STATUS_UNSPECIFIED_FAILURE,
3415 GFP_KERNEL);
3416 break;
3417 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02003418 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
3419 break;
3420 }
3421
3422 if (test_bit(CONNECTED, &vif->flags) ||
3423 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02003424 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003425
3426 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02003427 clear_bit(CONNECTED, &vif->flags);
3428 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003429
3430 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02003431 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
3432 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
3433 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003434
3435 ath6kl_cfg80211_scan_complete_event(vif, true);
3436}
3437
Kalle Valo7125f012011-12-13 14:51:37 +02003438void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3439{
3440 struct ath6kl_vif *vif;
3441
3442 vif = ath6kl_vif_first(ar);
3443 if (!vif) {
3444 /* save the current power mode before enabling power save */
3445 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3446
3447 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
Kalle Valocdeb8602012-04-12 11:02:18 +03003448 ath6kl_warn("ath6kl_deep_sleep_enable: wmi_powermode_cmd failed\n");
Kalle Valo7125f012011-12-13 14:51:37 +02003449 return;
3450 }
3451
3452 /*
3453 * FIXME: we should take ar->list_lock to protect changes in the
3454 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3455 * sleeps.
3456 */
3457 list_for_each_entry(vif, &ar->vif_list, list)
3458 ath6kl_cfg80211_stop(vif);
3459}
3460
Kalle Valo84841ba2012-07-19 16:00:56 +03003461static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy,
3462 struct regulatory_request *request)
3463{
3464 struct ath6kl *ar = wiphy_priv(wiphy);
3465 u32 rates[IEEE80211_NUM_BANDS];
3466 int ret, i;
3467
3468 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
3469 "cfg reg_notify %c%c%s%s initiator %d\n",
3470 request->alpha2[0], request->alpha2[1],
3471 request->intersect ? " intersect" : "",
3472 request->processed ? " processed" : "",
3473 request->initiator);
3474
3475 ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2);
3476 if (ret) {
3477 ath6kl_err("failed to set regdomain: %d\n", ret);
3478 return ret;
3479 }
3480
3481 /*
3482 * Firmware will apply the regdomain change only after a scan is
3483 * issued and it will send a WMI_REGDOMAIN_EVENTID when it has been
3484 * changed.
3485 */
3486
3487 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
3488 if (wiphy->bands[i])
3489 rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
3490
3491
3492 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, 0, WMI_LONG_SCAN, false,
3493 false, 0, ATH6KL_FG_SCAN_INTERVAL,
3494 0, NULL, false, rates);
3495 if (ret) {
3496 ath6kl_err("failed to start scan for a regdomain change: %d\n",
3497 ret);
3498 return ret;
3499 }
3500
3501 return 0;
3502}
3503
Kalle Valoc25889e2012-01-17 20:08:27 +02003504static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003505{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303506 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303507 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303508 ath6kl_err("failed to initialize aggr\n");
3509 return -ENOMEM;
3510 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003511
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303512 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303513 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003514 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3515 (unsigned long) vif);
3516
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303517 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303518 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303519
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303520 INIT_LIST_HEAD(&vif->mc_filter);
3521
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303522 return 0;
3523}
3524
Kalle Valoc25889e2012-01-17 20:08:27 +02003525void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303526{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303527 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303528 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303529
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303530 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303531
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303532 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3533
3534 if (vif->nw_type == ADHOC_NETWORK)
3535 ar->ibss_if_active = false;
3536
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303537 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3538 list_del(&mc_filter->list);
3539 kfree(mc_filter);
3540 }
3541
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303542 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303543
3544 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303545}
3546
Johannes Berg552bff02012-09-19 09:26:06 +02003547struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
Johannes Berg84efbb82012-06-16 00:00:26 +02003548 enum nl80211_iftype type,
3549 u8 fw_vif_idx, u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303550{
3551 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303552 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303553
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303554 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303555 if (!ndev)
3556 return NULL;
3557
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303558 vif = netdev_priv(ndev);
3559 ndev->ieee80211_ptr = &vif->wdev;
3560 vif->wdev.wiphy = ar->wiphy;
3561 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303562 vif->ndev = ndev;
3563 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3564 vif->wdev.netdev = ndev;
3565 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303566 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003567 vif->nw_type = nw_type;
3568 vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303569 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303570 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Raja Manieb389872012-04-16 16:09:56 +05303571 vif->bg_scan_period = 0;
Kiran Reddy67b3f122012-05-29 11:12:50 -07003572 vif->htcap[IEEE80211_BAND_2GHZ].ht_enable = true;
3573 vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303574
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303575 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
Aarthi Thiruvengadamc95dcb52012-07-10 13:20:40 -07003576 if (fw_vif_idx != 0) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303577 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3578 0x2;
Aarthi Thiruvengadamc95dcb52012-07-10 13:20:40 -07003579 if (test_bit(ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR,
3580 ar->fw_capabilities))
3581 ndev->dev_addr[4] ^= 0x80;
3582 }
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303583
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303584 init_netdev(ndev);
3585
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303586 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303587
Kalle Valoc25889e2012-01-17 20:08:27 +02003588 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303589 goto err;
3590
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303591 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303592 goto err;
3593
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303594 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303595 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303596 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303597 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303598 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303599
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303600 if (type == NL80211_IFTYPE_ADHOC)
3601 ar->ibss_if_active = true;
3602
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303603 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303604 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303605 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303606
Johannes Berg84efbb82012-06-16 00:00:26 +02003607 return &vif->wdev;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303608
3609err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303610 aggr_module_destroy(vif->aggr_cntxt);
3611 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303612 return NULL;
3613}
3614
Kalle Valo46d33a22012-01-17 20:08:40 +02003615int ath6kl_cfg80211_init(struct ath6kl *ar)
3616{
3617 struct wiphy *wiphy = ar->wiphy;
Thomas Pedersend92917e2012-04-19 15:31:56 -07003618 bool band_2gig = false, band_5gig = false, ht = false;
Kalle Valo46d33a22012-01-17 20:08:40 +02003619 int ret;
3620
3621 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3622
3623 wiphy->max_remain_on_channel_duration = 5000;
3624
3625 /* set device pointer for wiphy */
3626 set_wiphy_dev(wiphy, ar->dev);
3627
3628 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3629 BIT(NL80211_IFTYPE_ADHOC) |
3630 BIT(NL80211_IFTYPE_AP);
3631 if (ar->p2p) {
3632 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3633 BIT(NL80211_IFTYPE_P2P_CLIENT);
3634 }
3635
Kalle Valo84841ba2012-07-19 16:00:56 +03003636 if (config_enabled(CONFIG_ATH6KL_REGDOMAIN) &&
3637 test_bit(ATH6KL_FW_CAPABILITY_REGDOMAIN, ar->fw_capabilities))
3638 wiphy->reg_notifier = ath6kl_cfg80211_reg_notify;
3639
Kalle Valo46d33a22012-01-17 20:08:40 +02003640 /* max num of ssids that can be probed during scanning */
Jouni Malinen8ab54152012-05-09 22:14:51 +03003641 wiphy->max_scan_ssids = MAX_PROBED_SSIDS;
Naveen Singhdd45b752012-05-16 13:29:00 +03003642
3643 /* max num of ssids that can be matched after scan */
3644 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST,
3645 ar->fw_capabilities))
3646 wiphy->max_match_sets = MAX_PROBED_SSIDS;
3647
Kalle Valo46d33a22012-01-17 20:08:40 +02003648 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
Thomas Pedersend92917e2012-04-19 15:31:56 -07003649 switch (ar->hw.cap) {
3650 case WMI_11AN_CAP:
3651 ht = true;
3652 case WMI_11A_CAP:
3653 band_5gig = true;
3654 break;
3655 case WMI_11GN_CAP:
3656 ht = true;
3657 case WMI_11G_CAP:
3658 band_2gig = true;
3659 break;
3660 case WMI_11AGN_CAP:
3661 ht = true;
3662 case WMI_11AG_CAP:
3663 band_2gig = true;
3664 band_5gig = true;
3665 break;
3666 default:
3667 ath6kl_err("invalid phy capability!\n");
3668 return -EINVAL;
3669 }
3670
Vasanthakumar Thiagarajan7fd1ce72012-04-25 12:38:18 +05303671 /*
3672 * Even if the fw has HT support, advertise HT cap only when
3673 * the firmware has support to override RSN capability, otherwise
3674 * 4-way handshake would fail.
3675 */
3676 if (!(ht &&
3677 test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
3678 ar->fw_capabilities))) {
Thomas Pedersend92917e2012-04-19 15:31:56 -07003679 ath6kl_band_2ghz.ht_cap.cap = 0;
3680 ath6kl_band_2ghz.ht_cap.ht_supported = false;
3681 ath6kl_band_5ghz.ht_cap.cap = 0;
3682 ath6kl_band_5ghz.ht_cap.ht_supported = false;
3683 }
Bala Shanmugam06e360a2012-05-22 13:23:12 +05303684
3685 if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) {
3686 ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3687 ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3688 ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
3689 ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff;
3690 } else {
3691 ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3692 ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
3693 }
3694
Thomas Pedersend92917e2012-04-19 15:31:56 -07003695 if (band_2gig)
3696 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3697 if (band_5gig)
3698 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3699
Kalle Valo46d33a22012-01-17 20:08:40 +02003700 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3701
3702 wiphy->cipher_suites = cipher_suites;
3703 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3704
Johannes Bergdfb89c52012-06-27 09:23:48 +02003705#ifdef CONFIG_PM
Kalle Valo46d33a22012-01-17 20:08:40 +02003706 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3707 WIPHY_WOWLAN_DISCONNECT |
3708 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3709 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3710 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3711 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3712 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3713 wiphy->wowlan.pattern_min_len = 1;
3714 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
Johannes Bergdfb89c52012-06-27 09:23:48 +02003715#endif
Kalle Valo46d33a22012-01-17 20:08:40 +02003716
Jouni Malinen8ab54152012-05-09 22:14:51 +03003717 wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS;
Kalle Valo46d33a22012-01-17 20:08:40 +02003718
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303719 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3720 WIPHY_FLAG_HAVE_AP_SME |
3721 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3722 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3723
3724 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3725 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3726
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303727 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
3728 ar->fw_capabilities))
Johannes Bergb2922192012-10-12 10:55:53 +02003729 ar->wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303730
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303731 ar->wiphy->probe_resp_offload =
3732 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3733 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
Jouni Malinena432e7c2012-04-16 19:25:35 +03003734 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303735
Kalle Valo46d33a22012-01-17 20:08:40 +02003736 ret = wiphy_register(wiphy);
3737 if (ret < 0) {
3738 ath6kl_err("couldn't register wiphy device\n");
3739 return ret;
3740 }
3741
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303742 ar->wiphy_registered = true;
3743
Kalle Valo46d33a22012-01-17 20:08:40 +02003744 return 0;
3745}
3746
3747void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303748{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303749 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303750
3751 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003752}
Kalle Valo46d33a22012-01-17 20:08:40 +02003753
Kalle Valo45eaa782012-01-17 20:09:05 +02003754struct ath6kl *ath6kl_cfg80211_create(void)
3755{
3756 struct ath6kl *ar;
3757 struct wiphy *wiphy;
3758
3759 /* create a new wiphy for use with cfg80211 */
3760 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3761
3762 if (!wiphy) {
3763 ath6kl_err("couldn't allocate wiphy device\n");
3764 return NULL;
3765 }
3766
3767 ar = wiphy_priv(wiphy);
3768 ar->wiphy = wiphy;
3769
3770 return ar;
3771}
3772
3773/* Note: ar variable must not be accessed after calling this! */
3774void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3775{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303776 int i;
3777
3778 for (i = 0; i < AP_MAX_NUM_STA; i++)
3779 kfree(ar->sta_list[i].aggr_conn);
3780
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303781 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003782}
Kalle Valo45eaa782012-01-17 20:09:05 +02003783