blob: 0c49708cf37e0d43b30a15a5e3783c3fe76fd36a [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
Vasanthakumar Thiagarajan1b2df402012-02-06 20:15:53 +05303 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
Kalle Valobdcd8172011-07-18 00:22:30 +03004 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040018#include <linux/moduleparam.h>
Raja Manic08631c2011-12-16 14:24:24 +053019#include <linux/inetdevice.h>
Kalle Valod6a434d2012-01-17 20:09:36 +020020#include <linux/export.h>
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040021
Kalle Valobdcd8172011-07-18 00:22:30 +030022#include "core.h"
23#include "cfg80211.h"
24#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030025#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030026#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030027
28#define RATETAB_ENT(_rate, _rateid, _flags) { \
29 .bitrate = (_rate), \
30 .flags = (_flags), \
31 .hw_value = (_rateid), \
32}
33
34#define CHAN2G(_channel, _freq, _flags) { \
35 .band = IEEE80211_BAND_2GHZ, \
36 .hw_value = (_channel), \
37 .center_freq = (_freq), \
38 .flags = (_flags), \
39 .max_antenna_gain = 0, \
40 .max_power = 30, \
41}
42
43#define CHAN5G(_channel, _flags) { \
44 .band = IEEE80211_BAND_5GHZ, \
45 .hw_value = (_channel), \
46 .center_freq = 5000 + (5 * (_channel)), \
47 .flags = (_flags), \
48 .max_antenna_gain = 0, \
49 .max_power = 30, \
50}
51
Bala Shanmugamf5993592012-03-27 12:17:32 +053052#define DEFAULT_BG_SCAN_PERIOD 60
53
Kalle Valobdcd8172011-07-18 00:22:30 +030054static struct ieee80211_rate ath6kl_rates[] = {
55 RATETAB_ENT(10, 0x1, 0),
56 RATETAB_ENT(20, 0x2, 0),
57 RATETAB_ENT(55, 0x4, 0),
58 RATETAB_ENT(110, 0x8, 0),
59 RATETAB_ENT(60, 0x10, 0),
60 RATETAB_ENT(90, 0x20, 0),
61 RATETAB_ENT(120, 0x40, 0),
62 RATETAB_ENT(180, 0x80, 0),
63 RATETAB_ENT(240, 0x100, 0),
64 RATETAB_ENT(360, 0x200, 0),
65 RATETAB_ENT(480, 0x400, 0),
66 RATETAB_ENT(540, 0x800, 0),
67};
68
69#define ath6kl_a_rates (ath6kl_rates + 4)
70#define ath6kl_a_rates_size 8
71#define ath6kl_g_rates (ath6kl_rates + 0)
72#define ath6kl_g_rates_size 12
73
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +053074#define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
75 IEEE80211_HT_CAP_SGI_20 | \
76 IEEE80211_HT_CAP_SGI_40)
77
Kalle Valobdcd8172011-07-18 00:22:30 +030078static struct ieee80211_channel ath6kl_2ghz_channels[] = {
79 CHAN2G(1, 2412, 0),
80 CHAN2G(2, 2417, 0),
81 CHAN2G(3, 2422, 0),
82 CHAN2G(4, 2427, 0),
83 CHAN2G(5, 2432, 0),
84 CHAN2G(6, 2437, 0),
85 CHAN2G(7, 2442, 0),
86 CHAN2G(8, 2447, 0),
87 CHAN2G(9, 2452, 0),
88 CHAN2G(10, 2457, 0),
89 CHAN2G(11, 2462, 0),
90 CHAN2G(12, 2467, 0),
91 CHAN2G(13, 2472, 0),
92 CHAN2G(14, 2484, 0),
93};
94
95static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
96 CHAN5G(34, 0), CHAN5G(36, 0),
97 CHAN5G(38, 0), CHAN5G(40, 0),
98 CHAN5G(42, 0), CHAN5G(44, 0),
99 CHAN5G(46, 0), CHAN5G(48, 0),
100 CHAN5G(52, 0), CHAN5G(56, 0),
101 CHAN5G(60, 0), CHAN5G(64, 0),
102 CHAN5G(100, 0), CHAN5G(104, 0),
103 CHAN5G(108, 0), CHAN5G(112, 0),
104 CHAN5G(116, 0), CHAN5G(120, 0),
105 CHAN5G(124, 0), CHAN5G(128, 0),
106 CHAN5G(132, 0), CHAN5G(136, 0),
107 CHAN5G(140, 0), CHAN5G(149, 0),
108 CHAN5G(153, 0), CHAN5G(157, 0),
109 CHAN5G(161, 0), CHAN5G(165, 0),
110 CHAN5G(184, 0), CHAN5G(188, 0),
111 CHAN5G(192, 0), CHAN5G(196, 0),
112 CHAN5G(200, 0), CHAN5G(204, 0),
113 CHAN5G(208, 0), CHAN5G(212, 0),
114 CHAN5G(216, 0),
115};
116
117static struct ieee80211_supported_band ath6kl_band_2ghz = {
118 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
119 .channels = ath6kl_2ghz_channels,
120 .n_bitrates = ath6kl_g_rates_size,
121 .bitrates = ath6kl_g_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530122 .ht_cap.cap = ath6kl_g_htcap,
123 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300124};
125
126static struct ieee80211_supported_band ath6kl_band_5ghz = {
127 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
128 .channels = ath6kl_5ghz_a_channels,
129 .n_bitrates = ath6kl_a_rates_size,
130 .bitrates = ath6kl_a_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530131 .ht_cap.cap = ath6kl_g_htcap,
132 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300133};
134
Jouni Malinen837cb972011-10-11 17:31:57 +0300135#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
136
Kalle Valo10509f92011-12-13 14:52:07 +0200137/* returns true if scheduled scan was stopped */
138static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
139{
140 struct ath6kl *ar = vif->ar;
141
142 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
143 return false;
144
145 del_timer_sync(&vif->sched_scan_timer);
146
147 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
148 ATH6KL_HOST_MODE_AWAKE);
149
150 ar->state = ATH6KL_STATE_ON;
151
152 return true;
153}
154
155static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
156{
157 struct ath6kl *ar = vif->ar;
158 bool stopped;
159
160 stopped = __ath6kl_cfg80211_sscan_stop(vif);
161
162 if (!stopped)
163 return;
164
165 cfg80211_sched_scan_stopped(ar->wiphy);
166}
167
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530168static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300169 enum nl80211_wpa_versions wpa_version)
170{
171 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
172
173 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530174 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300175 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530176 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300177 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530178 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300179 } else {
180 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
181 return -ENOTSUPP;
182 }
183
184 return 0;
185}
186
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530187static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300188 enum nl80211_auth_type auth_type)
189{
Kalle Valobdcd8172011-07-18 00:22:30 +0300190 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
191
192 switch (auth_type) {
193 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530194 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300195 break;
196 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530197 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300198 break;
199 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530200 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300201 break;
202
203 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530204 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300205 break;
206
207 default:
Masanari Iida3c325fb2012-01-31 23:32:55 +0900208 ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300209 return -ENOTSUPP;
210 }
211
212 return 0;
213}
214
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530215static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300216{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530217 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
218 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
219 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300220
221 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
222 __func__, cipher, ucast);
223
224 switch (cipher) {
225 case 0:
226 /* our own hack to use value 0 as no crypto used */
227 *ar_cipher = NONE_CRYPT;
228 *ar_cipher_len = 0;
229 break;
230 case WLAN_CIPHER_SUITE_WEP40:
231 *ar_cipher = WEP_CRYPT;
232 *ar_cipher_len = 5;
233 break;
234 case WLAN_CIPHER_SUITE_WEP104:
235 *ar_cipher = WEP_CRYPT;
236 *ar_cipher_len = 13;
237 break;
238 case WLAN_CIPHER_SUITE_TKIP:
239 *ar_cipher = TKIP_CRYPT;
240 *ar_cipher_len = 0;
241 break;
242 case WLAN_CIPHER_SUITE_CCMP:
243 *ar_cipher = AES_CRYPT;
244 *ar_cipher_len = 0;
245 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200246 case WLAN_CIPHER_SUITE_SMS4:
247 *ar_cipher = WAPI_CRYPT;
248 *ar_cipher_len = 0;
249 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300250 default:
251 ath6kl_err("cipher 0x%x not supported\n", cipher);
252 return -ENOTSUPP;
253 }
254
255 return 0;
256}
257
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530258static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300259{
260 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
261
262 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530263 if (vif->auth_mode == WPA_AUTH)
264 vif->auth_mode = WPA_PSK_AUTH;
265 else if (vif->auth_mode == WPA2_AUTH)
266 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300267 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530268 if (vif->auth_mode == WPA_AUTH)
269 vif->auth_mode = WPA_AUTH_CCKM;
270 else if (vif->auth_mode == WPA2_AUTH)
271 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300272 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530273 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300274 }
275}
276
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530277static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300278{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530279 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530280
Kalle Valobdcd8172011-07-18 00:22:30 +0300281 if (!test_bit(WMI_READY, &ar->flag)) {
282 ath6kl_err("wmi is not ready\n");
283 return false;
284 }
285
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530286 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300287 ath6kl_err("wlan disabled\n");
288 return false;
289 }
290
291 return true;
292}
293
Kevin Fang6981ffd2011-10-07 08:51:19 +0800294static bool ath6kl_is_wpa_ie(const u8 *pos)
295{
296 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
297 pos[2] == 0x00 && pos[3] == 0x50 &&
298 pos[4] == 0xf2 && pos[5] == 0x01;
299}
300
301static bool ath6kl_is_rsn_ie(const u8 *pos)
302{
303 return pos[0] == WLAN_EID_RSN;
304}
305
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700306static bool ath6kl_is_wps_ie(const u8 *pos)
307{
308 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
309 pos[1] >= 4 &&
310 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
311 pos[5] == 0x04);
312}
313
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530314static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
315 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800316{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530317 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800318 const u8 *pos;
319 u8 *buf = NULL;
320 size_t len = 0;
321 int ret;
322
323 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700324 * Clear previously set flag
325 */
326
327 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
328
329 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800330 * Filter out RSN/WPA IE(s)
331 */
332
333 if (ies && ies_len) {
334 buf = kmalloc(ies_len, GFP_KERNEL);
335 if (buf == NULL)
336 return -ENOMEM;
337 pos = ies;
338
339 while (pos + 1 < ies + ies_len) {
340 if (pos + 2 + pos[1] > ies + ies_len)
341 break;
342 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
343 memcpy(buf + len, pos, 2 + pos[1]);
344 len += 2 + pos[1];
345 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700346
347 if (ath6kl_is_wps_ie(pos))
348 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
349
Kevin Fang6981ffd2011-10-07 08:51:19 +0800350 pos += 2 + pos[1];
351 }
352 }
353
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530354 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
355 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800356 kfree(buf);
357 return ret;
358}
359
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530360static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
361{
362 switch (type) {
363 case NL80211_IFTYPE_STATION:
364 *nw_type = INFRA_NETWORK;
365 break;
366 case NL80211_IFTYPE_ADHOC:
367 *nw_type = ADHOC_NETWORK;
368 break;
369 case NL80211_IFTYPE_AP:
370 *nw_type = AP_NETWORK;
371 break;
372 case NL80211_IFTYPE_P2P_CLIENT:
373 *nw_type = INFRA_NETWORK;
374 break;
375 case NL80211_IFTYPE_P2P_GO:
376 *nw_type = AP_NETWORK;
377 break;
378 default:
379 ath6kl_err("invalid interface type %u\n", type);
380 return -ENOTSUPP;
381 }
382
383 return 0;
384}
385
386static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
387 u8 *if_idx, u8 *nw_type)
388{
389 int i;
390
391 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
392 return false;
393
394 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
Kalle Valo96f1fad2012-03-07 20:03:57 +0200395 ar->num_vif))
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530396 return false;
397
398 if (type == NL80211_IFTYPE_STATION ||
399 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200400 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530401 if ((ar->avail_idx_map >> i) & BIT(0)) {
402 *if_idx = i;
403 return true;
404 }
405 }
406 }
407
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530408 if (type == NL80211_IFTYPE_P2P_CLIENT ||
409 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200410 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530411 if ((ar->avail_idx_map >> i) & BIT(0)) {
412 *if_idx = i;
413 return true;
414 }
415 }
416 }
417
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530418 return false;
419}
420
Kalle Valo8c9bb052012-03-07 20:04:00 +0200421static bool ath6kl_is_tx_pending(struct ath6kl *ar)
422{
423 return ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0;
424}
425
426
Kalle Valobdcd8172011-07-18 00:22:30 +0300427static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
428 struct cfg80211_connect_params *sme)
429{
430 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530431 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300432 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800433 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Raja Manice0dc0c2012-02-20 19:08:08 +0530434 u16 interval;
Kalle Valobdcd8172011-07-18 00:22:30 +0300435
Kalle Valo10509f92011-12-13 14:52:07 +0200436 ath6kl_cfg80211_sscan_disable(vif);
437
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530438 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300439
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530440 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300441 return -EIO;
442
443 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
444 ath6kl_err("destroy in progress\n");
445 return -EBUSY;
446 }
447
448 if (test_bit(SKIP_SCAN, &ar->flag) &&
449 ((sme->channel && sme->channel->center_freq == 0) ||
450 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
451 ath6kl_err("SkipScan: channel or bssid invalid\n");
452 return -EINVAL;
453 }
454
455 if (down_interruptible(&ar->sem)) {
456 ath6kl_err("busy, couldn't get access\n");
457 return -ERESTARTSYS;
458 }
459
460 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
461 ath6kl_err("busy, destroy in progress\n");
462 up(&ar->sem);
463 return -EBUSY;
464 }
465
466 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
467 /*
468 * sleep until the command queue drains
469 */
470 wait_event_interruptible_timeout(ar->event_wq,
Kalle Valo8c9bb052012-03-07 20:04:00 +0200471 ath6kl_is_tx_pending(ar),
472 WMI_TIMEOUT);
Kalle Valobdcd8172011-07-18 00:22:30 +0300473 if (signal_pending(current)) {
474 ath6kl_err("cmd queue drain timeout\n");
475 up(&ar->sem);
476 return -EINTR;
477 }
478 }
479
Jouni Malinen6e786cb2011-12-15 14:16:00 +0200480 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
481 if (status) {
482 up(&ar->sem);
483 return status;
484 }
485
486 if (sme->ie == NULL || sme->ie_len == 0)
Raja Mani542c5192011-11-15 14:14:56 +0530487 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800488
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530489 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530490 vif->ssid_len == sme->ssid_len &&
491 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530492 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530493 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
494 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530495 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300496
497 up(&ar->sem);
498 if (status) {
499 ath6kl_err("wmi_reconnect_cmd failed\n");
500 return -EIO;
501 }
502 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530503 } else if (vif->ssid_len == sme->ssid_len &&
504 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530505 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300506 }
507
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530508 memset(vif->ssid, 0, sizeof(vif->ssid));
509 vif->ssid_len = sme->ssid_len;
510 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300511
512 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530513 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300514
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530515 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300516 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530517 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300518
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530519 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300520
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530521 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300522 if (status) {
523 up(&ar->sem);
524 return status;
525 }
526
527 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530528 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300529 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530530 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300531
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530532 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300533
534 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530535 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300536
537 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530538 (vif->auth_mode == NONE_AUTH) &&
539 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300540 struct ath6kl_key *key = NULL;
541
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530542 if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300543 ath6kl_err("key index %d out of bounds\n",
544 sme->key_idx);
545 up(&ar->sem);
546 return -ENOENT;
547 }
548
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530549 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300550 key->key_len = sme->key_len;
551 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530552 key->cipher = vif->prwise_crypto;
553 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300554
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530555 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530556 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300557 GROUP_USAGE | TX_USAGE,
558 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200559 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300560 key->key, KEY_OP_INIT_VAL, NULL,
561 NO_SYNC_WMIFLAG);
562 }
563
564 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530565 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530566 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200567 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300568 ath6kl_err("couldn't set bss filtering\n");
569 up(&ar->sem);
570 return -EIO;
571 }
572 }
573
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530574 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300575
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800576 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
577 nw_subtype = SUBTYPE_P2PCLIENT;
578
Kalle Valobdcd8172011-07-18 00:22:30 +0300579 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
580 "%s: connect called with authmode %d dot11 auth %d"
581 " PW crypto %d PW crypto len %d GRP crypto %d"
582 " GRP crypto len %d channel hint %u\n",
583 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530584 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
585 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530586 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300587
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530588 vif->reconnect_flag = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +0530589
590 if (vif->nw_type == INFRA_NETWORK) {
Kalle Valob5283872012-03-12 13:23:23 +0200591 interval = max_t(u16, vif->listen_intvl_t,
592 ATH6KL_MAX_WOW_LISTEN_INTL);
Raja Manice0dc0c2012-02-20 19:08:08 +0530593 status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
594 interval,
595 0);
596 if (status) {
597 ath6kl_err("couldn't set listen intervel\n");
598 up(&ar->sem);
599 return status;
600 }
601 }
602
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530603 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530604 vif->dot11_auth_mode, vif->auth_mode,
605 vif->prwise_crypto,
606 vif->prwise_crypto_len,
607 vif->grp_crypto, vif->grp_crypto_len,
608 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530609 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800610 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300611
Bala Shanmugamf5993592012-03-27 12:17:32 +0530612 /* disable background scan if period is 0 */
613 if (sme->bg_scan_period == 0)
614 sme->bg_scan_period = 0xffff;
615
616 /* configure default value if not specified */
617 if (sme->bg_scan_period == -1)
618 sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
619
620 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0,
621 sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
622
Kalle Valobdcd8172011-07-18 00:22:30 +0300623 up(&ar->sem);
624
625 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530626 memset(vif->ssid, 0, sizeof(vif->ssid));
627 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300628 ath6kl_err("invalid request\n");
629 return -ENOENT;
630 } else if (status) {
631 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
632 return -EIO;
633 }
634
635 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Kalle Valoddc3d772012-03-07 20:03:58 +0200636 ((vif->auth_mode == WPA_PSK_AUTH) ||
637 (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530638 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300639 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
640 }
641
642 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530643 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300644
645 return 0;
646}
647
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530648static struct cfg80211_bss *
649ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
650 enum network_type nw_type,
651 const u8 *bssid,
652 struct ieee80211_channel *chan,
653 const u8 *beacon_ie,
654 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300655{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530656 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300657 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530658 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300659 u8 *ie;
660
Raja Mani4eab6f42011-11-09 17:02:23 +0530661 if (nw_type & ADHOC_NETWORK) {
662 cap_mask = WLAN_CAPABILITY_IBSS;
663 cap_val = WLAN_CAPABILITY_IBSS;
664 } else {
665 cap_mask = WLAN_CAPABILITY_ESS;
666 cap_val = WLAN_CAPABILITY_ESS;
667 }
668
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530669 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530670 vif->ssid, vif->ssid_len,
671 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300672 if (bss == NULL) {
673 /*
674 * Since cfg80211 may not yet know about the BSS,
675 * generate a partial entry until the first BSS info
676 * event becomes available.
677 *
678 * Prepend SSID element since it is not included in the Beacon
679 * IEs from the target.
680 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530681 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300682 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530683 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300684 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530685 ie[1] = vif->ssid_len;
686 memcpy(ie + 2, vif->ssid, vif->ssid_len);
687 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530688 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530689 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530690 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300691 0, GFP_KERNEL);
692 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530693 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
694 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300695 kfree(ie);
696 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530697 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300698
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530699 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300700}
701
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530702void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300703 u8 *bssid, u16 listen_intvl,
704 u16 beacon_intvl,
705 enum network_type nw_type,
706 u8 beacon_ie_len, u8 assoc_req_len,
707 u8 assoc_resp_len, u8 *assoc_info)
708{
Jouni Malinen01cac472011-09-19 19:14:59 +0300709 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530710 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530711 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300712
713 /* capinfo + listen interval */
714 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
715
716 /* capinfo + status code + associd */
717 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
718
719 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
720 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
721 assoc_resp_ie_offset;
722
723 assoc_req_len -= assoc_req_ie_offset;
724 assoc_resp_len -= assoc_resp_ie_offset;
725
Jouni Malinen32c10872011-09-19 19:15:07 +0300726 /*
727 * Store Beacon interval here; DTIM period will be available only once
728 * a Beacon frame from the AP is seen.
729 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530730 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530731 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300732
Kalle Valobdcd8172011-07-18 00:22:30 +0300733 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530734 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300735 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
736 "%s: ath6k not in ibss mode\n", __func__);
737 return;
738 }
739 }
740
741 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530742 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
743 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300744 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
745 "%s: ath6k not in station mode\n", __func__);
746 return;
747 }
748 }
749
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530750 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300751
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530752 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
753 assoc_info, beacon_ie_len);
754 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530755 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300756 return;
757 }
758
Raja Mani4eab6f42011-11-09 17:02:23 +0530759 if (nw_type & ADHOC_NETWORK) {
760 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
761 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
762 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530763 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300764 return;
765 }
766
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530767 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300768 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530769 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530770 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300771 assoc_req_ie, assoc_req_len,
772 assoc_resp_ie, assoc_resp_len,
773 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530774 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530775 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300776 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530777 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
778 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300779 }
780}
781
782static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
783 struct net_device *dev, u16 reason_code)
784{
Kalle Valod6d5c062011-11-25 13:17:37 +0200785 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530786 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300787
788 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
789 reason_code);
790
Kalle Valo10509f92011-12-13 14:52:07 +0200791 ath6kl_cfg80211_sscan_disable(vif);
792
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530793 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300794 return -EIO;
795
796 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
797 ath6kl_err("busy, destroy in progress\n");
798 return -EBUSY;
799 }
800
801 if (down_interruptible(&ar->sem)) {
802 ath6kl_err("busy, couldn't get access\n");
803 return -ERESTARTSYS;
804 }
805
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530806 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530807 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530808 memset(vif->ssid, 0, sizeof(vif->ssid));
809 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300810
811 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530812 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300813
814 up(&ar->sem);
815
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530816 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530817
Kalle Valobdcd8172011-07-18 00:22:30 +0300818 return 0;
819}
820
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530821void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300822 u8 *bssid, u8 assoc_resp_len,
823 u8 *assoc_info, u16 proto_reason)
824{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530825 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530826
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530827 if (vif->scan_req) {
828 cfg80211_scan_done(vif->scan_req, true);
829 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300830 }
831
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530832 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530833 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300834 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
835 "%s: ath6k not in ibss mode\n", __func__);
836 return;
837 }
838 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530839 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300840 return;
841 }
842
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530843 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530844 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
845 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300846 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
847 "%s: ath6k not in station mode\n", __func__);
848 return;
849 }
850 }
851
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530852 /*
853 * Send a disconnect command to target when a disconnect event is
854 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
855 * request from host) to make the firmware stop trying to connect even
856 * after giving disconnect event. There will be one more disconnect
857 * event for this disconnect command with reason code DISCONNECT_CMD
858 * which will be notified to cfg80211.
859 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300860
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530861 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530862 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300863 return;
864 }
865
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530866 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300867
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530868 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530869 cfg80211_connect_result(vif->ndev,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200870 bssid, NULL, 0,
871 NULL, 0,
872 WLAN_STATUS_UNSPECIFIED_FAILURE,
873 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530874 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530875 cfg80211_disconnected(vif->ndev, reason,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200876 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300877 }
878
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530879 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300880}
881
Kalle Valobdcd8172011-07-18 00:22:30 +0300882static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
883 struct cfg80211_scan_request *request)
884{
Kalle Valod6d5c062011-11-25 13:17:37 +0200885 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530886 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300887 s8 n_channels = 0;
888 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300889 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530890 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300891
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530892 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300893 return -EIO;
894
Kalle Valo10509f92011-12-13 14:52:07 +0200895 ath6kl_cfg80211_sscan_disable(vif);
896
Kalle Valobdcd8172011-07-18 00:22:30 +0300897 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530898 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300899 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530900 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530901 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300902 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
903 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300904 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300905 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300906 }
907 }
908
909 if (request->n_ssids && request->ssids[0].ssid_len) {
910 u8 i;
911
912 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
913 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
914
915 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530916 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
917 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300918 request->ssids[i].ssid_len,
919 request->ssids[i].ssid);
920 }
921
Aarthi Thiruvengadam080eec42012-02-28 09:17:04 -0800922 /* this also clears IE in fw if it's not set */
923 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
924 WMI_FRAME_PROBE_REQ,
925 request->ie, request->ie_len);
926 if (ret) {
927 ath6kl_err("failed to set Probe Request appie for "
928 "scan");
929 return ret;
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300930 }
931
Jouni Malinen11869be2011-09-02 20:07:06 +0300932 /*
933 * Scan only the requested channels if the request specifies a set of
934 * channels. If the list is longer than the target supports, do not
935 * configure the list and instead, scan all available channels.
936 */
937 if (request->n_channels > 0 &&
938 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300939 u8 i;
940
Jouni Malinen11869be2011-09-02 20:07:06 +0300941 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300942
943 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
944 if (channels == NULL) {
945 ath6kl_warn("failed to set scan channels, "
946 "scan all channels");
947 n_channels = 0;
948 }
949
950 for (i = 0; i < n_channels; i++)
951 channels[i] = request->channels[i]->center_freq;
952 }
953
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530954 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530955 force_fg_scan = 1;
956
Raja Mani5b35dff2012-03-28 18:50:35 +0530957 vif->scan_req = request;
958
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800959 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200960 ar->fw_capabilities)) {
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800961 /*
962 * If capable of doing P2P mgmt operations using
963 * station interface, send additional information like
964 * supported rates to advertise and xmit rates for
965 * probe requests
966 */
967 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
968 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530969 false, 0,
970 ATH6KL_FG_SCAN_INTERVAL,
971 n_channels, channels,
972 request->no_cck,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800973 request->rates);
974 } else {
975 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
976 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530977 false, 0,
978 ATH6KL_FG_SCAN_INTERVAL,
979 n_channels, channels);
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800980 }
Raja Mani5b35dff2012-03-28 18:50:35 +0530981 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300982 ath6kl_err("wmi_startscan_cmd failed\n");
Raja Mani5b35dff2012-03-28 18:50:35 +0530983 vif->scan_req = NULL;
984 }
Kalle Valobdcd8172011-07-18 00:22:30 +0300985
Edward Lu1276c9e2011-08-30 21:58:00 +0300986 kfree(channels);
987
Kalle Valobdcd8172011-07-18 00:22:30 +0300988 return ret;
989}
990
Kalle Valo1c17d312011-11-01 08:43:56 +0200991void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300992{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530993 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300994 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300995
Kalle Valo1c17d312011-11-01 08:43:56 +0200996 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
997 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300998
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530999 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001000 return;
Kalle Valobdcd8172011-07-18 00:22:30 +03001001
Kalle Valo1c17d312011-11-01 08:43:56 +02001002 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001003 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001004
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301005 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
1006 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301007 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
1008 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001009 0, NULL);
1010 }
1011 }
1012
1013out:
Kalle Valocb938212011-10-27 18:47:46 +03001014 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301015 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001016}
1017
1018static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1019 u8 key_index, bool pairwise,
1020 const u8 *mac_addr,
1021 struct key_params *params)
1022{
Kalle Valod6d5c062011-11-25 13:17:37 +02001023 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301024 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001025 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301026 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001027 u8 key_usage;
1028 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001029
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301030 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001031 return -EIO;
1032
Jouni Malinen837cb972011-10-11 17:31:57 +03001033 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
1034 if (params->key_len != WMI_KRK_LEN)
1035 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301036 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
1037 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +03001038 }
1039
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301040 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001041 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1042 "%s: key index %d out of bounds\n", __func__,
1043 key_index);
1044 return -ENOENT;
1045 }
1046
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301047 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001048 memset(key, 0, sizeof(struct ath6kl_key));
1049
1050 if (pairwise)
1051 key_usage = PAIRWISE_USAGE;
1052 else
1053 key_usage = GROUP_USAGE;
1054
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301055 seq_len = params->seq_len;
1056 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1057 seq_len > ATH6KL_KEY_SEQ_LEN) {
1058 /* Only first half of the WPI PN is configured */
1059 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001060 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301061 if (params->key_len > WLAN_MAX_KEY_LEN ||
1062 seq_len > sizeof(key->seq))
1063 return -EINVAL;
1064
1065 key->key_len = params->key_len;
1066 memcpy(key->key, params->key, key->key_len);
1067 key->seq_len = seq_len;
1068 memcpy(key->seq, params->seq, key->seq_len);
1069 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001070
1071 switch (key->cipher) {
1072 case WLAN_CIPHER_SUITE_WEP40:
1073 case WLAN_CIPHER_SUITE_WEP104:
1074 key_type = WEP_CRYPT;
1075 break;
1076
1077 case WLAN_CIPHER_SUITE_TKIP:
1078 key_type = TKIP_CRYPT;
1079 break;
1080
1081 case WLAN_CIPHER_SUITE_CCMP:
1082 key_type = AES_CRYPT;
1083 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001084 case WLAN_CIPHER_SUITE_SMS4:
1085 key_type = WAPI_CRYPT;
1086 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001087
1088 default:
1089 return -ENOTSUPP;
1090 }
1091
Kalle Valoddc3d772012-03-07 20:03:58 +02001092 if (((vif->auth_mode == WPA_PSK_AUTH) ||
1093 (vif->auth_mode == WPA2_PSK_AUTH)) &&
1094 (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301095 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001096
1097 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1098 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1099 __func__, key_index, key->key_len, key_type,
1100 key_usage, key->seq_len);
1101
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301102 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001103 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
Vasanthakumar Thiagarajancc4d6232012-02-14 20:33:00 +05301104 key_type == WAPI_CRYPT)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001105 ar->ap_mode_bkey.valid = true;
1106 ar->ap_mode_bkey.key_index = key_index;
1107 ar->ap_mode_bkey.key_type = key_type;
1108 ar->ap_mode_bkey.key_len = key->key_len;
1109 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301110 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001111 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1112 "key configuration until AP mode has been "
1113 "started\n");
1114 /*
1115 * The key will be set in ath6kl_connect_ap_mode() once
1116 * the connected event is received from the target.
1117 */
1118 return 0;
1119 }
1120 }
1121
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301122 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301123 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001124 /*
1125 * Store the key locally so that it can be re-configured after
1126 * the AP mode has properly started
1127 * (ath6kl_install_statioc_wep_keys).
1128 */
1129 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1130 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301131 vif->wep_key_list[key_index].key_len = key->key_len;
1132 memcpy(vif->wep_key_list[key_index].key, key->key,
1133 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001134 return 0;
1135 }
1136
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301137 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001138 key_type, key_usage, key->key_len,
1139 key->seq, key->seq_len, key->key,
1140 KEY_OP_INIT_VAL,
1141 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001142}
1143
1144static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1145 u8 key_index, bool pairwise,
1146 const u8 *mac_addr)
1147{
Kalle Valod6d5c062011-11-25 13:17:37 +02001148 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301149 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001150
1151 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1152
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301153 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001154 return -EIO;
1155
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301156 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001157 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1158 "%s: key index %d out of bounds\n", __func__,
1159 key_index);
1160 return -ENOENT;
1161 }
1162
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301163 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001164 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1165 "%s: index %d is empty\n", __func__, key_index);
1166 return 0;
1167 }
1168
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301169 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001170
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301171 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001172}
1173
1174static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1175 u8 key_index, bool pairwise,
1176 const u8 *mac_addr, void *cookie,
1177 void (*callback) (void *cookie,
1178 struct key_params *))
1179{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301180 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001181 struct ath6kl_key *key = NULL;
1182 struct key_params params;
1183
1184 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1185
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301186 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001187 return -EIO;
1188
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301189 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001190 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1191 "%s: key index %d out of bounds\n", __func__,
1192 key_index);
1193 return -ENOENT;
1194 }
1195
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301196 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001197 memset(&params, 0, sizeof(params));
1198 params.cipher = key->cipher;
1199 params.key_len = key->key_len;
1200 params.seq_len = key->seq_len;
1201 params.seq = key->seq;
1202 params.key = key->key;
1203
1204 callback(cookie, &params);
1205
1206 return key->key_len ? 0 : -ENOENT;
1207}
1208
1209static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1210 struct net_device *ndev,
1211 u8 key_index, bool unicast,
1212 bool multicast)
1213{
Kalle Valod6d5c062011-11-25 13:17:37 +02001214 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301215 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001216 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001217 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001218 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001219
1220 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1221
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301222 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001223 return -EIO;
1224
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301225 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001226 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1227 "%s: key index %d out of bounds\n",
1228 __func__, key_index);
1229 return -ENOENT;
1230 }
1231
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301232 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001233 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1234 __func__, key_index);
1235 return -EINVAL;
1236 }
1237
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301238 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301239 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001240 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301241 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001242 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001243 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301244 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001245 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301246 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001247
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301248 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001249 return 0; /* Delay until AP mode has been started */
1250
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001251 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1252 vif->def_txkey_index,
1253 key_type, key_usage,
1254 key->key_len, key->seq, key->seq_len,
1255 key->key,
1256 KEY_OP_INIT_VAL, NULL,
1257 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001258}
1259
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301260void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001261 bool ismcast)
1262{
1263 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1264 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1265
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301266 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001267 (ismcast ? NL80211_KEYTYPE_GROUP :
1268 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1269 GFP_KERNEL);
1270}
1271
1272static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1273{
1274 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301275 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001276 int ret;
1277
1278 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1279 changed);
1280
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301281 vif = ath6kl_vif_first(ar);
1282 if (!vif)
1283 return -EIO;
1284
1285 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001286 return -EIO;
1287
1288 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1289 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1290 if (ret != 0) {
1291 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1292 return -EIO;
1293 }
1294 }
1295
1296 return 0;
1297}
1298
1299/*
1300 * The type nl80211_tx_power_setting replaces the following
1301 * data type from 2.6.36 onwards
1302*/
1303static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1304 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001305 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001306{
1307 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301308 struct ath6kl_vif *vif;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001309 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001310
1311 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1312 type, dbm);
1313
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301314 vif = ath6kl_vif_first(ar);
1315 if (!vif)
1316 return -EIO;
1317
1318 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001319 return -EIO;
1320
1321 switch (type) {
1322 case NL80211_TX_POWER_AUTOMATIC:
1323 return 0;
1324 case NL80211_TX_POWER_LIMITED:
Kalle Valod0d670a2012-03-07 20:03:58 +02001325 ar->tx_pwr = dbm;
Kalle Valobdcd8172011-07-18 00:22:30 +03001326 break;
1327 default:
1328 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1329 __func__, type);
1330 return -EOPNOTSUPP;
1331 }
1332
Kalle Valod0d670a2012-03-07 20:03:58 +02001333 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001334
1335 return 0;
1336}
1337
1338static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1339{
1340 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301341 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001342
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301343 vif = ath6kl_vif_first(ar);
1344 if (!vif)
1345 return -EIO;
1346
1347 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001348 return -EIO;
1349
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301350 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001351 ar->tx_pwr = 0;
1352
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301353 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001354 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1355 return -EIO;
1356 }
1357
1358 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1359 5 * HZ);
1360
1361 if (signal_pending(current)) {
1362 ath6kl_err("target did not respond\n");
1363 return -EINTR;
1364 }
1365 }
1366
1367 *dbm = ar->tx_pwr;
1368 return 0;
1369}
1370
1371static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1372 struct net_device *dev,
1373 bool pmgmt, int timeout)
1374{
1375 struct ath6kl *ar = ath6kl_priv(dev);
1376 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301377 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001378
1379 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1380 __func__, pmgmt, timeout);
1381
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301382 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001383 return -EIO;
1384
1385 if (pmgmt) {
1386 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1387 mode.pwr_mode = REC_POWER;
1388 } else {
1389 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1390 mode.pwr_mode = MAX_PERF_POWER;
1391 }
1392
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301393 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001394 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001395 ath6kl_err("wmi_powermode_cmd failed\n");
1396 return -EIO;
1397 }
1398
1399 return 0;
1400}
1401
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301402static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1403 char *name,
1404 enum nl80211_iftype type,
1405 u32 *flags,
1406 struct vif_params *params)
1407{
1408 struct ath6kl *ar = wiphy_priv(wiphy);
1409 struct net_device *ndev;
1410 u8 if_idx, nw_type;
1411
Kalle Valo71f96ee2011-11-14 19:31:30 +02001412 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301413 ath6kl_err("Reached maximum number of supported vif\n");
1414 return ERR_PTR(-EINVAL);
1415 }
1416
1417 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1418 ath6kl_err("Not a supported interface type\n");
1419 return ERR_PTR(-EINVAL);
1420 }
1421
1422 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1423 if (!ndev)
1424 return ERR_PTR(-ENOMEM);
1425
1426 ar->num_vif++;
1427
1428 return ndev;
1429}
1430
1431static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1432 struct net_device *ndev)
1433{
1434 struct ath6kl *ar = wiphy_priv(wiphy);
1435 struct ath6kl_vif *vif = netdev_priv(ndev);
1436
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301437 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301438 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301439 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301440
1441 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1442
Kalle Valoc25889e2012-01-17 20:08:27 +02001443 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301444
1445 return 0;
1446}
1447
Kalle Valobdcd8172011-07-18 00:22:30 +03001448static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1449 struct net_device *ndev,
1450 enum nl80211_iftype type, u32 *flags,
1451 struct vif_params *params)
1452{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301453 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001454
1455 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1456
Kalle Valobdcd8172011-07-18 00:22:30 +03001457 switch (type) {
1458 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301459 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001460 break;
1461 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301462 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001463 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001464 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301465 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001466 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001467 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301468 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001469 break;
1470 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301471 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001472 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001473 default:
1474 ath6kl_err("invalid interface type %u\n", type);
1475 return -EOPNOTSUPP;
1476 }
1477
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301478 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001479
1480 return 0;
1481}
1482
1483static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1484 struct net_device *dev,
1485 struct cfg80211_ibss_params *ibss_param)
1486{
1487 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301488 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001489 int status;
1490
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301491 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001492 return -EIO;
1493
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301494 vif->ssid_len = ibss_param->ssid_len;
1495 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001496
1497 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301498 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001499
1500 if (ibss_param->channel_fixed) {
1501 /*
1502 * TODO: channel_fixed: The channel should be fixed, do not
1503 * search for IBSSs to join on other channels. Target
1504 * firmware does not support this feature, needs to be
1505 * updated.
1506 */
1507 return -EOPNOTSUPP;
1508 }
1509
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301510 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001511 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301512 memcpy(vif->req_bssid, ibss_param->bssid,
1513 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001514
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301515 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001516
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301517 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001518 if (status)
1519 return status;
1520
1521 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301522 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1523 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001524 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301525 ath6kl_set_cipher(vif, 0, true);
1526 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001527 }
1528
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301529 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001530
1531 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1532 "%s: connect called with authmode %d dot11 auth %d"
1533 " PW crypto %d PW crypto len %d GRP crypto %d"
1534 " GRP crypto len %d channel hint %u\n",
1535 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301536 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1537 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301538 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001539
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301540 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301541 vif->dot11_auth_mode, vif->auth_mode,
1542 vif->prwise_crypto,
1543 vif->prwise_crypto_len,
1544 vif->grp_crypto, vif->grp_crypto_len,
1545 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301546 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001547 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301548 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001549
1550 return 0;
1551}
1552
1553static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1554 struct net_device *dev)
1555{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301556 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001557
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301558 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001559 return -EIO;
1560
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301561 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301562 memset(vif->ssid, 0, sizeof(vif->ssid));
1563 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001564
1565 return 0;
1566}
1567
1568static const u32 cipher_suites[] = {
1569 WLAN_CIPHER_SUITE_WEP40,
1570 WLAN_CIPHER_SUITE_WEP104,
1571 WLAN_CIPHER_SUITE_TKIP,
1572 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001573 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001574 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001575};
1576
1577static bool is_rate_legacy(s32 rate)
1578{
1579 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1580 6000, 9000, 12000, 18000, 24000,
1581 36000, 48000, 54000
1582 };
1583 u8 i;
1584
1585 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1586 if (rate == legacy[i])
1587 return true;
1588
1589 return false;
1590}
1591
1592static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1593{
1594 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1595 52000, 58500, 65000, 72200
1596 };
1597 u8 i;
1598
1599 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1600 if (rate == ht20[i]) {
1601 if (i == ARRAY_SIZE(ht20) - 1)
1602 /* last rate uses sgi */
1603 *sgi = true;
1604 else
1605 *sgi = false;
1606
1607 *mcs = i;
1608 return true;
1609 }
1610 }
1611 return false;
1612}
1613
1614static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1615{
1616 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1617 81000, 108000, 121500, 135000,
1618 150000
1619 };
1620 u8 i;
1621
1622 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1623 if (rate == ht40[i]) {
1624 if (i == ARRAY_SIZE(ht40) - 1)
1625 /* last rate uses sgi */
1626 *sgi = true;
1627 else
1628 *sgi = false;
1629
1630 *mcs = i;
1631 return true;
1632 }
1633 }
1634
1635 return false;
1636}
1637
1638static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1639 u8 *mac, struct station_info *sinfo)
1640{
1641 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301642 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001643 long left;
1644 bool sgi;
1645 s32 rate;
1646 int ret;
1647 u8 mcs;
1648
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301649 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001650 return -ENOENT;
1651
1652 if (down_interruptible(&ar->sem))
1653 return -EBUSY;
1654
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301655 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001656
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301657 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001658
1659 if (ret != 0) {
1660 up(&ar->sem);
1661 return -EIO;
1662 }
1663
1664 left = wait_event_interruptible_timeout(ar->event_wq,
1665 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301666 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001667 WMI_TIMEOUT);
1668
1669 up(&ar->sem);
1670
1671 if (left == 0)
1672 return -ETIMEDOUT;
1673 else if (left < 0)
1674 return left;
1675
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301676 if (vif->target_stats.rx_byte) {
1677 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001678 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301679 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001680 sinfo->filled |= STATION_INFO_RX_PACKETS;
1681 }
1682
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301683 if (vif->target_stats.tx_byte) {
1684 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001685 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301686 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001687 sinfo->filled |= STATION_INFO_TX_PACKETS;
1688 }
1689
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301690 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001691 sinfo->filled |= STATION_INFO_SIGNAL;
1692
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301693 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001694
1695 if (is_rate_legacy(rate)) {
1696 sinfo->txrate.legacy = rate / 100;
1697 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1698 if (sgi) {
1699 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1700 sinfo->txrate.mcs = mcs - 1;
1701 } else {
1702 sinfo->txrate.mcs = mcs;
1703 }
1704
1705 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1706 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1707 if (sgi) {
1708 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1709 sinfo->txrate.mcs = mcs - 1;
1710 } else {
1711 sinfo->txrate.mcs = mcs;
1712 }
1713
1714 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1715 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1716 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001717 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1718 "invalid rate from stats: %d\n", rate);
1719 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001720 return 0;
1721 }
1722
1723 sinfo->filled |= STATION_INFO_TX_BITRATE;
1724
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301725 if (test_bit(CONNECTED, &vif->flags) &&
1726 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301727 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001728 sinfo->filled |= STATION_INFO_BSS_PARAM;
1729 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301730 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1731 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001732 }
1733
Kalle Valobdcd8172011-07-18 00:22:30 +03001734 return 0;
1735}
1736
1737static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1738 struct cfg80211_pmksa *pmksa)
1739{
1740 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301741 struct ath6kl_vif *vif = netdev_priv(netdev);
1742
1743 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001744 pmksa->pmkid, true);
1745}
1746
1747static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1748 struct cfg80211_pmksa *pmksa)
1749{
1750 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301751 struct ath6kl_vif *vif = netdev_priv(netdev);
1752
1753 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001754 pmksa->pmkid, false);
1755}
1756
1757static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1758{
1759 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301760 struct ath6kl_vif *vif = netdev_priv(netdev);
1761
1762 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301763 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1764 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001765 return 0;
1766}
1767
Raja Manid91e8ee2012-01-30 17:13:10 +05301768static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1769 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001770{
Raja Manid91e8ee2012-01-30 17:13:10 +05301771 int ret, pos;
1772 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001773 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001774
Raja Manid91e8ee2012-01-30 17:13:10 +05301775 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001776 for (i = 0; i < wow->n_patterns; i++) {
1777
1778 /*
1779 * Convert given nl80211 specific mask value to equivalent
1780 * driver specific mask value and send it to the chip along
1781 * with patterns. For example, If the mask value defined in
1782 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1783 * then equivalent driver specific mask value is
1784 * "0xFF 0x00 0xFF 0x00".
1785 */
1786 memset(&mask, 0, sizeof(mask));
1787 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1788 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1789 mask[pos] = 0xFF;
1790 }
1791 /*
1792 * Note: Pattern's offset is not passed as part of wowlan
1793 * parameter from CFG layer. So it's always passed as ZERO
1794 * to the firmware. It means, given WOW patterns are always
1795 * matched from the first byte of received pkt in the firmware.
1796 */
1797 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301798 vif->fw_vif_idx, WOW_LIST_ID,
1799 wow->patterns[i].pattern_len,
1800 0 /* pattern offset */,
1801 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001802 if (ret)
1803 return ret;
1804 }
1805
Raja Manid91e8ee2012-01-30 17:13:10 +05301806 if (wow->disconnect)
1807 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1808
1809 if (wow->magic_pkt)
1810 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1811
1812 if (wow->gtk_rekey_failure)
1813 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1814
1815 if (wow->eap_identity_req)
1816 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1817
1818 if (wow->four_way_handshake)
1819 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1820
1821 return 0;
1822}
1823
1824static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1825{
1826 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1827 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1828 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1829 0x00, 0x08 };
1830 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1831 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1832 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1833 0x00, 0x7f };
1834 u8 unicst_offset = 0;
1835 static const u8 arp_pattern[] = { 0x08, 0x06 };
1836 static const u8 arp_mask[] = { 0xff, 0xff };
1837 u8 arp_offset = 20;
1838 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1839 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1840 u8 discvr_offset = 38;
1841 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1842 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1843 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1844 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1845 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1846 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1847 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1848 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1849 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1850 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1851 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1852 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1853 u8 dhcp_offset = 0;
1854 int ret;
1855
1856 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1857 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1858 vif->fw_vif_idx, WOW_LIST_ID,
1859 sizeof(unicst_pattern), unicst_offset,
1860 unicst_pattern, unicst_mask);
1861 if (ret) {
1862 ath6kl_err("failed to add WOW unicast IP pattern\n");
1863 return ret;
1864 }
1865
1866 /* Setup all ARP pkt pattern */
1867 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1868 vif->fw_vif_idx, WOW_LIST_ID,
1869 sizeof(arp_pattern), arp_offset,
1870 arp_pattern, arp_mask);
1871 if (ret) {
1872 ath6kl_err("failed to add WOW ARP pattern\n");
1873 return ret;
1874 }
1875
1876 /*
1877 * Setup multicast pattern for mDNS 224.0.0.251,
1878 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1879 */
1880 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1881 vif->fw_vif_idx, WOW_LIST_ID,
1882 sizeof(discvr_pattern), discvr_offset,
1883 discvr_pattern, discvr_mask);
1884 if (ret) {
1885 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1886 return ret;
1887 }
1888
1889 /* Setup all DHCP broadcast pkt pattern */
1890 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1891 vif->fw_vif_idx, WOW_LIST_ID,
1892 sizeof(dhcp_pattern), dhcp_offset,
1893 dhcp_pattern, dhcp_mask);
1894 if (ret) {
1895 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
1896 return ret;
1897 }
1898
1899 return 0;
1900}
1901
1902static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
1903{
1904 struct net_device *ndev = vif->ndev;
1905 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1906 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1907 u8 discvr_offset = 38;
1908 u8 mac_mask[ETH_ALEN];
1909 int ret;
1910
1911 /* Setup unicast pkt pattern */
1912 memset(mac_mask, 0xff, ETH_ALEN);
1913 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1914 vif->fw_vif_idx, WOW_LIST_ID,
1915 ETH_ALEN, 0, ndev->dev_addr,
1916 mac_mask);
1917 if (ret) {
1918 ath6kl_err("failed to add WOW unicast pattern\n");
1919 return ret;
1920 }
1921
1922 /*
1923 * Setup multicast pattern for mDNS 224.0.0.251,
1924 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1925 */
1926 if ((ndev->flags & IFF_ALLMULTI) ||
1927 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
1928 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1929 vif->fw_vif_idx, WOW_LIST_ID,
1930 sizeof(discvr_pattern), discvr_offset,
1931 discvr_pattern, discvr_mask);
1932 if (ret) {
1933 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
1934 "pattern\n");
1935 return ret;
1936 }
1937 }
1938
1939 return 0;
1940}
1941
Raja Mani055bde42012-03-21 15:03:37 +05301942static int is_hsleep_mode_procsed(struct ath6kl_vif *vif)
1943{
1944 return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
1945}
1946
1947static bool is_ctrl_ep_empty(struct ath6kl *ar)
1948{
1949 return !ar->tx_pending[ar->ctrl_ep];
1950}
1951
1952static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
1953{
1954 int ret, left;
1955
1956 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
1957
1958 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1959 ATH6KL_HOST_MODE_ASLEEP);
1960 if (ret)
1961 return ret;
1962
1963 left = wait_event_interruptible_timeout(ar->event_wq,
1964 is_hsleep_mode_procsed(vif),
1965 WMI_TIMEOUT);
1966 if (left == 0) {
1967 ath6kl_warn("timeout, didn't get host sleep cmd processed event\n");
1968 ret = -ETIMEDOUT;
1969 } else if (left < 0) {
1970 ath6kl_warn("error while waiting for host sleep cmd processed event %d\n",
1971 left);
1972 ret = left;
1973 }
1974
1975 if (ar->tx_pending[ar->ctrl_ep]) {
1976 left = wait_event_interruptible_timeout(ar->event_wq,
1977 is_ctrl_ep_empty(ar),
1978 WMI_TIMEOUT);
1979 if (left == 0) {
1980 ath6kl_warn("clear wmi ctrl data timeout\n");
1981 ret = -ETIMEDOUT;
1982 } else if (left < 0) {
1983 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
1984 ret = left;
1985 }
1986 }
1987
1988 return ret;
1989}
1990
Raja Manid91e8ee2012-01-30 17:13:10 +05301991static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1992{
1993 struct in_device *in_dev;
1994 struct in_ifaddr *ifa;
1995 struct ath6kl_vif *vif;
Raja Mani055bde42012-03-21 15:03:37 +05301996 int ret;
Raja Manid91e8ee2012-01-30 17:13:10 +05301997 u32 filter = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +05301998 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05301999 u8 index = 0;
2000 __be32 ips[MAX_IP_ADDRS];
2001
2002 vif = ath6kl_vif_first(ar);
2003 if (!vif)
2004 return -EIO;
2005
2006 if (!ath6kl_cfg80211_ready(vif))
2007 return -EIO;
2008
2009 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05302010 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05302011
2012 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
2013 return -EINVAL;
2014
2015 /* Clear existing WOW patterns */
2016 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
2017 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
2018 WOW_LIST_ID, i);
2019
2020 /*
2021 * Skip the default WOW pattern configuration
2022 * if the driver receives any WOW patterns from
2023 * the user.
2024 */
2025 if (wow)
2026 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
2027 else if (vif->nw_type == AP_NETWORK)
2028 ret = ath6kl_wow_ap(ar, vif);
2029 else
2030 ret = ath6kl_wow_sta(ar, vif);
2031
2032 if (ret)
2033 return ret;
2034
Raja Mani390a8c82012-03-07 11:35:04 +05302035 netif_stop_queue(vif->ndev);
2036
Raja Manice0dc0c2012-02-20 19:08:08 +05302037 if (vif->nw_type != AP_NETWORK) {
2038 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2039 ATH6KL_MAX_WOW_LISTEN_INTL,
2040 0);
2041 if (ret)
2042 return ret;
2043
2044 /* Set listen interval x 15 times as bmiss time */
2045 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
2046 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
2047 bmiss_time = ATH6KL_MAX_BMISS_TIME;
2048
2049 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2050 bmiss_time, 0);
2051 if (ret)
2052 return ret;
2053
2054 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2055 0xFFFF, 0, 0xFFFF, 0, 0, 0,
2056 0, 0, 0, 0);
2057 if (ret)
2058 return ret;
2059 }
2060
Raja Mani390a8c82012-03-07 11:35:04 +05302061 ar->state = ATH6KL_STATE_SUSPENDING;
2062
Raja Manic08631c2011-12-16 14:24:24 +05302063 /* Setup own IP addr for ARP agent. */
2064 in_dev = __in_dev_get_rtnl(vif->ndev);
2065 if (!in_dev)
2066 goto skip_arp;
2067
2068 ifa = in_dev->ifa_list;
2069 memset(&ips, 0, sizeof(ips));
2070
2071 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2072 while (index < MAX_IP_ADDRS && ifa) {
2073 ips[index] = ifa->ifa_local;
2074 ifa = ifa->ifa_next;
2075 index++;
2076 }
2077
2078 if (ifa) {
2079 ath6kl_err("total IP addr count is exceeding fw limit\n");
2080 return -EINVAL;
2081 }
2082
2083 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2084 if (ret) {
2085 ath6kl_err("fail to setup ip for arp agent\n");
2086 return ret;
2087 }
2088
2089skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02002090 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2091 ATH6KL_WOW_MODE_ENABLE,
2092 filter,
2093 WOW_HOST_REQ_DELAY);
2094 if (ret)
2095 return ret;
2096
Raja Mani055bde42012-03-21 15:03:37 +05302097 ret = ath6kl_cfg80211_host_sleep(ar, vif);
Raja Mani6cb3c712011-11-07 22:52:45 +02002098 if (ret)
2099 return ret;
2100
Raja Mani055bde42012-03-21 15:03:37 +05302101 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002102}
2103
2104static int ath6kl_wow_resume(struct ath6kl *ar)
2105{
2106 struct ath6kl_vif *vif;
2107 int ret;
2108
2109 vif = ath6kl_vif_first(ar);
2110 if (!vif)
2111 return -EIO;
2112
Raja Mani390a8c82012-03-07 11:35:04 +05302113 ar->state = ATH6KL_STATE_RESUMING;
2114
Raja Mani6cb3c712011-11-07 22:52:45 +02002115 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2116 ATH6KL_HOST_MODE_AWAKE);
Raja Mani390a8c82012-03-07 11:35:04 +05302117 if (ret) {
2118 ath6kl_warn("Failed to configure host sleep mode for "
2119 "wow resume: %d\n", ret);
2120 ar->state = ATH6KL_STATE_WOW;
2121 return ret;
2122 }
2123
Raja Manice0dc0c2012-02-20 19:08:08 +05302124 if (vif->nw_type != AP_NETWORK) {
2125 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2126 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2127 if (ret)
2128 return ret;
2129
2130 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2131 vif->listen_intvl_t, 0);
2132 if (ret)
2133 return ret;
2134
2135 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2136 vif->bmiss_time_t, 0);
2137 if (ret)
2138 return ret;
2139 }
2140
Raja Mani390a8c82012-03-07 11:35:04 +05302141 ar->state = ATH6KL_STATE_ON;
2142
2143 netif_wake_queue(vif->ndev);
2144
2145 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002146}
2147
Raja Mani40abc2d2012-03-21 15:03:38 +05302148static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
2149{
2150 struct ath6kl_vif *vif;
2151 int ret;
2152
2153 vif = ath6kl_vif_first(ar);
2154 if (!vif)
2155 return -EIO;
2156
2157 if (!ath6kl_cfg80211_ready(vif))
2158 return -EIO;
2159
2160 ath6kl_cfg80211_stop_all(ar);
2161
2162 /* Save the current power mode before enabling power save */
2163 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2164
2165 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2166 if (ret)
2167 return ret;
2168
2169 /* Disable WOW mode */
2170 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2171 ATH6KL_WOW_MODE_DISABLE,
2172 0, 0);
2173 if (ret)
2174 return ret;
2175
2176 /* Flush all non control pkts in TX path */
2177 ath6kl_tx_data_cleanup(ar);
2178
2179 ret = ath6kl_cfg80211_host_sleep(ar, vif);
2180 if (ret)
2181 return ret;
2182
2183 return 0;
2184}
2185
2186static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
2187{
2188 struct ath6kl_vif *vif;
2189 int ret;
2190
2191 vif = ath6kl_vif_first(ar);
2192
2193 if (!vif)
2194 return -EIO;
2195
2196 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2197 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2198 ar->wmi->saved_pwr_mode);
2199 if (ret)
2200 return ret;
2201 }
2202
2203 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2204 ATH6KL_HOST_MODE_AWAKE);
2205 if (ret)
2206 return ret;
2207
2208 ar->state = ATH6KL_STATE_ON;
2209
2210 /* Reset scan parameter to default values */
2211 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2212 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2213 if (ret)
2214 return ret;
2215
2216 return 0;
2217}
2218
Kalle Valo52d81a62011-11-01 08:44:21 +02002219int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002220 enum ath6kl_cfg_suspend_mode mode,
2221 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002222{
Raja Mani390a8c82012-03-07 11:35:04 +05302223 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002224 int ret;
2225
Kalle Valo52d81a62011-11-01 08:44:21 +02002226 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002227 case ATH6KL_CFG_SUSPEND_WOW:
2228
2229 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2230
2231 /* Flush all non control pkts in TX path */
2232 ath6kl_tx_data_cleanup(ar);
2233
Raja Mani390a8c82012-03-07 11:35:04 +05302234 prev_state = ar->state;
2235
Raja Manid7c44e02011-11-07 22:52:46 +02002236 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302237 if (ret) {
2238 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002239 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302240 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302241
Raja Manid7c44e02011-11-07 22:52:46 +02002242 ar->state = ATH6KL_STATE_WOW;
2243 break;
2244
Kalle Valo52d81a62011-11-01 08:44:21 +02002245 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002246
Raja Mani40abc2d2012-03-21 15:03:38 +05302247 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
Raja Mani524441e2011-11-07 22:52:46 +02002248
Raja Mani40abc2d2012-03-21 15:03:38 +05302249 ret = ath6kl_cfg80211_deepsleep_suspend(ar);
Kalle Valo52d81a62011-11-01 08:44:21 +02002250 if (ret) {
Raja Mani40abc2d2012-03-21 15:03:38 +05302251 ath6kl_err("deepsleep suspend failed: %d\n", ret);
2252 return ret;
Kalle Valo52d81a62011-11-01 08:44:21 +02002253 }
2254
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002255 ar->state = ATH6KL_STATE_DEEPSLEEP;
2256
Kalle Valo52d81a62011-11-01 08:44:21 +02002257 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002258
2259 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002260
Kalle Valo7125f012011-12-13 14:51:37 +02002261 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002262
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002263 if (ar->state == ATH6KL_STATE_OFF) {
2264 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2265 "suspend hw off, no action for cutpower\n");
2266 break;
2267 }
2268
2269 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2270
2271 ret = ath6kl_init_hw_stop(ar);
2272 if (ret) {
2273 ath6kl_warn("failed to stop hw during suspend: %d\n",
2274 ret);
2275 }
2276
2277 ar->state = ATH6KL_STATE_CUTPOWER;
2278
2279 break;
2280
Kalle Valo10509f92011-12-13 14:52:07 +02002281 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2282 /*
2283 * Nothing needed for schedule scan, firmware is already in
2284 * wow mode and sleeping most of the time.
2285 */
2286 break;
2287
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002288 default:
2289 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002290 }
2291
2292 return 0;
2293}
Kalle Valod6a434d2012-01-17 20:09:36 +02002294EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002295
2296int ath6kl_cfg80211_resume(struct ath6kl *ar)
2297{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002298 int ret;
2299
2300 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002301 case ATH6KL_STATE_WOW:
2302 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2303
2304 ret = ath6kl_wow_resume(ar);
2305 if (ret) {
2306 ath6kl_warn("wow mode resume failed: %d\n", ret);
2307 return ret;
2308 }
2309
Raja Manid7c44e02011-11-07 22:52:46 +02002310 break;
2311
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002312 case ATH6KL_STATE_DEEPSLEEP:
Raja Mani40abc2d2012-03-21 15:03:38 +05302313 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
2314
2315 ret = ath6kl_cfg80211_deepsleep_resume(ar);
2316 if (ret) {
2317 ath6kl_warn("deep sleep resume failed: %d\n", ret);
2318 return ret;
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002319 }
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002320 break;
2321
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002322 case ATH6KL_STATE_CUTPOWER:
2323 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2324
2325 ret = ath6kl_init_hw_start(ar);
2326 if (ret) {
2327 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2328 return ret;
2329 }
Raja Manid7c44e02011-11-07 22:52:46 +02002330 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002331
Kalle Valo10509f92011-12-13 14:52:07 +02002332 case ATH6KL_STATE_SCHED_SCAN:
2333 break;
2334
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002335 default:
2336 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002337 }
2338
2339 return 0;
2340}
Kalle Valod6a434d2012-01-17 20:09:36 +02002341EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002342
Kalle Valoabcb3442011-07-22 08:26:20 +03002343#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002344
2345/* hif layer decides what suspend mode to use */
2346static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002347 struct cfg80211_wowlan *wow)
2348{
2349 struct ath6kl *ar = wiphy_priv(wiphy);
2350
Raja Mani0f60e9f2011-11-07 22:52:45 +02002351 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002352}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002353
Kalle Valo52d81a62011-11-01 08:44:21 +02002354static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002355{
2356 struct ath6kl *ar = wiphy_priv(wiphy);
2357
2358 return ath6kl_hif_resume(ar);
2359}
Raja Mania918fb32011-11-07 22:52:46 +02002360
2361/*
2362 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2363 * both sdio irq wake up and keep power. The target pulls sdio data line to
2364 * wake up the host when WOW pattern matches. This causes sdio irq handler
2365 * is being called in the host side which internally hits ath6kl's RX path.
2366 *
2367 * Since sdio interrupt is not disabled, RX path executes even before
2368 * the host executes the actual resume operation from PM module.
2369 *
2370 * In the current scenario, WOW resume should happen before start processing
2371 * any data from the target. So It's required to perform WOW resume in RX path.
2372 * Ideally we should perform WOW resume only in the actual platform
2373 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2374 *
2375 * ath6kl_check_wow_status() is called from ath6kl_rx().
2376 */
2377void ath6kl_check_wow_status(struct ath6kl *ar)
2378{
Raja Mani390a8c82012-03-07 11:35:04 +05302379 if (ar->state == ATH6KL_STATE_SUSPENDING)
2380 return;
2381
Raja Mania918fb32011-11-07 22:52:46 +02002382 if (ar->state == ATH6KL_STATE_WOW)
2383 ath6kl_cfg80211_resume(ar);
2384}
2385
2386#else
2387
2388void ath6kl_check_wow_status(struct ath6kl *ar)
2389{
2390}
Kalle Valoabcb3442011-07-22 08:26:20 +03002391#endif
2392
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002393static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2394 struct ieee80211_channel *chan,
2395 enum nl80211_channel_type channel_type)
2396{
Sujith Manoharane68f6752011-12-22 12:15:27 +05302397 struct ath6kl_vif *vif;
2398
2399 /*
2400 * 'dev' could be NULL if a channel change is required for the hardware
2401 * device itself, instead of a particular VIF.
2402 *
2403 * FIXME: To be handled properly when monitor mode is supported.
2404 */
2405 if (!dev)
2406 return -EBUSY;
2407
2408 vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002409
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302410 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002411 return -EIO;
2412
2413 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2414 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302415 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002416
2417 return 0;
2418}
2419
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002420static bool ath6kl_is_p2p_ie(const u8 *pos)
2421{
2422 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2423 pos[2] == 0x50 && pos[3] == 0x6f &&
2424 pos[4] == 0x9a && pos[5] == 0x09;
2425}
2426
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302427static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2428 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002429{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302430 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002431 const u8 *pos;
2432 u8 *buf = NULL;
2433 size_t len = 0;
2434 int ret;
2435
2436 /*
2437 * Filter out P2P IE(s) since they will be included depending on
2438 * the Probe Request frame in ath6kl_send_go_probe_resp().
2439 */
2440
2441 if (ies && ies_len) {
2442 buf = kmalloc(ies_len, GFP_KERNEL);
2443 if (buf == NULL)
2444 return -ENOMEM;
2445 pos = ies;
2446 while (pos + 1 < ies + ies_len) {
2447 if (pos + 2 + pos[1] > ies + ies_len)
2448 break;
2449 if (!ath6kl_is_p2p_ie(pos)) {
2450 memcpy(buf + len, pos, 2 + pos[1]);
2451 len += 2 + pos[1];
2452 }
2453 pos += 2 + pos[1];
2454 }
2455 }
2456
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302457 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2458 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002459 kfree(buf);
2460 return ret;
2461}
2462
Johannes Berg88600202012-02-13 15:17:18 +01002463static int ath6kl_set_ies(struct ath6kl_vif *vif,
2464 struct cfg80211_beacon_data *info)
2465{
2466 struct ath6kl *ar = vif->ar;
2467 int res;
2468
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002469 /* this also clears IE in fw if it's not set */
2470 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2471 WMI_FRAME_BEACON,
2472 info->beacon_ies,
2473 info->beacon_ies_len);
2474 if (res)
2475 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002476
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002477 /* this also clears IE in fw if it's not set */
2478 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2479 info->proberesp_ies_len);
2480 if (res)
2481 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002482
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002483 /* this also clears IE in fw if it's not set */
2484 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2485 WMI_FRAME_ASSOC_RESP,
2486 info->assocresp_ies,
2487 info->assocresp_ies_len);
2488 if (res)
2489 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002490
2491 return 0;
2492}
2493
2494static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2495 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002496{
2497 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302498 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002499 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002500 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002501 u8 *ies;
2502 int ies_len;
2503 struct wmi_connect_cmd p;
2504 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302505 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002506
Johannes Berg88600202012-02-13 15:17:18 +01002507 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002508
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302509 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002510 return -EIO;
2511
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302512 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002513 return -EOPNOTSUPP;
2514
Johannes Berg88600202012-02-13 15:17:18 +01002515 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002516
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002517 ar->ap_mode_bkey.valid = false;
2518
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002519 /* TODO:
2520 * info->interval
2521 * info->dtim_period
2522 */
2523
Johannes Berg88600202012-02-13 15:17:18 +01002524 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002525 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002526 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002527 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002528 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002529 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002530 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002531
2532 if (info->ssid == NULL)
2533 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302534 memcpy(vif->ssid, info->ssid, info->ssid_len);
2535 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002536 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002537 hidden = true;
2538
2539 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2540 if (res)
2541 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002542
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302543 ret = ath6kl_set_auth_type(vif, info->auth_type);
2544 if (ret)
2545 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002546
2547 memset(&p, 0, sizeof(p));
2548
2549 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2550 switch (info->crypto.akm_suites[i]) {
2551 case WLAN_AKM_SUITE_8021X:
2552 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2553 p.auth_mode |= WPA_AUTH;
2554 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2555 p.auth_mode |= WPA2_AUTH;
2556 break;
2557 case WLAN_AKM_SUITE_PSK:
2558 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2559 p.auth_mode |= WPA_PSK_AUTH;
2560 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2561 p.auth_mode |= WPA2_PSK_AUTH;
2562 break;
2563 }
2564 }
2565 if (p.auth_mode == 0)
2566 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302567 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002568
2569 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2570 switch (info->crypto.ciphers_pairwise[i]) {
2571 case WLAN_CIPHER_SUITE_WEP40:
2572 case WLAN_CIPHER_SUITE_WEP104:
2573 p.prwise_crypto_type |= WEP_CRYPT;
2574 break;
2575 case WLAN_CIPHER_SUITE_TKIP:
2576 p.prwise_crypto_type |= TKIP_CRYPT;
2577 break;
2578 case WLAN_CIPHER_SUITE_CCMP:
2579 p.prwise_crypto_type |= AES_CRYPT;
2580 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002581 case WLAN_CIPHER_SUITE_SMS4:
2582 p.prwise_crypto_type |= WAPI_CRYPT;
2583 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002584 }
2585 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002586 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002587 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302588 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002589 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302590 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002591
2592 switch (info->crypto.cipher_group) {
2593 case WLAN_CIPHER_SUITE_WEP40:
2594 case WLAN_CIPHER_SUITE_WEP104:
2595 p.grp_crypto_type = WEP_CRYPT;
2596 break;
2597 case WLAN_CIPHER_SUITE_TKIP:
2598 p.grp_crypto_type = TKIP_CRYPT;
2599 break;
2600 case WLAN_CIPHER_SUITE_CCMP:
2601 p.grp_crypto_type = AES_CRYPT;
2602 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002603 case WLAN_CIPHER_SUITE_SMS4:
2604 p.grp_crypto_type = WAPI_CRYPT;
2605 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002606 default:
2607 p.grp_crypto_type = NONE_CRYPT;
2608 break;
2609 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302610 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002611
2612 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302613 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002614
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302615 p.ssid_len = vif->ssid_len;
2616 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2617 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302618 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002619
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302620 /* Enable uAPSD support by default */
2621 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2622 if (res < 0)
2623 return res;
2624
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002625 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2626 p.nw_subtype = SUBTYPE_P2PGO;
2627 } else {
2628 /*
2629 * Due to firmware limitation, it is not possible to
2630 * do P2P mgmt operations in AP mode
2631 */
2632 p.nw_subtype = SUBTYPE_NONE;
2633 }
2634
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05302635 if (info->inactivity_timeout) {
2636 res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
2637 info->inactivity_timeout);
2638 if (res < 0)
2639 return res;
2640 }
2641
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302642 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002643 if (res < 0)
2644 return res;
2645
2646 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002647}
2648
Johannes Berg88600202012-02-13 15:17:18 +01002649static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2650 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002651{
Johannes Berg88600202012-02-13 15:17:18 +01002652 struct ath6kl_vif *vif = netdev_priv(dev);
2653
2654 if (!ath6kl_cfg80211_ready(vif))
2655 return -EIO;
2656
2657 if (vif->next_mode != AP_NETWORK)
2658 return -EOPNOTSUPP;
2659
2660 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002661}
2662
Johannes Berg88600202012-02-13 15:17:18 +01002663static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002664{
2665 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302666 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002667
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302668 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002669 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302670 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002671 return -ENOTCONN;
2672
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302673 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302674 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002675
2676 return 0;
2677}
2678
Jouni Malinen33e53082011-12-27 11:02:56 +02002679static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2680
2681static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2682 u8 *mac)
2683{
2684 struct ath6kl *ar = ath6kl_priv(dev);
2685 struct ath6kl_vif *vif = netdev_priv(dev);
2686 const u8 *addr = mac ? mac : bcast_addr;
2687
2688 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2689 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2690}
2691
Jouni Malinen23875132011-08-30 21:57:53 +03002692static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2693 u8 *mac, struct station_parameters *params)
2694{
2695 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302696 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002697
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302698 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002699 return -EOPNOTSUPP;
2700
2701 /* Use this only for authorizing/unauthorizing a station */
2702 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2703 return -EOPNOTSUPP;
2704
2705 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302706 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2707 WMI_AP_MLME_AUTHORIZE, mac, 0);
2708 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2709 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002710}
2711
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002712static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2713 struct net_device *dev,
2714 struct ieee80211_channel *chan,
2715 enum nl80211_channel_type channel_type,
2716 unsigned int duration,
2717 u64 *cookie)
2718{
2719 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302720 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002721 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002722
2723 /* TODO: if already pending or ongoing remain-on-channel,
2724 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002725 id = ++vif->last_roc_id;
2726 if (id == 0) {
2727 /* Do not use 0 as the cookie value */
2728 id = ++vif->last_roc_id;
2729 }
2730 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002731
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302732 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2733 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002734}
2735
2736static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2737 struct net_device *dev,
2738 u64 cookie)
2739{
2740 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302741 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002742
Jouni Malinen10522612011-10-27 16:00:13 +03002743 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002744 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002745 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002746
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302747 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002748}
2749
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302750static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2751 const u8 *buf, size_t len,
2752 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002753{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302754 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002755 const u8 *pos;
2756 u8 *p2p;
2757 int p2p_len;
2758 int ret;
2759 const struct ieee80211_mgmt *mgmt;
2760
2761 mgmt = (const struct ieee80211_mgmt *) buf;
2762
2763 /* Include P2P IE(s) from the frame generated in user space. */
2764
2765 p2p = kmalloc(len, GFP_KERNEL);
2766 if (p2p == NULL)
2767 return -ENOMEM;
2768 p2p_len = 0;
2769
2770 pos = mgmt->u.probe_resp.variable;
2771 while (pos + 1 < buf + len) {
2772 if (pos + 2 + pos[1] > buf + len)
2773 break;
2774 if (ath6kl_is_p2p_ie(pos)) {
2775 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2776 p2p_len += 2 + pos[1];
2777 }
2778 pos += 2 + pos[1];
2779 }
2780
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302781 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2782 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002783 kfree(p2p);
2784 return ret;
2785}
2786
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002787static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
2788 u32 id,
2789 u32 freq,
2790 u32 wait,
2791 const u8 *buf,
2792 size_t len,
2793 bool *more_data,
2794 bool no_cck)
2795{
2796 struct ieee80211_mgmt *mgmt;
2797 struct ath6kl_sta *conn;
2798 bool is_psq_empty = false;
2799 struct ath6kl_mgmt_buff *mgmt_buf;
2800 size_t mgmt_buf_size;
2801 struct ath6kl *ar = vif->ar;
2802
2803 mgmt = (struct ieee80211_mgmt *) buf;
2804 if (is_multicast_ether_addr(mgmt->da))
2805 return false;
2806
2807 conn = ath6kl_find_sta(vif, mgmt->da);
2808 if (!conn)
2809 return false;
2810
2811 if (conn->sta_flags & STA_PS_SLEEP) {
2812 if (!(conn->sta_flags & STA_PS_POLLED)) {
2813 /* Queue the frames if the STA is sleeping */
2814 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
2815 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
2816 if (!mgmt_buf)
2817 return false;
2818
2819 INIT_LIST_HEAD(&mgmt_buf->list);
2820 mgmt_buf->id = id;
2821 mgmt_buf->freq = freq;
2822 mgmt_buf->wait = wait;
2823 mgmt_buf->len = len;
2824 mgmt_buf->no_cck = no_cck;
2825 memcpy(mgmt_buf->buf, buf, len);
2826 spin_lock_bh(&conn->psq_lock);
2827 is_psq_empty = skb_queue_empty(&conn->psq) &&
2828 (conn->mgmt_psq_len == 0);
2829 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
2830 conn->mgmt_psq_len++;
2831 spin_unlock_bh(&conn->psq_lock);
2832
2833 /*
2834 * If this is the first pkt getting queued
2835 * for this STA, update the PVB for this
2836 * STA.
2837 */
2838 if (is_psq_empty)
2839 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
2840 conn->aid, 1);
2841 return true;
2842 }
2843
2844 /*
2845 * This tx is because of a PsPoll.
2846 * Determine if MoreData bit has to be set.
2847 */
2848 spin_lock_bh(&conn->psq_lock);
2849 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
2850 *more_data = true;
2851 spin_unlock_bh(&conn->psq_lock);
2852 }
2853
2854 return false;
2855}
2856
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07002857/* Check if SSID length is greater than DIRECT- */
2858static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
2859{
2860 const struct ieee80211_mgmt *mgmt;
2861 mgmt = (const struct ieee80211_mgmt *) buf;
2862
2863 /* variable[1] contains the SSID tag length */
2864 if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
2865 (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
2866 return true;
2867 }
2868
2869 return false;
2870}
2871
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002872static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2873 struct ieee80211_channel *chan, bool offchan,
2874 enum nl80211_channel_type channel_type,
2875 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002876 const u8 *buf, size_t len, bool no_cck,
2877 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002878{
2879 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302880 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002881 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002882 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002883 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002884
2885 mgmt = (const struct ieee80211_mgmt *) buf;
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07002886 if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
2887 ieee80211_is_probe_resp(mgmt->frame_control) &&
2888 ath6kl_is_p2p_go_ssid(buf, len)) {
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002889 /*
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07002890 * Send Probe Response frame in GO mode using a separate WMI
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002891 * command to allow the target to fill in the generic IEs.
2892 */
2893 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302894 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002895 chan->center_freq);
2896 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002897
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302898 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002899 if (id == 0) {
2900 /*
2901 * 0 is a reserved value in the WMI command and shall not be
2902 * used for the command.
2903 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302904 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002905 }
2906
2907 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002908
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002909 /* AP mode Power saving processing */
2910 if (vif->nw_type == AP_NETWORK) {
2911 queued = ath6kl_mgmt_powersave_ap(vif,
2912 id, chan->center_freq,
2913 wait, buf,
2914 len, &more_data, no_cck);
2915 if (queued)
2916 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002917 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002918
2919 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2920 chan->center_freq, wait,
2921 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002922}
2923
Jouni Malinenae32c302011-08-30 21:58:01 +03002924static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2925 struct net_device *dev,
2926 u16 frame_type, bool reg)
2927{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302928 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002929
2930 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2931 __func__, frame_type, reg);
2932 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2933 /*
2934 * Note: This notification callback is not allowed to sleep, so
2935 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2936 * hardcode target to report Probe Request frames all the time.
2937 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302938 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002939 }
2940}
2941
Kalle Valo10509f92011-12-13 14:52:07 +02002942static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2943 struct net_device *dev,
2944 struct cfg80211_sched_scan_request *request)
2945{
2946 struct ath6kl *ar = ath6kl_priv(dev);
2947 struct ath6kl_vif *vif = netdev_priv(dev);
2948 u16 interval;
2949 int ret;
2950 u8 i;
2951
2952 if (ar->state != ATH6KL_STATE_ON)
2953 return -EIO;
2954
2955 if (vif->sme_state != SME_DISCONNECTED)
2956 return -EBUSY;
2957
Kalle Valob4d13d32012-03-21 10:01:09 +02002958 ath6kl_cfg80211_scan_complete_event(vif, true);
2959
Kalle Valo10509f92011-12-13 14:52:07 +02002960 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2961 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2962 i, DISABLE_SSID_FLAG,
2963 0, NULL);
2964 }
2965
2966 /* fw uses seconds, also make sure that it's >0 */
2967 interval = max_t(u16, 1, request->interval / 1000);
2968
2969 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2970 interval, interval,
2971 10, 0, 0, 0, 3, 0, 0, 0);
2972
2973 if (request->n_ssids && request->ssids[0].ssid_len) {
2974 for (i = 0; i < request->n_ssids; i++) {
2975 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2976 i, SPECIFIC_SSID_FLAG,
2977 request->ssids[i].ssid_len,
2978 request->ssids[i].ssid);
2979 }
2980 }
2981
2982 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2983 ATH6KL_WOW_MODE_ENABLE,
2984 WOW_FILTER_SSID,
2985 WOW_HOST_REQ_DELAY);
2986 if (ret) {
2987 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2988 return ret;
2989 }
2990
2991 /* this also clears IE in fw if it's not set */
2992 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2993 WMI_FRAME_PROBE_REQ,
2994 request->ie, request->ie_len);
2995 if (ret) {
2996 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
2997 ret);
2998 return ret;
2999 }
3000
3001 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
3002 ATH6KL_HOST_MODE_ASLEEP);
3003 if (ret) {
3004 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
3005 ret);
3006 return ret;
3007 }
3008
3009 ar->state = ATH6KL_STATE_SCHED_SCAN;
3010
3011 return ret;
3012}
3013
3014static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
3015 struct net_device *dev)
3016{
3017 struct ath6kl_vif *vif = netdev_priv(dev);
3018 bool stopped;
3019
3020 stopped = __ath6kl_cfg80211_sscan_stop(vif);
3021
3022 if (!stopped)
3023 return -EIO;
3024
3025 return 0;
3026}
3027
Jouni Malinenf80574a2011-08-30 21:58:04 +03003028static const struct ieee80211_txrx_stypes
3029ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
3030 [NL80211_IFTYPE_STATION] = {
3031 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3032 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3033 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3034 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3035 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02003036 [NL80211_IFTYPE_AP] = {
3037 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3038 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3039 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3040 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3041 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03003042 [NL80211_IFTYPE_P2P_CLIENT] = {
3043 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3044 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3045 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3046 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3047 },
3048 [NL80211_IFTYPE_P2P_GO] = {
3049 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3050 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3051 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3052 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3053 },
3054};
3055
Kalle Valobdcd8172011-07-18 00:22:30 +03003056static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303057 .add_virtual_intf = ath6kl_cfg80211_add_iface,
3058 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03003059 .change_virtual_intf = ath6kl_cfg80211_change_iface,
3060 .scan = ath6kl_cfg80211_scan,
3061 .connect = ath6kl_cfg80211_connect,
3062 .disconnect = ath6kl_cfg80211_disconnect,
3063 .add_key = ath6kl_cfg80211_add_key,
3064 .get_key = ath6kl_cfg80211_get_key,
3065 .del_key = ath6kl_cfg80211_del_key,
3066 .set_default_key = ath6kl_cfg80211_set_default_key,
3067 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
3068 .set_tx_power = ath6kl_cfg80211_set_txpower,
3069 .get_tx_power = ath6kl_cfg80211_get_txpower,
3070 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
3071 .join_ibss = ath6kl_cfg80211_join_ibss,
3072 .leave_ibss = ath6kl_cfg80211_leave_ibss,
3073 .get_station = ath6kl_get_station,
3074 .set_pmksa = ath6kl_set_pmksa,
3075 .del_pmksa = ath6kl_del_pmksa,
3076 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03003077 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03003078#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02003079 .suspend = __ath6kl_cfg80211_suspend,
3080 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03003081#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03003082 .set_channel = ath6kl_set_channel,
Johannes Berg88600202012-02-13 15:17:18 +01003083 .start_ap = ath6kl_start_ap,
3084 .change_beacon = ath6kl_change_beacon,
3085 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02003086 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03003087 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003088 .remain_on_channel = ath6kl_remain_on_channel,
3089 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003090 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03003091 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02003092 .sched_scan_start = ath6kl_cfg80211_sscan_start,
3093 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03003094};
3095
Kalle Valo7125f012011-12-13 14:51:37 +02003096void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02003097{
Kalle Valo10509f92011-12-13 14:52:07 +02003098 ath6kl_cfg80211_sscan_disable(vif);
3099
Kalle Valoec4b7f62011-11-01 08:44:04 +02003100 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02003101 case SME_DISCONNECTED:
3102 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02003103 case SME_CONNECTING:
3104 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
3105 NULL, 0,
3106 WLAN_STATUS_UNSPECIFIED_FAILURE,
3107 GFP_KERNEL);
3108 break;
3109 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02003110 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
3111 break;
3112 }
3113
3114 if (test_bit(CONNECTED, &vif->flags) ||
3115 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02003116 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003117
3118 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02003119 clear_bit(CONNECTED, &vif->flags);
3120 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003121
3122 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02003123 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
3124 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
3125 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003126
3127 ath6kl_cfg80211_scan_complete_event(vif, true);
3128}
3129
Kalle Valo7125f012011-12-13 14:51:37 +02003130void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3131{
3132 struct ath6kl_vif *vif;
3133
3134 vif = ath6kl_vif_first(ar);
3135 if (!vif) {
3136 /* save the current power mode before enabling power save */
3137 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3138
3139 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
3140 ath6kl_warn("ath6kl_deep_sleep_enable: "
3141 "wmi_powermode_cmd failed\n");
3142 return;
3143 }
3144
3145 /*
3146 * FIXME: we should take ar->list_lock to protect changes in the
3147 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3148 * sleeps.
3149 */
3150 list_for_each_entry(vif, &ar->vif_list, list)
3151 ath6kl_cfg80211_stop(vif);
3152}
3153
Kalle Valoc25889e2012-01-17 20:08:27 +02003154static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003155{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303156 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303157 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303158 ath6kl_err("failed to initialize aggr\n");
3159 return -ENOMEM;
3160 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003161
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303162 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303163 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003164 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3165 (unsigned long) vif);
3166
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303167 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303168 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303169
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303170 INIT_LIST_HEAD(&vif->mc_filter);
3171
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303172 return 0;
3173}
3174
Kalle Valoc25889e2012-01-17 20:08:27 +02003175void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303176{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303177 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303178 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303179
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303180 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303181
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303182 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3183
3184 if (vif->nw_type == ADHOC_NETWORK)
3185 ar->ibss_if_active = false;
3186
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303187 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3188 list_del(&mc_filter->list);
3189 kfree(mc_filter);
3190 }
3191
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303192 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303193
3194 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303195}
3196
3197struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303198 enum nl80211_iftype type, u8 fw_vif_idx,
3199 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303200{
3201 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303202 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303203
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303204 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303205 if (!ndev)
3206 return NULL;
3207
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303208 vif = netdev_priv(ndev);
3209 ndev->ieee80211_ptr = &vif->wdev;
3210 vif->wdev.wiphy = ar->wiphy;
3211 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303212 vif->ndev = ndev;
3213 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3214 vif->wdev.netdev = ndev;
3215 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303216 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003217 vif->nw_type = nw_type;
3218 vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303219 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303220 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303221
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303222 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
3223 if (fw_vif_idx != 0)
3224 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3225 0x2;
3226
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303227 init_netdev(ndev);
3228
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303229 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303230
Kalle Valoc25889e2012-01-17 20:08:27 +02003231 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303232 goto err;
3233
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303234 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303235 goto err;
3236
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303237 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303238 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303239 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303240 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303241 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303242
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303243 if (type == NL80211_IFTYPE_ADHOC)
3244 ar->ibss_if_active = true;
3245
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303246 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303247 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303248 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303249
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303250 return ndev;
3251
3252err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303253 aggr_module_destroy(vif->aggr_cntxt);
3254 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303255 return NULL;
3256}
3257
Kalle Valo46d33a22012-01-17 20:08:40 +02003258int ath6kl_cfg80211_init(struct ath6kl *ar)
3259{
3260 struct wiphy *wiphy = ar->wiphy;
3261 int ret;
3262
3263 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3264
3265 wiphy->max_remain_on_channel_duration = 5000;
3266
3267 /* set device pointer for wiphy */
3268 set_wiphy_dev(wiphy, ar->dev);
3269
3270 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3271 BIT(NL80211_IFTYPE_ADHOC) |
3272 BIT(NL80211_IFTYPE_AP);
3273 if (ar->p2p) {
3274 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3275 BIT(NL80211_IFTYPE_P2P_CLIENT);
3276 }
3277
3278 /* max num of ssids that can be probed during scanning */
3279 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
3280 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
3281 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3282 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3283 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3284
3285 wiphy->cipher_suites = cipher_suites;
3286 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3287
3288 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3289 WIPHY_WOWLAN_DISCONNECT |
3290 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3291 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3292 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3293 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3294 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3295 wiphy->wowlan.pattern_min_len = 1;
3296 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
3297
3298 wiphy->max_sched_scan_ssids = 10;
3299
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303300 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3301 WIPHY_FLAG_HAVE_AP_SME |
3302 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3303 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3304
3305 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3306 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3307
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303308 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
3309 ar->fw_capabilities))
3310 ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER;
3311
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303312 ar->wiphy->probe_resp_offload =
3313 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3314 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
3315 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
3316 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
3317
Kalle Valo46d33a22012-01-17 20:08:40 +02003318 ret = wiphy_register(wiphy);
3319 if (ret < 0) {
3320 ath6kl_err("couldn't register wiphy device\n");
3321 return ret;
3322 }
3323
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303324 ar->wiphy_registered = true;
3325
Kalle Valo46d33a22012-01-17 20:08:40 +02003326 return 0;
3327}
3328
3329void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303330{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303331 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303332
3333 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003334}
Kalle Valo46d33a22012-01-17 20:08:40 +02003335
Kalle Valo45eaa782012-01-17 20:09:05 +02003336struct ath6kl *ath6kl_cfg80211_create(void)
3337{
3338 struct ath6kl *ar;
3339 struct wiphy *wiphy;
3340
3341 /* create a new wiphy for use with cfg80211 */
3342 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3343
3344 if (!wiphy) {
3345 ath6kl_err("couldn't allocate wiphy device\n");
3346 return NULL;
3347 }
3348
3349 ar = wiphy_priv(wiphy);
3350 ar->wiphy = wiphy;
3351
3352 return ar;
3353}
3354
3355/* Note: ar variable must not be accessed after calling this! */
3356void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3357{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303358 int i;
3359
3360 for (i = 0; i < AP_MAX_NUM_STA; i++)
3361 kfree(ar->sta_list[i].aggr_conn);
3362
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303363 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003364}
Kalle Valo45eaa782012-01-17 20:09:05 +02003365