blob: 1272508513f75bf2899586e41534318cd9bf6c62 [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{
Vivek Natarajan3d794992012-03-28 19:21:26 +05302223 struct ath6kl_vif *vif;
Raja Mani390a8c82012-03-07 11:35:04 +05302224 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002225 int ret;
2226
Kalle Valo52d81a62011-11-01 08:44:21 +02002227 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002228 case ATH6KL_CFG_SUSPEND_WOW:
2229
2230 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2231
2232 /* Flush all non control pkts in TX path */
2233 ath6kl_tx_data_cleanup(ar);
2234
Raja Mani390a8c82012-03-07 11:35:04 +05302235 prev_state = ar->state;
2236
Raja Manid7c44e02011-11-07 22:52:46 +02002237 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302238 if (ret) {
2239 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002240 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302241 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302242
Raja Manid7c44e02011-11-07 22:52:46 +02002243 ar->state = ATH6KL_STATE_WOW;
2244 break;
2245
Kalle Valo52d81a62011-11-01 08:44:21 +02002246 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002247
Raja Mani40abc2d2012-03-21 15:03:38 +05302248 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
Raja Mani524441e2011-11-07 22:52:46 +02002249
Raja Mani40abc2d2012-03-21 15:03:38 +05302250 ret = ath6kl_cfg80211_deepsleep_suspend(ar);
Kalle Valo52d81a62011-11-01 08:44:21 +02002251 if (ret) {
Raja Mani40abc2d2012-03-21 15:03:38 +05302252 ath6kl_err("deepsleep suspend failed: %d\n", ret);
2253 return ret;
Kalle Valo52d81a62011-11-01 08:44:21 +02002254 }
2255
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002256 ar->state = ATH6KL_STATE_DEEPSLEEP;
2257
Kalle Valo52d81a62011-11-01 08:44:21 +02002258 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002259
2260 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002261
Kalle Valo7125f012011-12-13 14:51:37 +02002262 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002263
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002264 if (ar->state == ATH6KL_STATE_OFF) {
2265 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2266 "suspend hw off, no action for cutpower\n");
2267 break;
2268 }
2269
2270 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2271
2272 ret = ath6kl_init_hw_stop(ar);
2273 if (ret) {
2274 ath6kl_warn("failed to stop hw during suspend: %d\n",
2275 ret);
2276 }
2277
2278 ar->state = ATH6KL_STATE_CUTPOWER;
2279
2280 break;
2281
Kalle Valo10509f92011-12-13 14:52:07 +02002282 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2283 /*
2284 * Nothing needed for schedule scan, firmware is already in
2285 * wow mode and sleeping most of the time.
2286 */
2287 break;
2288
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002289 default:
2290 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002291 }
2292
Vivek Natarajan3d794992012-03-28 19:21:26 +05302293 list_for_each_entry(vif, &ar->vif_list, list)
2294 ath6kl_cfg80211_scan_complete_event(vif, true);
2295
Kalle Valo52d81a62011-11-01 08:44:21 +02002296 return 0;
2297}
Kalle Valod6a434d2012-01-17 20:09:36 +02002298EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002299
2300int ath6kl_cfg80211_resume(struct ath6kl *ar)
2301{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002302 int ret;
2303
2304 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002305 case ATH6KL_STATE_WOW:
2306 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2307
2308 ret = ath6kl_wow_resume(ar);
2309 if (ret) {
2310 ath6kl_warn("wow mode resume failed: %d\n", ret);
2311 return ret;
2312 }
2313
Raja Manid7c44e02011-11-07 22:52:46 +02002314 break;
2315
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002316 case ATH6KL_STATE_DEEPSLEEP:
Raja Mani40abc2d2012-03-21 15:03:38 +05302317 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
2318
2319 ret = ath6kl_cfg80211_deepsleep_resume(ar);
2320 if (ret) {
2321 ath6kl_warn("deep sleep resume failed: %d\n", ret);
2322 return ret;
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002323 }
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002324 break;
2325
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002326 case ATH6KL_STATE_CUTPOWER:
2327 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2328
2329 ret = ath6kl_init_hw_start(ar);
2330 if (ret) {
2331 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2332 return ret;
2333 }
Raja Manid7c44e02011-11-07 22:52:46 +02002334 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002335
Kalle Valo10509f92011-12-13 14:52:07 +02002336 case ATH6KL_STATE_SCHED_SCAN:
2337 break;
2338
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002339 default:
2340 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002341 }
2342
2343 return 0;
2344}
Kalle Valod6a434d2012-01-17 20:09:36 +02002345EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002346
Kalle Valoabcb3442011-07-22 08:26:20 +03002347#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002348
2349/* hif layer decides what suspend mode to use */
2350static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002351 struct cfg80211_wowlan *wow)
2352{
2353 struct ath6kl *ar = wiphy_priv(wiphy);
2354
Raja Mani0f60e9f2011-11-07 22:52:45 +02002355 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002356}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002357
Kalle Valo52d81a62011-11-01 08:44:21 +02002358static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002359{
2360 struct ath6kl *ar = wiphy_priv(wiphy);
2361
2362 return ath6kl_hif_resume(ar);
2363}
Raja Mania918fb32011-11-07 22:52:46 +02002364
2365/*
2366 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2367 * both sdio irq wake up and keep power. The target pulls sdio data line to
2368 * wake up the host when WOW pattern matches. This causes sdio irq handler
2369 * is being called in the host side which internally hits ath6kl's RX path.
2370 *
2371 * Since sdio interrupt is not disabled, RX path executes even before
2372 * the host executes the actual resume operation from PM module.
2373 *
2374 * In the current scenario, WOW resume should happen before start processing
2375 * any data from the target. So It's required to perform WOW resume in RX path.
2376 * Ideally we should perform WOW resume only in the actual platform
2377 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2378 *
2379 * ath6kl_check_wow_status() is called from ath6kl_rx().
2380 */
2381void ath6kl_check_wow_status(struct ath6kl *ar)
2382{
Raja Mani390a8c82012-03-07 11:35:04 +05302383 if (ar->state == ATH6KL_STATE_SUSPENDING)
2384 return;
2385
Raja Mania918fb32011-11-07 22:52:46 +02002386 if (ar->state == ATH6KL_STATE_WOW)
2387 ath6kl_cfg80211_resume(ar);
2388}
2389
2390#else
2391
2392void ath6kl_check_wow_status(struct ath6kl *ar)
2393{
2394}
Kalle Valoabcb3442011-07-22 08:26:20 +03002395#endif
2396
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002397static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2398 struct ieee80211_channel *chan,
2399 enum nl80211_channel_type channel_type)
2400{
Sujith Manoharane68f6752011-12-22 12:15:27 +05302401 struct ath6kl_vif *vif;
2402
2403 /*
2404 * 'dev' could be NULL if a channel change is required for the hardware
2405 * device itself, instead of a particular VIF.
2406 *
2407 * FIXME: To be handled properly when monitor mode is supported.
2408 */
2409 if (!dev)
2410 return -EBUSY;
2411
2412 vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002413
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302414 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002415 return -EIO;
2416
2417 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2418 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302419 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002420
2421 return 0;
2422}
2423
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002424static bool ath6kl_is_p2p_ie(const u8 *pos)
2425{
2426 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2427 pos[2] == 0x50 && pos[3] == 0x6f &&
2428 pos[4] == 0x9a && pos[5] == 0x09;
2429}
2430
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302431static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2432 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002433{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302434 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002435 const u8 *pos;
2436 u8 *buf = NULL;
2437 size_t len = 0;
2438 int ret;
2439
2440 /*
2441 * Filter out P2P IE(s) since they will be included depending on
2442 * the Probe Request frame in ath6kl_send_go_probe_resp().
2443 */
2444
2445 if (ies && ies_len) {
2446 buf = kmalloc(ies_len, GFP_KERNEL);
2447 if (buf == NULL)
2448 return -ENOMEM;
2449 pos = ies;
2450 while (pos + 1 < ies + ies_len) {
2451 if (pos + 2 + pos[1] > ies + ies_len)
2452 break;
2453 if (!ath6kl_is_p2p_ie(pos)) {
2454 memcpy(buf + len, pos, 2 + pos[1]);
2455 len += 2 + pos[1];
2456 }
2457 pos += 2 + pos[1];
2458 }
2459 }
2460
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302461 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2462 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002463 kfree(buf);
2464 return ret;
2465}
2466
Johannes Berg88600202012-02-13 15:17:18 +01002467static int ath6kl_set_ies(struct ath6kl_vif *vif,
2468 struct cfg80211_beacon_data *info)
2469{
2470 struct ath6kl *ar = vif->ar;
2471 int res;
2472
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002473 /* this also clears IE in fw if it's not set */
2474 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2475 WMI_FRAME_BEACON,
2476 info->beacon_ies,
2477 info->beacon_ies_len);
2478 if (res)
2479 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002480
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002481 /* this also clears IE in fw if it's not set */
2482 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2483 info->proberesp_ies_len);
2484 if (res)
2485 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002486
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002487 /* this also clears IE in fw if it's not set */
2488 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2489 WMI_FRAME_ASSOC_RESP,
2490 info->assocresp_ies,
2491 info->assocresp_ies_len);
2492 if (res)
2493 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002494
2495 return 0;
2496}
2497
2498static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2499 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002500{
2501 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302502 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002503 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002504 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002505 u8 *ies;
2506 int ies_len;
2507 struct wmi_connect_cmd p;
2508 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302509 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002510
Johannes Berg88600202012-02-13 15:17:18 +01002511 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002512
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302513 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002514 return -EIO;
2515
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302516 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002517 return -EOPNOTSUPP;
2518
Johannes Berg88600202012-02-13 15:17:18 +01002519 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002520
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002521 ar->ap_mode_bkey.valid = false;
2522
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002523 /* TODO:
2524 * info->interval
2525 * info->dtim_period
2526 */
2527
Johannes Berg88600202012-02-13 15:17:18 +01002528 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002529 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002530 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002531 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002532 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002533 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002534 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002535
2536 if (info->ssid == NULL)
2537 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302538 memcpy(vif->ssid, info->ssid, info->ssid_len);
2539 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002540 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002541 hidden = true;
2542
2543 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2544 if (res)
2545 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002546
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302547 ret = ath6kl_set_auth_type(vif, info->auth_type);
2548 if (ret)
2549 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002550
2551 memset(&p, 0, sizeof(p));
2552
2553 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2554 switch (info->crypto.akm_suites[i]) {
2555 case WLAN_AKM_SUITE_8021X:
2556 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2557 p.auth_mode |= WPA_AUTH;
2558 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2559 p.auth_mode |= WPA2_AUTH;
2560 break;
2561 case WLAN_AKM_SUITE_PSK:
2562 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2563 p.auth_mode |= WPA_PSK_AUTH;
2564 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2565 p.auth_mode |= WPA2_PSK_AUTH;
2566 break;
2567 }
2568 }
2569 if (p.auth_mode == 0)
2570 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302571 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002572
2573 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2574 switch (info->crypto.ciphers_pairwise[i]) {
2575 case WLAN_CIPHER_SUITE_WEP40:
2576 case WLAN_CIPHER_SUITE_WEP104:
2577 p.prwise_crypto_type |= WEP_CRYPT;
2578 break;
2579 case WLAN_CIPHER_SUITE_TKIP:
2580 p.prwise_crypto_type |= TKIP_CRYPT;
2581 break;
2582 case WLAN_CIPHER_SUITE_CCMP:
2583 p.prwise_crypto_type |= AES_CRYPT;
2584 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002585 case WLAN_CIPHER_SUITE_SMS4:
2586 p.prwise_crypto_type |= WAPI_CRYPT;
2587 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002588 }
2589 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002590 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002591 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302592 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002593 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302594 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002595
2596 switch (info->crypto.cipher_group) {
2597 case WLAN_CIPHER_SUITE_WEP40:
2598 case WLAN_CIPHER_SUITE_WEP104:
2599 p.grp_crypto_type = WEP_CRYPT;
2600 break;
2601 case WLAN_CIPHER_SUITE_TKIP:
2602 p.grp_crypto_type = TKIP_CRYPT;
2603 break;
2604 case WLAN_CIPHER_SUITE_CCMP:
2605 p.grp_crypto_type = AES_CRYPT;
2606 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002607 case WLAN_CIPHER_SUITE_SMS4:
2608 p.grp_crypto_type = WAPI_CRYPT;
2609 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002610 default:
2611 p.grp_crypto_type = NONE_CRYPT;
2612 break;
2613 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302614 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002615
2616 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302617 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002618
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302619 p.ssid_len = vif->ssid_len;
2620 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2621 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302622 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002623
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302624 /* Enable uAPSD support by default */
2625 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2626 if (res < 0)
2627 return res;
2628
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002629 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2630 p.nw_subtype = SUBTYPE_P2PGO;
2631 } else {
2632 /*
2633 * Due to firmware limitation, it is not possible to
2634 * do P2P mgmt operations in AP mode
2635 */
2636 p.nw_subtype = SUBTYPE_NONE;
2637 }
2638
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05302639 if (info->inactivity_timeout) {
2640 res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
2641 info->inactivity_timeout);
2642 if (res < 0)
2643 return res;
2644 }
2645
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302646 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002647 if (res < 0)
2648 return res;
2649
2650 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002651}
2652
Johannes Berg88600202012-02-13 15:17:18 +01002653static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2654 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002655{
Johannes Berg88600202012-02-13 15:17:18 +01002656 struct ath6kl_vif *vif = netdev_priv(dev);
2657
2658 if (!ath6kl_cfg80211_ready(vif))
2659 return -EIO;
2660
2661 if (vif->next_mode != AP_NETWORK)
2662 return -EOPNOTSUPP;
2663
2664 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002665}
2666
Johannes Berg88600202012-02-13 15:17:18 +01002667static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002668{
2669 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302670 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002671
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302672 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002673 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302674 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002675 return -ENOTCONN;
2676
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302677 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302678 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002679
2680 return 0;
2681}
2682
Jouni Malinen33e53082011-12-27 11:02:56 +02002683static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2684
2685static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2686 u8 *mac)
2687{
2688 struct ath6kl *ar = ath6kl_priv(dev);
2689 struct ath6kl_vif *vif = netdev_priv(dev);
2690 const u8 *addr = mac ? mac : bcast_addr;
2691
2692 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2693 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2694}
2695
Jouni Malinen23875132011-08-30 21:57:53 +03002696static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2697 u8 *mac, struct station_parameters *params)
2698{
2699 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302700 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002701
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302702 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002703 return -EOPNOTSUPP;
2704
2705 /* Use this only for authorizing/unauthorizing a station */
2706 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2707 return -EOPNOTSUPP;
2708
2709 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302710 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2711 WMI_AP_MLME_AUTHORIZE, mac, 0);
2712 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2713 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002714}
2715
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002716static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2717 struct net_device *dev,
2718 struct ieee80211_channel *chan,
2719 enum nl80211_channel_type channel_type,
2720 unsigned int duration,
2721 u64 *cookie)
2722{
2723 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302724 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002725 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002726
2727 /* TODO: if already pending or ongoing remain-on-channel,
2728 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002729 id = ++vif->last_roc_id;
2730 if (id == 0) {
2731 /* Do not use 0 as the cookie value */
2732 id = ++vif->last_roc_id;
2733 }
2734 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002735
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302736 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2737 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002738}
2739
2740static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2741 struct net_device *dev,
2742 u64 cookie)
2743{
2744 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302745 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002746
Jouni Malinen10522612011-10-27 16:00:13 +03002747 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002748 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002749 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002750
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302751 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002752}
2753
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302754static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2755 const u8 *buf, size_t len,
2756 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002757{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302758 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002759 const u8 *pos;
2760 u8 *p2p;
2761 int p2p_len;
2762 int ret;
2763 const struct ieee80211_mgmt *mgmt;
2764
2765 mgmt = (const struct ieee80211_mgmt *) buf;
2766
2767 /* Include P2P IE(s) from the frame generated in user space. */
2768
2769 p2p = kmalloc(len, GFP_KERNEL);
2770 if (p2p == NULL)
2771 return -ENOMEM;
2772 p2p_len = 0;
2773
2774 pos = mgmt->u.probe_resp.variable;
2775 while (pos + 1 < buf + len) {
2776 if (pos + 2 + pos[1] > buf + len)
2777 break;
2778 if (ath6kl_is_p2p_ie(pos)) {
2779 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2780 p2p_len += 2 + pos[1];
2781 }
2782 pos += 2 + pos[1];
2783 }
2784
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302785 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2786 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002787 kfree(p2p);
2788 return ret;
2789}
2790
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002791static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
2792 u32 id,
2793 u32 freq,
2794 u32 wait,
2795 const u8 *buf,
2796 size_t len,
2797 bool *more_data,
2798 bool no_cck)
2799{
2800 struct ieee80211_mgmt *mgmt;
2801 struct ath6kl_sta *conn;
2802 bool is_psq_empty = false;
2803 struct ath6kl_mgmt_buff *mgmt_buf;
2804 size_t mgmt_buf_size;
2805 struct ath6kl *ar = vif->ar;
2806
2807 mgmt = (struct ieee80211_mgmt *) buf;
2808 if (is_multicast_ether_addr(mgmt->da))
2809 return false;
2810
2811 conn = ath6kl_find_sta(vif, mgmt->da);
2812 if (!conn)
2813 return false;
2814
2815 if (conn->sta_flags & STA_PS_SLEEP) {
2816 if (!(conn->sta_flags & STA_PS_POLLED)) {
2817 /* Queue the frames if the STA is sleeping */
2818 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
2819 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
2820 if (!mgmt_buf)
2821 return false;
2822
2823 INIT_LIST_HEAD(&mgmt_buf->list);
2824 mgmt_buf->id = id;
2825 mgmt_buf->freq = freq;
2826 mgmt_buf->wait = wait;
2827 mgmt_buf->len = len;
2828 mgmt_buf->no_cck = no_cck;
2829 memcpy(mgmt_buf->buf, buf, len);
2830 spin_lock_bh(&conn->psq_lock);
2831 is_psq_empty = skb_queue_empty(&conn->psq) &&
2832 (conn->mgmt_psq_len == 0);
2833 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
2834 conn->mgmt_psq_len++;
2835 spin_unlock_bh(&conn->psq_lock);
2836
2837 /*
2838 * If this is the first pkt getting queued
2839 * for this STA, update the PVB for this
2840 * STA.
2841 */
2842 if (is_psq_empty)
2843 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
2844 conn->aid, 1);
2845 return true;
2846 }
2847
2848 /*
2849 * This tx is because of a PsPoll.
2850 * Determine if MoreData bit has to be set.
2851 */
2852 spin_lock_bh(&conn->psq_lock);
2853 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
2854 *more_data = true;
2855 spin_unlock_bh(&conn->psq_lock);
2856 }
2857
2858 return false;
2859}
2860
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07002861/* Check if SSID length is greater than DIRECT- */
2862static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
2863{
2864 const struct ieee80211_mgmt *mgmt;
2865 mgmt = (const struct ieee80211_mgmt *) buf;
2866
2867 /* variable[1] contains the SSID tag length */
2868 if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
2869 (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
2870 return true;
2871 }
2872
2873 return false;
2874}
2875
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002876static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2877 struct ieee80211_channel *chan, bool offchan,
2878 enum nl80211_channel_type channel_type,
2879 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002880 const u8 *buf, size_t len, bool no_cck,
2881 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002882{
2883 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302884 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002885 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002886 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002887 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002888
2889 mgmt = (const struct ieee80211_mgmt *) buf;
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07002890 if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
2891 ieee80211_is_probe_resp(mgmt->frame_control) &&
2892 ath6kl_is_p2p_go_ssid(buf, len)) {
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002893 /*
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07002894 * Send Probe Response frame in GO mode using a separate WMI
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002895 * command to allow the target to fill in the generic IEs.
2896 */
2897 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302898 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002899 chan->center_freq);
2900 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002901
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302902 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002903 if (id == 0) {
2904 /*
2905 * 0 is a reserved value in the WMI command and shall not be
2906 * used for the command.
2907 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302908 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002909 }
2910
2911 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002912
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002913 /* AP mode Power saving processing */
2914 if (vif->nw_type == AP_NETWORK) {
2915 queued = ath6kl_mgmt_powersave_ap(vif,
2916 id, chan->center_freq,
2917 wait, buf,
2918 len, &more_data, no_cck);
2919 if (queued)
2920 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002921 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002922
2923 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2924 chan->center_freq, wait,
2925 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002926}
2927
Jouni Malinenae32c302011-08-30 21:58:01 +03002928static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2929 struct net_device *dev,
2930 u16 frame_type, bool reg)
2931{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302932 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002933
2934 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2935 __func__, frame_type, reg);
2936 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2937 /*
2938 * Note: This notification callback is not allowed to sleep, so
2939 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2940 * hardcode target to report Probe Request frames all the time.
2941 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302942 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002943 }
2944}
2945
Kalle Valo10509f92011-12-13 14:52:07 +02002946static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2947 struct net_device *dev,
2948 struct cfg80211_sched_scan_request *request)
2949{
2950 struct ath6kl *ar = ath6kl_priv(dev);
2951 struct ath6kl_vif *vif = netdev_priv(dev);
2952 u16 interval;
2953 int ret;
2954 u8 i;
2955
2956 if (ar->state != ATH6KL_STATE_ON)
2957 return -EIO;
2958
2959 if (vif->sme_state != SME_DISCONNECTED)
2960 return -EBUSY;
2961
Kalle Valob4d13d32012-03-21 10:01:09 +02002962 ath6kl_cfg80211_scan_complete_event(vif, true);
2963
Kalle Valo10509f92011-12-13 14:52:07 +02002964 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2965 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2966 i, DISABLE_SSID_FLAG,
2967 0, NULL);
2968 }
2969
2970 /* fw uses seconds, also make sure that it's >0 */
2971 interval = max_t(u16, 1, request->interval / 1000);
2972
2973 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2974 interval, interval,
2975 10, 0, 0, 0, 3, 0, 0, 0);
2976
2977 if (request->n_ssids && request->ssids[0].ssid_len) {
2978 for (i = 0; i < request->n_ssids; i++) {
2979 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2980 i, SPECIFIC_SSID_FLAG,
2981 request->ssids[i].ssid_len,
2982 request->ssids[i].ssid);
2983 }
2984 }
2985
2986 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2987 ATH6KL_WOW_MODE_ENABLE,
2988 WOW_FILTER_SSID,
2989 WOW_HOST_REQ_DELAY);
2990 if (ret) {
2991 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2992 return ret;
2993 }
2994
2995 /* this also clears IE in fw if it's not set */
2996 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2997 WMI_FRAME_PROBE_REQ,
2998 request->ie, request->ie_len);
2999 if (ret) {
3000 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
3001 ret);
3002 return ret;
3003 }
3004
3005 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
3006 ATH6KL_HOST_MODE_ASLEEP);
3007 if (ret) {
3008 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
3009 ret);
3010 return ret;
3011 }
3012
3013 ar->state = ATH6KL_STATE_SCHED_SCAN;
3014
3015 return ret;
3016}
3017
3018static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
3019 struct net_device *dev)
3020{
3021 struct ath6kl_vif *vif = netdev_priv(dev);
3022 bool stopped;
3023
3024 stopped = __ath6kl_cfg80211_sscan_stop(vif);
3025
3026 if (!stopped)
3027 return -EIO;
3028
3029 return 0;
3030}
3031
Jouni Malinenf80574a2011-08-30 21:58:04 +03003032static const struct ieee80211_txrx_stypes
3033ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
3034 [NL80211_IFTYPE_STATION] = {
3035 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3036 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3037 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3038 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3039 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02003040 [NL80211_IFTYPE_AP] = {
3041 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3042 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3043 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3044 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3045 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03003046 [NL80211_IFTYPE_P2P_CLIENT] = {
3047 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3048 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3049 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3050 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3051 },
3052 [NL80211_IFTYPE_P2P_GO] = {
3053 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3054 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3055 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3056 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3057 },
3058};
3059
Kalle Valobdcd8172011-07-18 00:22:30 +03003060static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303061 .add_virtual_intf = ath6kl_cfg80211_add_iface,
3062 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03003063 .change_virtual_intf = ath6kl_cfg80211_change_iface,
3064 .scan = ath6kl_cfg80211_scan,
3065 .connect = ath6kl_cfg80211_connect,
3066 .disconnect = ath6kl_cfg80211_disconnect,
3067 .add_key = ath6kl_cfg80211_add_key,
3068 .get_key = ath6kl_cfg80211_get_key,
3069 .del_key = ath6kl_cfg80211_del_key,
3070 .set_default_key = ath6kl_cfg80211_set_default_key,
3071 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
3072 .set_tx_power = ath6kl_cfg80211_set_txpower,
3073 .get_tx_power = ath6kl_cfg80211_get_txpower,
3074 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
3075 .join_ibss = ath6kl_cfg80211_join_ibss,
3076 .leave_ibss = ath6kl_cfg80211_leave_ibss,
3077 .get_station = ath6kl_get_station,
3078 .set_pmksa = ath6kl_set_pmksa,
3079 .del_pmksa = ath6kl_del_pmksa,
3080 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03003081 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03003082#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02003083 .suspend = __ath6kl_cfg80211_suspend,
3084 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03003085#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03003086 .set_channel = ath6kl_set_channel,
Johannes Berg88600202012-02-13 15:17:18 +01003087 .start_ap = ath6kl_start_ap,
3088 .change_beacon = ath6kl_change_beacon,
3089 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02003090 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03003091 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003092 .remain_on_channel = ath6kl_remain_on_channel,
3093 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003094 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03003095 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02003096 .sched_scan_start = ath6kl_cfg80211_sscan_start,
3097 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03003098};
3099
Kalle Valo7125f012011-12-13 14:51:37 +02003100void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02003101{
Kalle Valo10509f92011-12-13 14:52:07 +02003102 ath6kl_cfg80211_sscan_disable(vif);
3103
Kalle Valoec4b7f62011-11-01 08:44:04 +02003104 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02003105 case SME_DISCONNECTED:
3106 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02003107 case SME_CONNECTING:
3108 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
3109 NULL, 0,
3110 WLAN_STATUS_UNSPECIFIED_FAILURE,
3111 GFP_KERNEL);
3112 break;
3113 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02003114 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
3115 break;
3116 }
3117
3118 if (test_bit(CONNECTED, &vif->flags) ||
3119 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02003120 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003121
3122 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02003123 clear_bit(CONNECTED, &vif->flags);
3124 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003125
3126 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02003127 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
3128 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
3129 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003130
3131 ath6kl_cfg80211_scan_complete_event(vif, true);
3132}
3133
Kalle Valo7125f012011-12-13 14:51:37 +02003134void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3135{
3136 struct ath6kl_vif *vif;
3137
3138 vif = ath6kl_vif_first(ar);
3139 if (!vif) {
3140 /* save the current power mode before enabling power save */
3141 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3142
3143 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
3144 ath6kl_warn("ath6kl_deep_sleep_enable: "
3145 "wmi_powermode_cmd failed\n");
3146 return;
3147 }
3148
3149 /*
3150 * FIXME: we should take ar->list_lock to protect changes in the
3151 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3152 * sleeps.
3153 */
3154 list_for_each_entry(vif, &ar->vif_list, list)
3155 ath6kl_cfg80211_stop(vif);
3156}
3157
Kalle Valoc25889e2012-01-17 20:08:27 +02003158static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003159{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303160 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303161 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303162 ath6kl_err("failed to initialize aggr\n");
3163 return -ENOMEM;
3164 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003165
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303166 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303167 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003168 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3169 (unsigned long) vif);
3170
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303171 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303172 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303173
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303174 INIT_LIST_HEAD(&vif->mc_filter);
3175
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303176 return 0;
3177}
3178
Kalle Valoc25889e2012-01-17 20:08:27 +02003179void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303180{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303181 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303182 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303183
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303184 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303185
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303186 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3187
3188 if (vif->nw_type == ADHOC_NETWORK)
3189 ar->ibss_if_active = false;
3190
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303191 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3192 list_del(&mc_filter->list);
3193 kfree(mc_filter);
3194 }
3195
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303196 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303197
3198 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303199}
3200
3201struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303202 enum nl80211_iftype type, u8 fw_vif_idx,
3203 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303204{
3205 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303206 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303207
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303208 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303209 if (!ndev)
3210 return NULL;
3211
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303212 vif = netdev_priv(ndev);
3213 ndev->ieee80211_ptr = &vif->wdev;
3214 vif->wdev.wiphy = ar->wiphy;
3215 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303216 vif->ndev = ndev;
3217 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3218 vif->wdev.netdev = ndev;
3219 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303220 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003221 vif->nw_type = nw_type;
3222 vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303223 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303224 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303225
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303226 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
3227 if (fw_vif_idx != 0)
3228 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3229 0x2;
3230
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303231 init_netdev(ndev);
3232
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303233 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303234
Kalle Valoc25889e2012-01-17 20:08:27 +02003235 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303236 goto err;
3237
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303238 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303239 goto err;
3240
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303241 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303242 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303243 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303244 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303245 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303246
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303247 if (type == NL80211_IFTYPE_ADHOC)
3248 ar->ibss_if_active = true;
3249
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303250 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303251 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303252 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303253
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303254 return ndev;
3255
3256err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303257 aggr_module_destroy(vif->aggr_cntxt);
3258 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303259 return NULL;
3260}
3261
Kalle Valo46d33a22012-01-17 20:08:40 +02003262int ath6kl_cfg80211_init(struct ath6kl *ar)
3263{
3264 struct wiphy *wiphy = ar->wiphy;
3265 int ret;
3266
3267 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3268
3269 wiphy->max_remain_on_channel_duration = 5000;
3270
3271 /* set device pointer for wiphy */
3272 set_wiphy_dev(wiphy, ar->dev);
3273
3274 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3275 BIT(NL80211_IFTYPE_ADHOC) |
3276 BIT(NL80211_IFTYPE_AP);
3277 if (ar->p2p) {
3278 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3279 BIT(NL80211_IFTYPE_P2P_CLIENT);
3280 }
3281
3282 /* max num of ssids that can be probed during scanning */
3283 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
3284 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
3285 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3286 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3287 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3288
3289 wiphy->cipher_suites = cipher_suites;
3290 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3291
3292 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3293 WIPHY_WOWLAN_DISCONNECT |
3294 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3295 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3296 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3297 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3298 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3299 wiphy->wowlan.pattern_min_len = 1;
3300 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
3301
3302 wiphy->max_sched_scan_ssids = 10;
3303
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303304 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3305 WIPHY_FLAG_HAVE_AP_SME |
3306 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3307 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3308
3309 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3310 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3311
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303312 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
3313 ar->fw_capabilities))
3314 ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER;
3315
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303316 ar->wiphy->probe_resp_offload =
3317 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3318 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
3319 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
3320 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
3321
Kalle Valo46d33a22012-01-17 20:08:40 +02003322 ret = wiphy_register(wiphy);
3323 if (ret < 0) {
3324 ath6kl_err("couldn't register wiphy device\n");
3325 return ret;
3326 }
3327
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303328 ar->wiphy_registered = true;
3329
Kalle Valo46d33a22012-01-17 20:08:40 +02003330 return 0;
3331}
3332
3333void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303334{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303335 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303336
3337 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003338}
Kalle Valo46d33a22012-01-17 20:08:40 +02003339
Kalle Valo45eaa782012-01-17 20:09:05 +02003340struct ath6kl *ath6kl_cfg80211_create(void)
3341{
3342 struct ath6kl *ar;
3343 struct wiphy *wiphy;
3344
3345 /* create a new wiphy for use with cfg80211 */
3346 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3347
3348 if (!wiphy) {
3349 ath6kl_err("couldn't allocate wiphy device\n");
3350 return NULL;
3351 }
3352
3353 ar = wiphy_priv(wiphy);
3354 ar->wiphy = wiphy;
3355
3356 return ar;
3357}
3358
3359/* Note: ar variable must not be accessed after calling this! */
3360void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3361{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303362 int i;
3363
3364 for (i = 0; i < AP_MAX_NUM_STA; i++)
3365 kfree(ar->sta_list[i].aggr_conn);
3366
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303367 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003368}
Kalle Valo45eaa782012-01-17 20:09:05 +02003369