blob: a47688054ce9f5a2af19eee73e9404cc1809f9c0 [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
52static struct ieee80211_rate ath6kl_rates[] = {
53 RATETAB_ENT(10, 0x1, 0),
54 RATETAB_ENT(20, 0x2, 0),
55 RATETAB_ENT(55, 0x4, 0),
56 RATETAB_ENT(110, 0x8, 0),
57 RATETAB_ENT(60, 0x10, 0),
58 RATETAB_ENT(90, 0x20, 0),
59 RATETAB_ENT(120, 0x40, 0),
60 RATETAB_ENT(180, 0x80, 0),
61 RATETAB_ENT(240, 0x100, 0),
62 RATETAB_ENT(360, 0x200, 0),
63 RATETAB_ENT(480, 0x400, 0),
64 RATETAB_ENT(540, 0x800, 0),
65};
66
67#define ath6kl_a_rates (ath6kl_rates + 4)
68#define ath6kl_a_rates_size 8
69#define ath6kl_g_rates (ath6kl_rates + 0)
70#define ath6kl_g_rates_size 12
71
72static struct ieee80211_channel ath6kl_2ghz_channels[] = {
73 CHAN2G(1, 2412, 0),
74 CHAN2G(2, 2417, 0),
75 CHAN2G(3, 2422, 0),
76 CHAN2G(4, 2427, 0),
77 CHAN2G(5, 2432, 0),
78 CHAN2G(6, 2437, 0),
79 CHAN2G(7, 2442, 0),
80 CHAN2G(8, 2447, 0),
81 CHAN2G(9, 2452, 0),
82 CHAN2G(10, 2457, 0),
83 CHAN2G(11, 2462, 0),
84 CHAN2G(12, 2467, 0),
85 CHAN2G(13, 2472, 0),
86 CHAN2G(14, 2484, 0),
87};
88
89static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
90 CHAN5G(34, 0), CHAN5G(36, 0),
91 CHAN5G(38, 0), CHAN5G(40, 0),
92 CHAN5G(42, 0), CHAN5G(44, 0),
93 CHAN5G(46, 0), CHAN5G(48, 0),
94 CHAN5G(52, 0), CHAN5G(56, 0),
95 CHAN5G(60, 0), CHAN5G(64, 0),
96 CHAN5G(100, 0), CHAN5G(104, 0),
97 CHAN5G(108, 0), CHAN5G(112, 0),
98 CHAN5G(116, 0), CHAN5G(120, 0),
99 CHAN5G(124, 0), CHAN5G(128, 0),
100 CHAN5G(132, 0), CHAN5G(136, 0),
101 CHAN5G(140, 0), CHAN5G(149, 0),
102 CHAN5G(153, 0), CHAN5G(157, 0),
103 CHAN5G(161, 0), CHAN5G(165, 0),
104 CHAN5G(184, 0), CHAN5G(188, 0),
105 CHAN5G(192, 0), CHAN5G(196, 0),
106 CHAN5G(200, 0), CHAN5G(204, 0),
107 CHAN5G(208, 0), CHAN5G(212, 0),
108 CHAN5G(216, 0),
109};
110
111static struct ieee80211_supported_band ath6kl_band_2ghz = {
112 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
113 .channels = ath6kl_2ghz_channels,
114 .n_bitrates = ath6kl_g_rates_size,
115 .bitrates = ath6kl_g_rates,
116};
117
118static struct ieee80211_supported_band ath6kl_band_5ghz = {
119 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
120 .channels = ath6kl_5ghz_a_channels,
121 .n_bitrates = ath6kl_a_rates_size,
122 .bitrates = ath6kl_a_rates,
123};
124
Jouni Malinen837cb972011-10-11 17:31:57 +0300125#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
126
Kalle Valo10509f92011-12-13 14:52:07 +0200127/* returns true if scheduled scan was stopped */
128static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
129{
130 struct ath6kl *ar = vif->ar;
131
132 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
133 return false;
134
135 del_timer_sync(&vif->sched_scan_timer);
136
137 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
138 ATH6KL_HOST_MODE_AWAKE);
139
140 ar->state = ATH6KL_STATE_ON;
141
142 return true;
143}
144
145static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
146{
147 struct ath6kl *ar = vif->ar;
148 bool stopped;
149
150 stopped = __ath6kl_cfg80211_sscan_stop(vif);
151
152 if (!stopped)
153 return;
154
155 cfg80211_sched_scan_stopped(ar->wiphy);
156}
157
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530158static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300159 enum nl80211_wpa_versions wpa_version)
160{
161 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
162
163 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530164 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300165 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530166 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300167 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530168 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300169 } else {
170 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
171 return -ENOTSUPP;
172 }
173
174 return 0;
175}
176
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530177static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300178 enum nl80211_auth_type auth_type)
179{
Kalle Valobdcd8172011-07-18 00:22:30 +0300180 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
181
182 switch (auth_type) {
183 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530184 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300185 break;
186 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530187 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300188 break;
189 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530190 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300191 break;
192
193 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530194 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300195 break;
196
197 default:
Masanari Iida3c325fb2012-01-31 23:32:55 +0900198 ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300199 return -ENOTSUPP;
200 }
201
202 return 0;
203}
204
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530205static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300206{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530207 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
208 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
209 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300210
211 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
212 __func__, cipher, ucast);
213
214 switch (cipher) {
215 case 0:
216 /* our own hack to use value 0 as no crypto used */
217 *ar_cipher = NONE_CRYPT;
218 *ar_cipher_len = 0;
219 break;
220 case WLAN_CIPHER_SUITE_WEP40:
221 *ar_cipher = WEP_CRYPT;
222 *ar_cipher_len = 5;
223 break;
224 case WLAN_CIPHER_SUITE_WEP104:
225 *ar_cipher = WEP_CRYPT;
226 *ar_cipher_len = 13;
227 break;
228 case WLAN_CIPHER_SUITE_TKIP:
229 *ar_cipher = TKIP_CRYPT;
230 *ar_cipher_len = 0;
231 break;
232 case WLAN_CIPHER_SUITE_CCMP:
233 *ar_cipher = AES_CRYPT;
234 *ar_cipher_len = 0;
235 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200236 case WLAN_CIPHER_SUITE_SMS4:
237 *ar_cipher = WAPI_CRYPT;
238 *ar_cipher_len = 0;
239 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300240 default:
241 ath6kl_err("cipher 0x%x not supported\n", cipher);
242 return -ENOTSUPP;
243 }
244
245 return 0;
246}
247
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530248static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300249{
250 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
251
252 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530253 if (vif->auth_mode == WPA_AUTH)
254 vif->auth_mode = WPA_PSK_AUTH;
255 else if (vif->auth_mode == WPA2_AUTH)
256 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300257 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530258 if (vif->auth_mode == WPA_AUTH)
259 vif->auth_mode = WPA_AUTH_CCKM;
260 else if (vif->auth_mode == WPA2_AUTH)
261 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300262 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530263 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300264 }
265}
266
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530267static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300268{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530269 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530270
Kalle Valobdcd8172011-07-18 00:22:30 +0300271 if (!test_bit(WMI_READY, &ar->flag)) {
272 ath6kl_err("wmi is not ready\n");
273 return false;
274 }
275
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530276 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300277 ath6kl_err("wlan disabled\n");
278 return false;
279 }
280
281 return true;
282}
283
Kevin Fang6981ffd2011-10-07 08:51:19 +0800284static bool ath6kl_is_wpa_ie(const u8 *pos)
285{
286 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
287 pos[2] == 0x00 && pos[3] == 0x50 &&
288 pos[4] == 0xf2 && pos[5] == 0x01;
289}
290
291static bool ath6kl_is_rsn_ie(const u8 *pos)
292{
293 return pos[0] == WLAN_EID_RSN;
294}
295
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700296static bool ath6kl_is_wps_ie(const u8 *pos)
297{
298 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
299 pos[1] >= 4 &&
300 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
301 pos[5] == 0x04);
302}
303
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530304static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
305 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800306{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530307 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800308 const u8 *pos;
309 u8 *buf = NULL;
310 size_t len = 0;
311 int ret;
312
313 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700314 * Clear previously set flag
315 */
316
317 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
318
319 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800320 * Filter out RSN/WPA IE(s)
321 */
322
323 if (ies && ies_len) {
324 buf = kmalloc(ies_len, GFP_KERNEL);
325 if (buf == NULL)
326 return -ENOMEM;
327 pos = ies;
328
329 while (pos + 1 < ies + ies_len) {
330 if (pos + 2 + pos[1] > ies + ies_len)
331 break;
332 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
333 memcpy(buf + len, pos, 2 + pos[1]);
334 len += 2 + pos[1];
335 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700336
337 if (ath6kl_is_wps_ie(pos))
338 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
339
Kevin Fang6981ffd2011-10-07 08:51:19 +0800340 pos += 2 + pos[1];
341 }
342 }
343
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530344 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
345 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800346 kfree(buf);
347 return ret;
348}
349
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530350static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
351{
352 switch (type) {
353 case NL80211_IFTYPE_STATION:
354 *nw_type = INFRA_NETWORK;
355 break;
356 case NL80211_IFTYPE_ADHOC:
357 *nw_type = ADHOC_NETWORK;
358 break;
359 case NL80211_IFTYPE_AP:
360 *nw_type = AP_NETWORK;
361 break;
362 case NL80211_IFTYPE_P2P_CLIENT:
363 *nw_type = INFRA_NETWORK;
364 break;
365 case NL80211_IFTYPE_P2P_GO:
366 *nw_type = AP_NETWORK;
367 break;
368 default:
369 ath6kl_err("invalid interface type %u\n", type);
370 return -ENOTSUPP;
371 }
372
373 return 0;
374}
375
376static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
377 u8 *if_idx, u8 *nw_type)
378{
379 int i;
380
381 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
382 return false;
383
384 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
385 ar->num_vif))
386 return false;
387
388 if (type == NL80211_IFTYPE_STATION ||
389 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200390 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530391 if ((ar->avail_idx_map >> i) & BIT(0)) {
392 *if_idx = i;
393 return true;
394 }
395 }
396 }
397
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530398 if (type == NL80211_IFTYPE_P2P_CLIENT ||
399 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200400 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530401 if ((ar->avail_idx_map >> i) & BIT(0)) {
402 *if_idx = i;
403 return true;
404 }
405 }
406 }
407
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530408 return false;
409}
410
Kalle Valobdcd8172011-07-18 00:22:30 +0300411static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
412 struct cfg80211_connect_params *sme)
413{
414 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530415 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300416 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800417 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Kalle Valobdcd8172011-07-18 00:22:30 +0300418
Kalle Valo10509f92011-12-13 14:52:07 +0200419 ath6kl_cfg80211_sscan_disable(vif);
420
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530421 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300422
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530423 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300424 return -EIO;
425
426 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
427 ath6kl_err("destroy in progress\n");
428 return -EBUSY;
429 }
430
431 if (test_bit(SKIP_SCAN, &ar->flag) &&
432 ((sme->channel && sme->channel->center_freq == 0) ||
433 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
434 ath6kl_err("SkipScan: channel or bssid invalid\n");
435 return -EINVAL;
436 }
437
438 if (down_interruptible(&ar->sem)) {
439 ath6kl_err("busy, couldn't get access\n");
440 return -ERESTARTSYS;
441 }
442
443 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
444 ath6kl_err("busy, destroy in progress\n");
445 up(&ar->sem);
446 return -EBUSY;
447 }
448
449 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
450 /*
451 * sleep until the command queue drains
452 */
453 wait_event_interruptible_timeout(ar->event_wq,
454 ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
455 WMI_TIMEOUT);
456 if (signal_pending(current)) {
457 ath6kl_err("cmd queue drain timeout\n");
458 up(&ar->sem);
459 return -EINTR;
460 }
461 }
462
Jouni Malinen6e786cb2011-12-15 14:16:00 +0200463 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
464 if (status) {
465 up(&ar->sem);
466 return status;
467 }
468
469 if (sme->ie == NULL || sme->ie_len == 0)
Raja Mani542c5192011-11-15 14:14:56 +0530470 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800471
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530472 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530473 vif->ssid_len == sme->ssid_len &&
474 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530475 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530476 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
477 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530478 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300479
480 up(&ar->sem);
481 if (status) {
482 ath6kl_err("wmi_reconnect_cmd failed\n");
483 return -EIO;
484 }
485 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530486 } else if (vif->ssid_len == sme->ssid_len &&
487 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530488 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300489 }
490
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530491 memset(vif->ssid, 0, sizeof(vif->ssid));
492 vif->ssid_len = sme->ssid_len;
493 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300494
495 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530496 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300497
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530498 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300499 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530500 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300501
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530502 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300503
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530504 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300505 if (status) {
506 up(&ar->sem);
507 return status;
508 }
509
510 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530511 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300512 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530513 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300514
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530515 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300516
517 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530518 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300519
520 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530521 (vif->auth_mode == NONE_AUTH) &&
522 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300523 struct ath6kl_key *key = NULL;
524
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530525 if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300526 ath6kl_err("key index %d out of bounds\n",
527 sme->key_idx);
528 up(&ar->sem);
529 return -ENOENT;
530 }
531
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530532 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300533 key->key_len = sme->key_len;
534 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530535 key->cipher = vif->prwise_crypto;
536 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300537
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530538 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530539 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300540 GROUP_USAGE | TX_USAGE,
541 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200542 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300543 key->key, KEY_OP_INIT_VAL, NULL,
544 NO_SYNC_WMIFLAG);
545 }
546
547 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530548 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530549 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
550 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300551 ath6kl_err("couldn't set bss filtering\n");
552 up(&ar->sem);
553 return -EIO;
554 }
555 }
556
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530557 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300558
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800559 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
560 nw_subtype = SUBTYPE_P2PCLIENT;
561
Kalle Valobdcd8172011-07-18 00:22:30 +0300562 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
563 "%s: connect called with authmode %d dot11 auth %d"
564 " PW crypto %d PW crypto len %d GRP crypto %d"
565 " GRP crypto len %d channel hint %u\n",
566 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530567 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
568 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530569 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300570
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530571 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530572 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530573 vif->dot11_auth_mode, vif->auth_mode,
574 vif->prwise_crypto,
575 vif->prwise_crypto_len,
576 vif->grp_crypto, vif->grp_crypto_len,
577 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530578 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800579 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300580
581 up(&ar->sem);
582
583 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530584 memset(vif->ssid, 0, sizeof(vif->ssid));
585 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300586 ath6kl_err("invalid request\n");
587 return -ENOENT;
588 } else if (status) {
589 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
590 return -EIO;
591 }
592
593 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530594 ((vif->auth_mode == WPA_PSK_AUTH)
595 || (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530596 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300597 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
598 }
599
600 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530601 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300602
603 return 0;
604}
605
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530606static struct cfg80211_bss *
607ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
608 enum network_type nw_type,
609 const u8 *bssid,
610 struct ieee80211_channel *chan,
611 const u8 *beacon_ie,
612 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300613{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530614 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300615 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530616 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300617 u8 *ie;
618
Raja Mani4eab6f42011-11-09 17:02:23 +0530619 if (nw_type & ADHOC_NETWORK) {
620 cap_mask = WLAN_CAPABILITY_IBSS;
621 cap_val = WLAN_CAPABILITY_IBSS;
622 } else {
623 cap_mask = WLAN_CAPABILITY_ESS;
624 cap_val = WLAN_CAPABILITY_ESS;
625 }
626
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530627 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530628 vif->ssid, vif->ssid_len,
629 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300630 if (bss == NULL) {
631 /*
632 * Since cfg80211 may not yet know about the BSS,
633 * generate a partial entry until the first BSS info
634 * event becomes available.
635 *
636 * Prepend SSID element since it is not included in the Beacon
637 * IEs from the target.
638 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530639 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300640 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530641 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300642 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530643 ie[1] = vif->ssid_len;
644 memcpy(ie + 2, vif->ssid, vif->ssid_len);
645 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530646 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530647 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530648 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300649 0, GFP_KERNEL);
650 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530651 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
652 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300653 kfree(ie);
654 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530655 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300656
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530657 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300658}
659
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530660void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300661 u8 *bssid, u16 listen_intvl,
662 u16 beacon_intvl,
663 enum network_type nw_type,
664 u8 beacon_ie_len, u8 assoc_req_len,
665 u8 assoc_resp_len, u8 *assoc_info)
666{
Jouni Malinen01cac472011-09-19 19:14:59 +0300667 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530668 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530669 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300670
671 /* capinfo + listen interval */
672 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
673
674 /* capinfo + status code + associd */
675 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
676
677 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
678 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
679 assoc_resp_ie_offset;
680
681 assoc_req_len -= assoc_req_ie_offset;
682 assoc_resp_len -= assoc_resp_ie_offset;
683
Jouni Malinen32c10872011-09-19 19:15:07 +0300684 /*
685 * Store Beacon interval here; DTIM period will be available only once
686 * a Beacon frame from the AP is seen.
687 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530688 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530689 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300690
Kalle Valobdcd8172011-07-18 00:22:30 +0300691 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530692 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300693 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
694 "%s: ath6k not in ibss mode\n", __func__);
695 return;
696 }
697 }
698
699 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530700 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
701 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300702 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
703 "%s: ath6k not in station mode\n", __func__);
704 return;
705 }
706 }
707
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530708 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300709
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530710 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
711 assoc_info, beacon_ie_len);
712 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530713 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300714 return;
715 }
716
Raja Mani4eab6f42011-11-09 17:02:23 +0530717 if (nw_type & ADHOC_NETWORK) {
718 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
719 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
720 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530721 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300722 return;
723 }
724
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530725 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300726 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530727 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530728 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300729 assoc_req_ie, assoc_req_len,
730 assoc_resp_ie, assoc_resp_len,
731 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530732 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530733 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300734 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530735 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
736 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300737 }
738}
739
740static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
741 struct net_device *dev, u16 reason_code)
742{
Kalle Valod6d5c062011-11-25 13:17:37 +0200743 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530744 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300745
746 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
747 reason_code);
748
Kalle Valo10509f92011-12-13 14:52:07 +0200749 ath6kl_cfg80211_sscan_disable(vif);
750
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530751 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300752 return -EIO;
753
754 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
755 ath6kl_err("busy, destroy in progress\n");
756 return -EBUSY;
757 }
758
759 if (down_interruptible(&ar->sem)) {
760 ath6kl_err("busy, couldn't get access\n");
761 return -ERESTARTSYS;
762 }
763
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530764 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530765 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530766 memset(vif->ssid, 0, sizeof(vif->ssid));
767 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300768
769 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530770 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300771
772 up(&ar->sem);
773
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530774 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530775
Kalle Valobdcd8172011-07-18 00:22:30 +0300776 return 0;
777}
778
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530779void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300780 u8 *bssid, u8 assoc_resp_len,
781 u8 *assoc_info, u16 proto_reason)
782{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530783 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530784
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530785 if (vif->scan_req) {
786 cfg80211_scan_done(vif->scan_req, true);
787 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300788 }
789
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530790 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530791 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300792 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
793 "%s: ath6k not in ibss mode\n", __func__);
794 return;
795 }
796 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530797 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300798 return;
799 }
800
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530801 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530802 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
803 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300804 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
805 "%s: ath6k not in station mode\n", __func__);
806 return;
807 }
808 }
809
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530810 /*
811 * Send a disconnect command to target when a disconnect event is
812 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
813 * request from host) to make the firmware stop trying to connect even
814 * after giving disconnect event. There will be one more disconnect
815 * event for this disconnect command with reason code DISCONNECT_CMD
816 * which will be notified to cfg80211.
817 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300818
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530819 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530820 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300821 return;
822 }
823
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530824 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300825
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530826 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530827 cfg80211_connect_result(vif->ndev,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530828 bssid, NULL, 0,
829 NULL, 0,
830 WLAN_STATUS_UNSPECIFIED_FAILURE,
831 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530832 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530833 cfg80211_disconnected(vif->ndev, reason,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530834 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300835 }
836
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530837 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300838}
839
Kalle Valobdcd8172011-07-18 00:22:30 +0300840static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
841 struct cfg80211_scan_request *request)
842{
Kalle Valod6d5c062011-11-25 13:17:37 +0200843 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530844 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300845 s8 n_channels = 0;
846 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300847 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530848 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300849
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530850 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300851 return -EIO;
852
Kalle Valo10509f92011-12-13 14:52:07 +0200853 ath6kl_cfg80211_sscan_disable(vif);
854
Kalle Valobdcd8172011-07-18 00:22:30 +0300855 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530856 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300857 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530858 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530859 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300860 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
861 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300862 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300863 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300864 }
865 }
866
867 if (request->n_ssids && request->ssids[0].ssid_len) {
868 u8 i;
869
870 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
871 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
872
873 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530874 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
875 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300876 request->ssids[i].ssid_len,
877 request->ssids[i].ssid);
878 }
879
Kalle Valo10509f92011-12-13 14:52:07 +0200880 /*
881 * FIXME: we should clear the IE in fw if it's not set so just
882 * remove the check altogether
883 */
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300884 if (request->ie) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530885 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
886 WMI_FRAME_PROBE_REQ,
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300887 request->ie, request->ie_len);
888 if (ret) {
889 ath6kl_err("failed to set Probe Request appie for "
890 "scan");
891 return ret;
892 }
893 }
894
Jouni Malinen11869be2011-09-02 20:07:06 +0300895 /*
896 * Scan only the requested channels if the request specifies a set of
897 * channels. If the list is longer than the target supports, do not
898 * configure the list and instead, scan all available channels.
899 */
900 if (request->n_channels > 0 &&
901 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300902 u8 i;
903
Jouni Malinen11869be2011-09-02 20:07:06 +0300904 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300905
906 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
907 if (channels == NULL) {
908 ath6kl_warn("failed to set scan channels, "
909 "scan all channels");
910 n_channels = 0;
911 }
912
913 for (i = 0; i < n_channels; i++)
914 channels[i] = request->channels[i]->center_freq;
915 }
916
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530917 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530918 force_fg_scan = 1;
919
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800920 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
921 ar->fw_capabilities)) {
922 /*
923 * If capable of doing P2P mgmt operations using
924 * station interface, send additional information like
925 * supported rates to advertise and xmit rates for
926 * probe requests
927 */
928 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
929 WMI_LONG_SCAN, force_fg_scan,
930 false, 0, 0, n_channels,
931 channels, request->no_cck,
932 request->rates);
933 } else {
934 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
935 WMI_LONG_SCAN, force_fg_scan,
936 false, 0, 0, n_channels,
937 channels);
938 }
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300939 if (ret)
Kalle Valobdcd8172011-07-18 00:22:30 +0300940 ath6kl_err("wmi_startscan_cmd failed\n");
Jouni Malinen11869be2011-09-02 20:07:06 +0300941 else
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530942 vif->scan_req = request;
Kalle Valobdcd8172011-07-18 00:22:30 +0300943
Edward Lu1276c9e2011-08-30 21:58:00 +0300944 kfree(channels);
945
Kalle Valobdcd8172011-07-18 00:22:30 +0300946 return ret;
947}
948
Kalle Valo1c17d312011-11-01 08:43:56 +0200949void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300950{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530951 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300952 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300953
Kalle Valo1c17d312011-11-01 08:43:56 +0200954 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
955 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300956
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530957 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300958 return;
Kalle Valobdcd8172011-07-18 00:22:30 +0300959
Kalle Valo1c17d312011-11-01 08:43:56 +0200960 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300961 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300962
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530963 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
964 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530965 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
966 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300967 0, NULL);
968 }
969 }
970
971out:
Kalle Valocb938212011-10-27 18:47:46 +0300972 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530973 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300974}
975
976static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
977 u8 key_index, bool pairwise,
978 const u8 *mac_addr,
979 struct key_params *params)
980{
Kalle Valod6d5c062011-11-25 13:17:37 +0200981 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530982 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300983 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +0530984 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300985 u8 key_usage;
986 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +0300987
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530988 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300989 return -EIO;
990
Jouni Malinen837cb972011-10-11 17:31:57 +0300991 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
992 if (params->key_len != WMI_KRK_LEN)
993 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530994 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
995 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +0300996 }
997
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530998 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300999 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1000 "%s: key index %d out of bounds\n", __func__,
1001 key_index);
1002 return -ENOENT;
1003 }
1004
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301005 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001006 memset(key, 0, sizeof(struct ath6kl_key));
1007
1008 if (pairwise)
1009 key_usage = PAIRWISE_USAGE;
1010 else
1011 key_usage = GROUP_USAGE;
1012
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301013 seq_len = params->seq_len;
1014 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1015 seq_len > ATH6KL_KEY_SEQ_LEN) {
1016 /* Only first half of the WPI PN is configured */
1017 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001018 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301019 if (params->key_len > WLAN_MAX_KEY_LEN ||
1020 seq_len > sizeof(key->seq))
1021 return -EINVAL;
1022
1023 key->key_len = params->key_len;
1024 memcpy(key->key, params->key, key->key_len);
1025 key->seq_len = seq_len;
1026 memcpy(key->seq, params->seq, key->seq_len);
1027 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001028
1029 switch (key->cipher) {
1030 case WLAN_CIPHER_SUITE_WEP40:
1031 case WLAN_CIPHER_SUITE_WEP104:
1032 key_type = WEP_CRYPT;
1033 break;
1034
1035 case WLAN_CIPHER_SUITE_TKIP:
1036 key_type = TKIP_CRYPT;
1037 break;
1038
1039 case WLAN_CIPHER_SUITE_CCMP:
1040 key_type = AES_CRYPT;
1041 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001042 case WLAN_CIPHER_SUITE_SMS4:
1043 key_type = WAPI_CRYPT;
1044 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001045
1046 default:
1047 return -ENOTSUPP;
1048 }
1049
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301050 if (((vif->auth_mode == WPA_PSK_AUTH)
1051 || (vif->auth_mode == WPA2_PSK_AUTH))
Kalle Valobdcd8172011-07-18 00:22:30 +03001052 && (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301053 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001054
1055 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1056 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1057 __func__, key_index, key->key_len, key_type,
1058 key_usage, key->seq_len);
1059
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301060 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001061 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
Vasanthakumar Thiagarajancc4d6232012-02-14 20:33:00 +05301062 key_type == WAPI_CRYPT)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001063 ar->ap_mode_bkey.valid = true;
1064 ar->ap_mode_bkey.key_index = key_index;
1065 ar->ap_mode_bkey.key_type = key_type;
1066 ar->ap_mode_bkey.key_len = key->key_len;
1067 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301068 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001069 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1070 "key configuration until AP mode has been "
1071 "started\n");
1072 /*
1073 * The key will be set in ath6kl_connect_ap_mode() once
1074 * the connected event is received from the target.
1075 */
1076 return 0;
1077 }
1078 }
1079
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301080 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301081 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001082 /*
1083 * Store the key locally so that it can be re-configured after
1084 * the AP mode has properly started
1085 * (ath6kl_install_statioc_wep_keys).
1086 */
1087 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1088 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301089 vif->wep_key_list[key_index].key_len = key->key_len;
1090 memcpy(vif->wep_key_list[key_index].key, key->key,
1091 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001092 return 0;
1093 }
1094
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301095 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001096 key_type, key_usage, key->key_len,
1097 key->seq, key->seq_len, key->key,
1098 KEY_OP_INIT_VAL,
1099 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001100}
1101
1102static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1103 u8 key_index, bool pairwise,
1104 const u8 *mac_addr)
1105{
Kalle Valod6d5c062011-11-25 13:17:37 +02001106 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301107 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001108
1109 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1110
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301111 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001112 return -EIO;
1113
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301114 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001115 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1116 "%s: key index %d out of bounds\n", __func__,
1117 key_index);
1118 return -ENOENT;
1119 }
1120
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301121 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001122 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1123 "%s: index %d is empty\n", __func__, key_index);
1124 return 0;
1125 }
1126
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301127 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001128
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301129 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001130}
1131
1132static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1133 u8 key_index, bool pairwise,
1134 const u8 *mac_addr, void *cookie,
1135 void (*callback) (void *cookie,
1136 struct key_params *))
1137{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301138 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001139 struct ath6kl_key *key = NULL;
1140 struct key_params params;
1141
1142 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1143
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301144 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001145 return -EIO;
1146
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301147 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001148 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1149 "%s: key index %d out of bounds\n", __func__,
1150 key_index);
1151 return -ENOENT;
1152 }
1153
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301154 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001155 memset(&params, 0, sizeof(params));
1156 params.cipher = key->cipher;
1157 params.key_len = key->key_len;
1158 params.seq_len = key->seq_len;
1159 params.seq = key->seq;
1160 params.key = key->key;
1161
1162 callback(cookie, &params);
1163
1164 return key->key_len ? 0 : -ENOENT;
1165}
1166
1167static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1168 struct net_device *ndev,
1169 u8 key_index, bool unicast,
1170 bool multicast)
1171{
Kalle Valod6d5c062011-11-25 13:17:37 +02001172 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301173 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001174 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001175 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001176 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001177
1178 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1179
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301180 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001181 return -EIO;
1182
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301183 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001184 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1185 "%s: key index %d out of bounds\n",
1186 __func__, key_index);
1187 return -ENOENT;
1188 }
1189
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301190 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001191 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1192 __func__, key_index);
1193 return -EINVAL;
1194 }
1195
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301196 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301197 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001198 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301199 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001200 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001201 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301202 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001203 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301204 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001205
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301206 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001207 return 0; /* Delay until AP mode has been started */
1208
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001209 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1210 vif->def_txkey_index,
1211 key_type, key_usage,
1212 key->key_len, key->seq, key->seq_len,
1213 key->key,
1214 KEY_OP_INIT_VAL, NULL,
1215 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001216}
1217
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301218void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001219 bool ismcast)
1220{
1221 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1222 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1223
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301224 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001225 (ismcast ? NL80211_KEYTYPE_GROUP :
1226 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1227 GFP_KERNEL);
1228}
1229
1230static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1231{
1232 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301233 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001234 int ret;
1235
1236 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1237 changed);
1238
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301239 vif = ath6kl_vif_first(ar);
1240 if (!vif)
1241 return -EIO;
1242
1243 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001244 return -EIO;
1245
1246 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1247 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1248 if (ret != 0) {
1249 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1250 return -EIO;
1251 }
1252 }
1253
1254 return 0;
1255}
1256
1257/*
1258 * The type nl80211_tx_power_setting replaces the following
1259 * data type from 2.6.36 onwards
1260*/
1261static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1262 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001263 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001264{
1265 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301266 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001267 u8 ath6kl_dbm;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001268 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001269
1270 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1271 type, dbm);
1272
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301273 vif = ath6kl_vif_first(ar);
1274 if (!vif)
1275 return -EIO;
1276
1277 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001278 return -EIO;
1279
1280 switch (type) {
1281 case NL80211_TX_POWER_AUTOMATIC:
1282 return 0;
1283 case NL80211_TX_POWER_LIMITED:
1284 ar->tx_pwr = ath6kl_dbm = dbm;
1285 break;
1286 default:
1287 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1288 __func__, type);
1289 return -EOPNOTSUPP;
1290 }
1291
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301292 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001293
1294 return 0;
1295}
1296
1297static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1298{
1299 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301300 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001301
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301302 vif = ath6kl_vif_first(ar);
1303 if (!vif)
1304 return -EIO;
1305
1306 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001307 return -EIO;
1308
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301309 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001310 ar->tx_pwr = 0;
1311
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301312 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001313 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1314 return -EIO;
1315 }
1316
1317 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1318 5 * HZ);
1319
1320 if (signal_pending(current)) {
1321 ath6kl_err("target did not respond\n");
1322 return -EINTR;
1323 }
1324 }
1325
1326 *dbm = ar->tx_pwr;
1327 return 0;
1328}
1329
1330static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1331 struct net_device *dev,
1332 bool pmgmt, int timeout)
1333{
1334 struct ath6kl *ar = ath6kl_priv(dev);
1335 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301336 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001337
1338 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1339 __func__, pmgmt, timeout);
1340
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301341 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001342 return -EIO;
1343
1344 if (pmgmt) {
1345 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1346 mode.pwr_mode = REC_POWER;
1347 } else {
1348 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1349 mode.pwr_mode = MAX_PERF_POWER;
1350 }
1351
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301352 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
1353 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001354 ath6kl_err("wmi_powermode_cmd failed\n");
1355 return -EIO;
1356 }
1357
1358 return 0;
1359}
1360
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301361static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1362 char *name,
1363 enum nl80211_iftype type,
1364 u32 *flags,
1365 struct vif_params *params)
1366{
1367 struct ath6kl *ar = wiphy_priv(wiphy);
1368 struct net_device *ndev;
1369 u8 if_idx, nw_type;
1370
Kalle Valo71f96ee2011-11-14 19:31:30 +02001371 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301372 ath6kl_err("Reached maximum number of supported vif\n");
1373 return ERR_PTR(-EINVAL);
1374 }
1375
1376 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1377 ath6kl_err("Not a supported interface type\n");
1378 return ERR_PTR(-EINVAL);
1379 }
1380
1381 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1382 if (!ndev)
1383 return ERR_PTR(-ENOMEM);
1384
1385 ar->num_vif++;
1386
1387 return ndev;
1388}
1389
1390static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1391 struct net_device *ndev)
1392{
1393 struct ath6kl *ar = wiphy_priv(wiphy);
1394 struct ath6kl_vif *vif = netdev_priv(ndev);
1395
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301396 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301397 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301398 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301399
1400 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1401
Kalle Valoc25889e2012-01-17 20:08:27 +02001402 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301403
1404 return 0;
1405}
1406
Kalle Valobdcd8172011-07-18 00:22:30 +03001407static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1408 struct net_device *ndev,
1409 enum nl80211_iftype type, u32 *flags,
1410 struct vif_params *params)
1411{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301412 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001413
1414 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1415
Kalle Valobdcd8172011-07-18 00:22:30 +03001416 switch (type) {
1417 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301418 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001419 break;
1420 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301421 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001422 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001423 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301424 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001425 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001426 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301427 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001428 break;
1429 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301430 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001431 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001432 default:
1433 ath6kl_err("invalid interface type %u\n", type);
1434 return -EOPNOTSUPP;
1435 }
1436
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301437 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001438
1439 return 0;
1440}
1441
1442static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1443 struct net_device *dev,
1444 struct cfg80211_ibss_params *ibss_param)
1445{
1446 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301447 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001448 int status;
1449
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301450 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001451 return -EIO;
1452
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301453 vif->ssid_len = ibss_param->ssid_len;
1454 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001455
1456 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301457 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001458
1459 if (ibss_param->channel_fixed) {
1460 /*
1461 * TODO: channel_fixed: The channel should be fixed, do not
1462 * search for IBSSs to join on other channels. Target
1463 * firmware does not support this feature, needs to be
1464 * updated.
1465 */
1466 return -EOPNOTSUPP;
1467 }
1468
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301469 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001470 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301471 memcpy(vif->req_bssid, ibss_param->bssid,
1472 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001473
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301474 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001475
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301476 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001477 if (status)
1478 return status;
1479
1480 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301481 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1482 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001483 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301484 ath6kl_set_cipher(vif, 0, true);
1485 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001486 }
1487
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301488 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001489
1490 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1491 "%s: connect called with authmode %d dot11 auth %d"
1492 " PW crypto %d PW crypto len %d GRP crypto %d"
1493 " GRP crypto len %d channel hint %u\n",
1494 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301495 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1496 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301497 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001498
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301499 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301500 vif->dot11_auth_mode, vif->auth_mode,
1501 vif->prwise_crypto,
1502 vif->prwise_crypto_len,
1503 vif->grp_crypto, vif->grp_crypto_len,
1504 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301505 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001506 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301507 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001508
1509 return 0;
1510}
1511
1512static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1513 struct net_device *dev)
1514{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301515 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001516
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301517 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001518 return -EIO;
1519
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301520 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301521 memset(vif->ssid, 0, sizeof(vif->ssid));
1522 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001523
1524 return 0;
1525}
1526
1527static const u32 cipher_suites[] = {
1528 WLAN_CIPHER_SUITE_WEP40,
1529 WLAN_CIPHER_SUITE_WEP104,
1530 WLAN_CIPHER_SUITE_TKIP,
1531 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001532 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001533 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001534};
1535
1536static bool is_rate_legacy(s32 rate)
1537{
1538 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1539 6000, 9000, 12000, 18000, 24000,
1540 36000, 48000, 54000
1541 };
1542 u8 i;
1543
1544 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1545 if (rate == legacy[i])
1546 return true;
1547
1548 return false;
1549}
1550
1551static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1552{
1553 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1554 52000, 58500, 65000, 72200
1555 };
1556 u8 i;
1557
1558 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1559 if (rate == ht20[i]) {
1560 if (i == ARRAY_SIZE(ht20) - 1)
1561 /* last rate uses sgi */
1562 *sgi = true;
1563 else
1564 *sgi = false;
1565
1566 *mcs = i;
1567 return true;
1568 }
1569 }
1570 return false;
1571}
1572
1573static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1574{
1575 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1576 81000, 108000, 121500, 135000,
1577 150000
1578 };
1579 u8 i;
1580
1581 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1582 if (rate == ht40[i]) {
1583 if (i == ARRAY_SIZE(ht40) - 1)
1584 /* last rate uses sgi */
1585 *sgi = true;
1586 else
1587 *sgi = false;
1588
1589 *mcs = i;
1590 return true;
1591 }
1592 }
1593
1594 return false;
1595}
1596
1597static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1598 u8 *mac, struct station_info *sinfo)
1599{
1600 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301601 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001602 long left;
1603 bool sgi;
1604 s32 rate;
1605 int ret;
1606 u8 mcs;
1607
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301608 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001609 return -ENOENT;
1610
1611 if (down_interruptible(&ar->sem))
1612 return -EBUSY;
1613
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301614 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001615
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301616 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001617
1618 if (ret != 0) {
1619 up(&ar->sem);
1620 return -EIO;
1621 }
1622
1623 left = wait_event_interruptible_timeout(ar->event_wq,
1624 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301625 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001626 WMI_TIMEOUT);
1627
1628 up(&ar->sem);
1629
1630 if (left == 0)
1631 return -ETIMEDOUT;
1632 else if (left < 0)
1633 return left;
1634
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301635 if (vif->target_stats.rx_byte) {
1636 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001637 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301638 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001639 sinfo->filled |= STATION_INFO_RX_PACKETS;
1640 }
1641
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301642 if (vif->target_stats.tx_byte) {
1643 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001644 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301645 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001646 sinfo->filled |= STATION_INFO_TX_PACKETS;
1647 }
1648
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301649 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001650 sinfo->filled |= STATION_INFO_SIGNAL;
1651
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301652 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001653
1654 if (is_rate_legacy(rate)) {
1655 sinfo->txrate.legacy = rate / 100;
1656 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1657 if (sgi) {
1658 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1659 sinfo->txrate.mcs = mcs - 1;
1660 } else {
1661 sinfo->txrate.mcs = mcs;
1662 }
1663
1664 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1665 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1666 if (sgi) {
1667 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1668 sinfo->txrate.mcs = mcs - 1;
1669 } else {
1670 sinfo->txrate.mcs = mcs;
1671 }
1672
1673 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1674 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1675 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001676 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1677 "invalid rate from stats: %d\n", rate);
1678 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001679 return 0;
1680 }
1681
1682 sinfo->filled |= STATION_INFO_TX_BITRATE;
1683
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301684 if (test_bit(CONNECTED, &vif->flags) &&
1685 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301686 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001687 sinfo->filled |= STATION_INFO_BSS_PARAM;
1688 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301689 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1690 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001691 }
1692
Kalle Valobdcd8172011-07-18 00:22:30 +03001693 return 0;
1694}
1695
1696static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1697 struct cfg80211_pmksa *pmksa)
1698{
1699 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301700 struct ath6kl_vif *vif = netdev_priv(netdev);
1701
1702 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001703 pmksa->pmkid, true);
1704}
1705
1706static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1707 struct cfg80211_pmksa *pmksa)
1708{
1709 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301710 struct ath6kl_vif *vif = netdev_priv(netdev);
1711
1712 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001713 pmksa->pmkid, false);
1714}
1715
1716static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1717{
1718 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301719 struct ath6kl_vif *vif = netdev_priv(netdev);
1720
1721 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301722 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1723 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001724 return 0;
1725}
1726
Raja Manid91e8ee2012-01-30 17:13:10 +05301727static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1728 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001729{
Raja Manid91e8ee2012-01-30 17:13:10 +05301730 int ret, pos;
1731 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001732 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001733
Raja Manid91e8ee2012-01-30 17:13:10 +05301734 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001735 for (i = 0; i < wow->n_patterns; i++) {
1736
1737 /*
1738 * Convert given nl80211 specific mask value to equivalent
1739 * driver specific mask value and send it to the chip along
1740 * with patterns. For example, If the mask value defined in
1741 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1742 * then equivalent driver specific mask value is
1743 * "0xFF 0x00 0xFF 0x00".
1744 */
1745 memset(&mask, 0, sizeof(mask));
1746 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1747 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1748 mask[pos] = 0xFF;
1749 }
1750 /*
1751 * Note: Pattern's offset is not passed as part of wowlan
1752 * parameter from CFG layer. So it's always passed as ZERO
1753 * to the firmware. It means, given WOW patterns are always
1754 * matched from the first byte of received pkt in the firmware.
1755 */
1756 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301757 vif->fw_vif_idx, WOW_LIST_ID,
1758 wow->patterns[i].pattern_len,
1759 0 /* pattern offset */,
1760 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001761 if (ret)
1762 return ret;
1763 }
1764
Raja Manid91e8ee2012-01-30 17:13:10 +05301765 if (wow->disconnect)
1766 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1767
1768 if (wow->magic_pkt)
1769 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1770
1771 if (wow->gtk_rekey_failure)
1772 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1773
1774 if (wow->eap_identity_req)
1775 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1776
1777 if (wow->four_way_handshake)
1778 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1779
1780 return 0;
1781}
1782
1783static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1784{
1785 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1786 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1787 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1788 0x00, 0x08 };
1789 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1790 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1791 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1792 0x00, 0x7f };
1793 u8 unicst_offset = 0;
1794 static const u8 arp_pattern[] = { 0x08, 0x06 };
1795 static const u8 arp_mask[] = { 0xff, 0xff };
1796 u8 arp_offset = 20;
1797 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1798 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1799 u8 discvr_offset = 38;
1800 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1801 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1802 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1803 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1804 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1805 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1806 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1807 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1808 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1809 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1810 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1811 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1812 u8 dhcp_offset = 0;
1813 int ret;
1814
1815 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1816 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1817 vif->fw_vif_idx, WOW_LIST_ID,
1818 sizeof(unicst_pattern), unicst_offset,
1819 unicst_pattern, unicst_mask);
1820 if (ret) {
1821 ath6kl_err("failed to add WOW unicast IP pattern\n");
1822 return ret;
1823 }
1824
1825 /* Setup all ARP pkt pattern */
1826 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1827 vif->fw_vif_idx, WOW_LIST_ID,
1828 sizeof(arp_pattern), arp_offset,
1829 arp_pattern, arp_mask);
1830 if (ret) {
1831 ath6kl_err("failed to add WOW ARP pattern\n");
1832 return ret;
1833 }
1834
1835 /*
1836 * Setup multicast pattern for mDNS 224.0.0.251,
1837 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1838 */
1839 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1840 vif->fw_vif_idx, WOW_LIST_ID,
1841 sizeof(discvr_pattern), discvr_offset,
1842 discvr_pattern, discvr_mask);
1843 if (ret) {
1844 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1845 return ret;
1846 }
1847
1848 /* Setup all DHCP broadcast pkt pattern */
1849 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1850 vif->fw_vif_idx, WOW_LIST_ID,
1851 sizeof(dhcp_pattern), dhcp_offset,
1852 dhcp_pattern, dhcp_mask);
1853 if (ret) {
1854 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
1855 return ret;
1856 }
1857
1858 return 0;
1859}
1860
1861static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
1862{
1863 struct net_device *ndev = vif->ndev;
1864 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1865 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1866 u8 discvr_offset = 38;
1867 u8 mac_mask[ETH_ALEN];
1868 int ret;
1869
1870 /* Setup unicast pkt pattern */
1871 memset(mac_mask, 0xff, ETH_ALEN);
1872 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1873 vif->fw_vif_idx, WOW_LIST_ID,
1874 ETH_ALEN, 0, ndev->dev_addr,
1875 mac_mask);
1876 if (ret) {
1877 ath6kl_err("failed to add WOW unicast pattern\n");
1878 return ret;
1879 }
1880
1881 /*
1882 * Setup multicast pattern for mDNS 224.0.0.251,
1883 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1884 */
1885 if ((ndev->flags & IFF_ALLMULTI) ||
1886 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
1887 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1888 vif->fw_vif_idx, WOW_LIST_ID,
1889 sizeof(discvr_pattern), discvr_offset,
1890 discvr_pattern, discvr_mask);
1891 if (ret) {
1892 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
1893 "pattern\n");
1894 return ret;
1895 }
1896 }
1897
1898 return 0;
1899}
1900
1901static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1902{
1903 struct in_device *in_dev;
1904 struct in_ifaddr *ifa;
1905 struct ath6kl_vif *vif;
1906 int ret, left;
1907 u32 filter = 0;
1908 u16 i;
1909 u8 index = 0;
1910 __be32 ips[MAX_IP_ADDRS];
1911
1912 vif = ath6kl_vif_first(ar);
1913 if (!vif)
1914 return -EIO;
1915
1916 if (!ath6kl_cfg80211_ready(vif))
1917 return -EIO;
1918
1919 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05301920 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05301921
1922 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
1923 return -EINVAL;
1924
1925 /* Clear existing WOW patterns */
1926 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
1927 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
1928 WOW_LIST_ID, i);
1929
1930 /*
1931 * Skip the default WOW pattern configuration
1932 * if the driver receives any WOW patterns from
1933 * the user.
1934 */
1935 if (wow)
1936 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
1937 else if (vif->nw_type == AP_NETWORK)
1938 ret = ath6kl_wow_ap(ar, vif);
1939 else
1940 ret = ath6kl_wow_sta(ar, vif);
1941
1942 if (ret)
1943 return ret;
1944
Raja Manic08631c2011-12-16 14:24:24 +05301945 /* Setup own IP addr for ARP agent. */
1946 in_dev = __in_dev_get_rtnl(vif->ndev);
1947 if (!in_dev)
1948 goto skip_arp;
1949
1950 ifa = in_dev->ifa_list;
1951 memset(&ips, 0, sizeof(ips));
1952
1953 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
1954 while (index < MAX_IP_ADDRS && ifa) {
1955 ips[index] = ifa->ifa_local;
1956 ifa = ifa->ifa_next;
1957 index++;
1958 }
1959
1960 if (ifa) {
1961 ath6kl_err("total IP addr count is exceeding fw limit\n");
1962 return -EINVAL;
1963 }
1964
1965 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
1966 if (ret) {
1967 ath6kl_err("fail to setup ip for arp agent\n");
1968 return ret;
1969 }
1970
1971skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02001972 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
1973 ATH6KL_WOW_MODE_ENABLE,
1974 filter,
1975 WOW_HOST_REQ_DELAY);
1976 if (ret)
1977 return ret;
1978
Raja Mani081c7a82012-01-30 17:13:11 +05301979 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
1980
Raja Mani6cb3c712011-11-07 22:52:45 +02001981 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1982 ATH6KL_HOST_MODE_ASLEEP);
1983 if (ret)
1984 return ret;
1985
Raja Mani081c7a82012-01-30 17:13:11 +05301986 left = wait_event_interruptible_timeout(ar->event_wq,
1987 test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags),
1988 WMI_TIMEOUT);
1989 if (left == 0) {
1990 ath6kl_warn("timeout, didn't get host sleep cmd "
1991 "processed event\n");
1992 ret = -ETIMEDOUT;
1993 } else if (left < 0) {
1994 ath6kl_warn("error while waiting for host sleep cmd "
1995 "processed event %d\n", left);
1996 ret = left;
1997 }
1998
Raja Mani6cb3c712011-11-07 22:52:45 +02001999 if (ar->tx_pending[ar->ctrl_ep]) {
2000 left = wait_event_interruptible_timeout(ar->event_wq,
2001 ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
2002 if (left == 0) {
2003 ath6kl_warn("clear wmi ctrl data timeout\n");
2004 ret = -ETIMEDOUT;
2005 } else if (left < 0) {
2006 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
2007 ret = left;
2008 }
2009 }
2010
2011 return ret;
2012}
2013
2014static int ath6kl_wow_resume(struct ath6kl *ar)
2015{
2016 struct ath6kl_vif *vif;
2017 int ret;
2018
2019 vif = ath6kl_vif_first(ar);
2020 if (!vif)
2021 return -EIO;
2022
2023 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2024 ATH6KL_HOST_MODE_AWAKE);
2025 return ret;
2026}
2027
Kalle Valo52d81a62011-11-01 08:44:21 +02002028int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002029 enum ath6kl_cfg_suspend_mode mode,
2030 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002031{
2032 int ret;
2033
Kalle Valo52d81a62011-11-01 08:44:21 +02002034 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002035 case ATH6KL_CFG_SUSPEND_WOW:
2036
2037 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2038
2039 /* Flush all non control pkts in TX path */
2040 ath6kl_tx_data_cleanup(ar);
2041
2042 ret = ath6kl_wow_suspend(ar, wow);
2043 if (ret) {
2044 ath6kl_err("wow suspend failed: %d\n", ret);
2045 return ret;
2046 }
2047 ar->state = ATH6KL_STATE_WOW;
2048 break;
2049
Kalle Valo52d81a62011-11-01 08:44:21 +02002050 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002051
Kalle Valo7125f012011-12-13 14:51:37 +02002052 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002053
Kalle Valo52d81a62011-11-01 08:44:21 +02002054 /* save the current power mode before enabling power save */
2055 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2056
2057 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2058 if (ret) {
2059 ath6kl_warn("wmi powermode command failed during suspend: %d\n",
2060 ret);
2061 }
2062
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002063 ar->state = ATH6KL_STATE_DEEPSLEEP;
2064
Kalle Valo52d81a62011-11-01 08:44:21 +02002065 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002066
2067 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002068
Kalle Valo7125f012011-12-13 14:51:37 +02002069 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002070
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002071 if (ar->state == ATH6KL_STATE_OFF) {
2072 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2073 "suspend hw off, no action for cutpower\n");
2074 break;
2075 }
2076
2077 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2078
2079 ret = ath6kl_init_hw_stop(ar);
2080 if (ret) {
2081 ath6kl_warn("failed to stop hw during suspend: %d\n",
2082 ret);
2083 }
2084
2085 ar->state = ATH6KL_STATE_CUTPOWER;
2086
2087 break;
2088
Kalle Valo10509f92011-12-13 14:52:07 +02002089 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2090 /*
2091 * Nothing needed for schedule scan, firmware is already in
2092 * wow mode and sleeping most of the time.
2093 */
2094 break;
2095
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002096 default:
2097 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002098 }
2099
2100 return 0;
2101}
Kalle Valod6a434d2012-01-17 20:09:36 +02002102EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002103
2104int ath6kl_cfg80211_resume(struct ath6kl *ar)
2105{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002106 int ret;
2107
2108 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002109 case ATH6KL_STATE_WOW:
2110 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2111
2112 ret = ath6kl_wow_resume(ar);
2113 if (ret) {
2114 ath6kl_warn("wow mode resume failed: %d\n", ret);
2115 return ret;
2116 }
2117
2118 ar->state = ATH6KL_STATE_ON;
2119 break;
2120
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002121 case ATH6KL_STATE_DEEPSLEEP:
2122 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2123 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2124 ar->wmi->saved_pwr_mode);
2125 if (ret) {
2126 ath6kl_warn("wmi powermode command failed during resume: %d\n",
2127 ret);
2128 }
2129 }
2130
2131 ar->state = ATH6KL_STATE_ON;
2132
2133 break;
2134
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002135 case ATH6KL_STATE_CUTPOWER:
2136 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2137
2138 ret = ath6kl_init_hw_start(ar);
2139 if (ret) {
2140 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2141 return ret;
2142 }
Raja Manid7c44e02011-11-07 22:52:46 +02002143 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002144
Kalle Valo10509f92011-12-13 14:52:07 +02002145 case ATH6KL_STATE_SCHED_SCAN:
2146 break;
2147
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002148 default:
2149 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002150 }
2151
2152 return 0;
2153}
Kalle Valod6a434d2012-01-17 20:09:36 +02002154EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002155
Kalle Valoabcb3442011-07-22 08:26:20 +03002156#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002157
2158/* hif layer decides what suspend mode to use */
2159static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002160 struct cfg80211_wowlan *wow)
2161{
2162 struct ath6kl *ar = wiphy_priv(wiphy);
2163
Raja Mani0f60e9f2011-11-07 22:52:45 +02002164 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002165}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002166
Kalle Valo52d81a62011-11-01 08:44:21 +02002167static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002168{
2169 struct ath6kl *ar = wiphy_priv(wiphy);
2170
2171 return ath6kl_hif_resume(ar);
2172}
Raja Mania918fb32011-11-07 22:52:46 +02002173
2174/*
2175 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2176 * both sdio irq wake up and keep power. The target pulls sdio data line to
2177 * wake up the host when WOW pattern matches. This causes sdio irq handler
2178 * is being called in the host side which internally hits ath6kl's RX path.
2179 *
2180 * Since sdio interrupt is not disabled, RX path executes even before
2181 * the host executes the actual resume operation from PM module.
2182 *
2183 * In the current scenario, WOW resume should happen before start processing
2184 * any data from the target. So It's required to perform WOW resume in RX path.
2185 * Ideally we should perform WOW resume only in the actual platform
2186 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2187 *
2188 * ath6kl_check_wow_status() is called from ath6kl_rx().
2189 */
2190void ath6kl_check_wow_status(struct ath6kl *ar)
2191{
2192 if (ar->state == ATH6KL_STATE_WOW)
2193 ath6kl_cfg80211_resume(ar);
2194}
2195
2196#else
2197
2198void ath6kl_check_wow_status(struct ath6kl *ar)
2199{
2200}
Kalle Valoabcb3442011-07-22 08:26:20 +03002201#endif
2202
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002203static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2204 struct ieee80211_channel *chan,
2205 enum nl80211_channel_type channel_type)
2206{
Sujith Manoharane68f6752011-12-22 12:15:27 +05302207 struct ath6kl_vif *vif;
2208
2209 /*
2210 * 'dev' could be NULL if a channel change is required for the hardware
2211 * device itself, instead of a particular VIF.
2212 *
2213 * FIXME: To be handled properly when monitor mode is supported.
2214 */
2215 if (!dev)
2216 return -EBUSY;
2217
2218 vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002219
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302220 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002221 return -EIO;
2222
2223 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2224 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302225 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002226
2227 return 0;
2228}
2229
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002230static bool ath6kl_is_p2p_ie(const u8 *pos)
2231{
2232 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2233 pos[2] == 0x50 && pos[3] == 0x6f &&
2234 pos[4] == 0x9a && pos[5] == 0x09;
2235}
2236
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302237static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2238 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002239{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302240 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002241 const u8 *pos;
2242 u8 *buf = NULL;
2243 size_t len = 0;
2244 int ret;
2245
2246 /*
2247 * Filter out P2P IE(s) since they will be included depending on
2248 * the Probe Request frame in ath6kl_send_go_probe_resp().
2249 */
2250
2251 if (ies && ies_len) {
2252 buf = kmalloc(ies_len, GFP_KERNEL);
2253 if (buf == NULL)
2254 return -ENOMEM;
2255 pos = ies;
2256 while (pos + 1 < ies + ies_len) {
2257 if (pos + 2 + pos[1] > ies + ies_len)
2258 break;
2259 if (!ath6kl_is_p2p_ie(pos)) {
2260 memcpy(buf + len, pos, 2 + pos[1]);
2261 len += 2 + pos[1];
2262 }
2263 pos += 2 + pos[1];
2264 }
2265 }
2266
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302267 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2268 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002269 kfree(buf);
2270 return ret;
2271}
2272
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002273static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
2274 struct beacon_parameters *info, bool add)
2275{
2276 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302277 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002278 struct ieee80211_mgmt *mgmt;
2279 u8 *ies;
2280 int ies_len;
2281 struct wmi_connect_cmd p;
2282 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302283 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002284
2285 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
2286
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302287 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002288 return -EIO;
2289
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302290 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002291 return -EOPNOTSUPP;
2292
2293 if (info->beacon_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302294 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2295 WMI_FRAME_BEACON,
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002296 info->beacon_ies,
2297 info->beacon_ies_len);
2298 if (res)
2299 return res;
2300 }
2301 if (info->proberesp_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302302 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002303 info->proberesp_ies_len);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002304 if (res)
2305 return res;
2306 }
2307 if (info->assocresp_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302308 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2309 WMI_FRAME_ASSOC_RESP,
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002310 info->assocresp_ies,
2311 info->assocresp_ies_len);
2312 if (res)
2313 return res;
2314 }
2315
2316 if (!add)
2317 return 0;
2318
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002319 ar->ap_mode_bkey.valid = false;
2320
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002321 /* TODO:
2322 * info->interval
2323 * info->dtim_period
2324 */
2325
2326 if (info->head == NULL)
2327 return -EINVAL;
2328 mgmt = (struct ieee80211_mgmt *) info->head;
2329 ies = mgmt->u.beacon.variable;
2330 if (ies > info->head + info->head_len)
2331 return -EINVAL;
2332 ies_len = info->head + info->head_len - ies;
2333
2334 if (info->ssid == NULL)
2335 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302336 memcpy(vif->ssid, info->ssid, info->ssid_len);
2337 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002338 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
2339 return -EOPNOTSUPP; /* TODO */
2340
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302341 ret = ath6kl_set_auth_type(vif, info->auth_type);
2342 if (ret)
2343 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002344
2345 memset(&p, 0, sizeof(p));
2346
2347 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2348 switch (info->crypto.akm_suites[i]) {
2349 case WLAN_AKM_SUITE_8021X:
2350 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2351 p.auth_mode |= WPA_AUTH;
2352 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2353 p.auth_mode |= WPA2_AUTH;
2354 break;
2355 case WLAN_AKM_SUITE_PSK:
2356 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2357 p.auth_mode |= WPA_PSK_AUTH;
2358 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2359 p.auth_mode |= WPA2_PSK_AUTH;
2360 break;
2361 }
2362 }
2363 if (p.auth_mode == 0)
2364 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302365 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002366
2367 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2368 switch (info->crypto.ciphers_pairwise[i]) {
2369 case WLAN_CIPHER_SUITE_WEP40:
2370 case WLAN_CIPHER_SUITE_WEP104:
2371 p.prwise_crypto_type |= WEP_CRYPT;
2372 break;
2373 case WLAN_CIPHER_SUITE_TKIP:
2374 p.prwise_crypto_type |= TKIP_CRYPT;
2375 break;
2376 case WLAN_CIPHER_SUITE_CCMP:
2377 p.prwise_crypto_type |= AES_CRYPT;
2378 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002379 case WLAN_CIPHER_SUITE_SMS4:
2380 p.prwise_crypto_type |= WAPI_CRYPT;
2381 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002382 }
2383 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002384 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002385 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302386 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002387 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302388 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002389
2390 switch (info->crypto.cipher_group) {
2391 case WLAN_CIPHER_SUITE_WEP40:
2392 case WLAN_CIPHER_SUITE_WEP104:
2393 p.grp_crypto_type = WEP_CRYPT;
2394 break;
2395 case WLAN_CIPHER_SUITE_TKIP:
2396 p.grp_crypto_type = TKIP_CRYPT;
2397 break;
2398 case WLAN_CIPHER_SUITE_CCMP:
2399 p.grp_crypto_type = AES_CRYPT;
2400 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002401 case WLAN_CIPHER_SUITE_SMS4:
2402 p.grp_crypto_type = WAPI_CRYPT;
2403 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002404 default:
2405 p.grp_crypto_type = NONE_CRYPT;
2406 break;
2407 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302408 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002409
2410 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302411 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002412
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302413 p.ssid_len = vif->ssid_len;
2414 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2415 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302416 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002417
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302418 /* Enable uAPSD support by default */
2419 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2420 if (res < 0)
2421 return res;
2422
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002423 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2424 p.nw_subtype = SUBTYPE_P2PGO;
2425 } else {
2426 /*
2427 * Due to firmware limitation, it is not possible to
2428 * do P2P mgmt operations in AP mode
2429 */
2430 p.nw_subtype = SUBTYPE_NONE;
2431 }
2432
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302433 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002434 if (res < 0)
2435 return res;
2436
2437 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002438}
2439
2440static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
2441 struct beacon_parameters *info)
2442{
2443 return ath6kl_ap_beacon(wiphy, dev, info, true);
2444}
2445
2446static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
2447 struct beacon_parameters *info)
2448{
2449 return ath6kl_ap_beacon(wiphy, dev, info, false);
2450}
2451
2452static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
2453{
2454 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302455 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002456
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302457 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002458 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302459 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002460 return -ENOTCONN;
2461
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302462 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302463 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002464
2465 return 0;
2466}
2467
Jouni Malinen33e53082011-12-27 11:02:56 +02002468static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2469
2470static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2471 u8 *mac)
2472{
2473 struct ath6kl *ar = ath6kl_priv(dev);
2474 struct ath6kl_vif *vif = netdev_priv(dev);
2475 const u8 *addr = mac ? mac : bcast_addr;
2476
2477 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2478 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2479}
2480
Jouni Malinen23875132011-08-30 21:57:53 +03002481static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2482 u8 *mac, struct station_parameters *params)
2483{
2484 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302485 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002486
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302487 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002488 return -EOPNOTSUPP;
2489
2490 /* Use this only for authorizing/unauthorizing a station */
2491 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2492 return -EOPNOTSUPP;
2493
2494 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302495 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2496 WMI_AP_MLME_AUTHORIZE, mac, 0);
2497 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2498 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002499}
2500
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002501static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2502 struct net_device *dev,
2503 struct ieee80211_channel *chan,
2504 enum nl80211_channel_type channel_type,
2505 unsigned int duration,
2506 u64 *cookie)
2507{
2508 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302509 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002510 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002511
2512 /* TODO: if already pending or ongoing remain-on-channel,
2513 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002514 id = ++vif->last_roc_id;
2515 if (id == 0) {
2516 /* Do not use 0 as the cookie value */
2517 id = ++vif->last_roc_id;
2518 }
2519 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002520
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302521 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2522 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002523}
2524
2525static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2526 struct net_device *dev,
2527 u64 cookie)
2528{
2529 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302530 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002531
Jouni Malinen10522612011-10-27 16:00:13 +03002532 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002533 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002534 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002535
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302536 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002537}
2538
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302539static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2540 const u8 *buf, size_t len,
2541 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002542{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302543 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002544 const u8 *pos;
2545 u8 *p2p;
2546 int p2p_len;
2547 int ret;
2548 const struct ieee80211_mgmt *mgmt;
2549
2550 mgmt = (const struct ieee80211_mgmt *) buf;
2551
2552 /* Include P2P IE(s) from the frame generated in user space. */
2553
2554 p2p = kmalloc(len, GFP_KERNEL);
2555 if (p2p == NULL)
2556 return -ENOMEM;
2557 p2p_len = 0;
2558
2559 pos = mgmt->u.probe_resp.variable;
2560 while (pos + 1 < buf + len) {
2561 if (pos + 2 + pos[1] > buf + len)
2562 break;
2563 if (ath6kl_is_p2p_ie(pos)) {
2564 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2565 p2p_len += 2 + pos[1];
2566 }
2567 pos += 2 + pos[1];
2568 }
2569
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302570 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2571 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002572 kfree(p2p);
2573 return ret;
2574}
2575
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002576static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
2577 u32 id,
2578 u32 freq,
2579 u32 wait,
2580 const u8 *buf,
2581 size_t len,
2582 bool *more_data,
2583 bool no_cck)
2584{
2585 struct ieee80211_mgmt *mgmt;
2586 struct ath6kl_sta *conn;
2587 bool is_psq_empty = false;
2588 struct ath6kl_mgmt_buff *mgmt_buf;
2589 size_t mgmt_buf_size;
2590 struct ath6kl *ar = vif->ar;
2591
2592 mgmt = (struct ieee80211_mgmt *) buf;
2593 if (is_multicast_ether_addr(mgmt->da))
2594 return false;
2595
2596 conn = ath6kl_find_sta(vif, mgmt->da);
2597 if (!conn)
2598 return false;
2599
2600 if (conn->sta_flags & STA_PS_SLEEP) {
2601 if (!(conn->sta_flags & STA_PS_POLLED)) {
2602 /* Queue the frames if the STA is sleeping */
2603 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
2604 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
2605 if (!mgmt_buf)
2606 return false;
2607
2608 INIT_LIST_HEAD(&mgmt_buf->list);
2609 mgmt_buf->id = id;
2610 mgmt_buf->freq = freq;
2611 mgmt_buf->wait = wait;
2612 mgmt_buf->len = len;
2613 mgmt_buf->no_cck = no_cck;
2614 memcpy(mgmt_buf->buf, buf, len);
2615 spin_lock_bh(&conn->psq_lock);
2616 is_psq_empty = skb_queue_empty(&conn->psq) &&
2617 (conn->mgmt_psq_len == 0);
2618 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
2619 conn->mgmt_psq_len++;
2620 spin_unlock_bh(&conn->psq_lock);
2621
2622 /*
2623 * If this is the first pkt getting queued
2624 * for this STA, update the PVB for this
2625 * STA.
2626 */
2627 if (is_psq_empty)
2628 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
2629 conn->aid, 1);
2630 return true;
2631 }
2632
2633 /*
2634 * This tx is because of a PsPoll.
2635 * Determine if MoreData bit has to be set.
2636 */
2637 spin_lock_bh(&conn->psq_lock);
2638 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
2639 *more_data = true;
2640 spin_unlock_bh(&conn->psq_lock);
2641 }
2642
2643 return false;
2644}
2645
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002646static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2647 struct ieee80211_channel *chan, bool offchan,
2648 enum nl80211_channel_type channel_type,
2649 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002650 const u8 *buf, size_t len, bool no_cck,
2651 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002652{
2653 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302654 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002655 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002656 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002657 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002658
2659 mgmt = (const struct ieee80211_mgmt *) buf;
2660 if (buf + len >= mgmt->u.probe_resp.variable &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302661 vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002662 ieee80211_is_probe_resp(mgmt->frame_control)) {
2663 /*
2664 * Send Probe Response frame in AP mode using a separate WMI
2665 * command to allow the target to fill in the generic IEs.
2666 */
2667 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302668 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002669 chan->center_freq);
2670 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002671
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302672 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002673 if (id == 0) {
2674 /*
2675 * 0 is a reserved value in the WMI command and shall not be
2676 * used for the command.
2677 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302678 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002679 }
2680
2681 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002682
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002683 /* AP mode Power saving processing */
2684 if (vif->nw_type == AP_NETWORK) {
2685 queued = ath6kl_mgmt_powersave_ap(vif,
2686 id, chan->center_freq,
2687 wait, buf,
2688 len, &more_data, no_cck);
2689 if (queued)
2690 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002691 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002692
2693 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2694 chan->center_freq, wait,
2695 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002696}
2697
Jouni Malinenae32c302011-08-30 21:58:01 +03002698static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2699 struct net_device *dev,
2700 u16 frame_type, bool reg)
2701{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302702 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002703
2704 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2705 __func__, frame_type, reg);
2706 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2707 /*
2708 * Note: This notification callback is not allowed to sleep, so
2709 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2710 * hardcode target to report Probe Request frames all the time.
2711 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302712 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002713 }
2714}
2715
Kalle Valo10509f92011-12-13 14:52:07 +02002716static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2717 struct net_device *dev,
2718 struct cfg80211_sched_scan_request *request)
2719{
2720 struct ath6kl *ar = ath6kl_priv(dev);
2721 struct ath6kl_vif *vif = netdev_priv(dev);
2722 u16 interval;
2723 int ret;
2724 u8 i;
2725
2726 if (ar->state != ATH6KL_STATE_ON)
2727 return -EIO;
2728
2729 if (vif->sme_state != SME_DISCONNECTED)
2730 return -EBUSY;
2731
2732 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2733 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2734 i, DISABLE_SSID_FLAG,
2735 0, NULL);
2736 }
2737
2738 /* fw uses seconds, also make sure that it's >0 */
2739 interval = max_t(u16, 1, request->interval / 1000);
2740
2741 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2742 interval, interval,
2743 10, 0, 0, 0, 3, 0, 0, 0);
2744
2745 if (request->n_ssids && request->ssids[0].ssid_len) {
2746 for (i = 0; i < request->n_ssids; i++) {
2747 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2748 i, SPECIFIC_SSID_FLAG,
2749 request->ssids[i].ssid_len,
2750 request->ssids[i].ssid);
2751 }
2752 }
2753
2754 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2755 ATH6KL_WOW_MODE_ENABLE,
2756 WOW_FILTER_SSID,
2757 WOW_HOST_REQ_DELAY);
2758 if (ret) {
2759 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2760 return ret;
2761 }
2762
2763 /* this also clears IE in fw if it's not set */
2764 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2765 WMI_FRAME_PROBE_REQ,
2766 request->ie, request->ie_len);
2767 if (ret) {
2768 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
2769 ret);
2770 return ret;
2771 }
2772
2773 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2774 ATH6KL_HOST_MODE_ASLEEP);
2775 if (ret) {
2776 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
2777 ret);
2778 return ret;
2779 }
2780
2781 ar->state = ATH6KL_STATE_SCHED_SCAN;
2782
2783 return ret;
2784}
2785
2786static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
2787 struct net_device *dev)
2788{
2789 struct ath6kl_vif *vif = netdev_priv(dev);
2790 bool stopped;
2791
2792 stopped = __ath6kl_cfg80211_sscan_stop(vif);
2793
2794 if (!stopped)
2795 return -EIO;
2796
2797 return 0;
2798}
2799
Jouni Malinenf80574a2011-08-30 21:58:04 +03002800static const struct ieee80211_txrx_stypes
2801ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2802 [NL80211_IFTYPE_STATION] = {
2803 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2804 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2805 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2806 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2807 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02002808 [NL80211_IFTYPE_AP] = {
2809 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2810 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2811 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2812 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2813 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03002814 [NL80211_IFTYPE_P2P_CLIENT] = {
2815 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2816 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2817 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2818 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2819 },
2820 [NL80211_IFTYPE_P2P_GO] = {
2821 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2822 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2823 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2824 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2825 },
2826};
2827
Kalle Valobdcd8172011-07-18 00:22:30 +03002828static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302829 .add_virtual_intf = ath6kl_cfg80211_add_iface,
2830 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03002831 .change_virtual_intf = ath6kl_cfg80211_change_iface,
2832 .scan = ath6kl_cfg80211_scan,
2833 .connect = ath6kl_cfg80211_connect,
2834 .disconnect = ath6kl_cfg80211_disconnect,
2835 .add_key = ath6kl_cfg80211_add_key,
2836 .get_key = ath6kl_cfg80211_get_key,
2837 .del_key = ath6kl_cfg80211_del_key,
2838 .set_default_key = ath6kl_cfg80211_set_default_key,
2839 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
2840 .set_tx_power = ath6kl_cfg80211_set_txpower,
2841 .get_tx_power = ath6kl_cfg80211_get_txpower,
2842 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
2843 .join_ibss = ath6kl_cfg80211_join_ibss,
2844 .leave_ibss = ath6kl_cfg80211_leave_ibss,
2845 .get_station = ath6kl_get_station,
2846 .set_pmksa = ath6kl_set_pmksa,
2847 .del_pmksa = ath6kl_del_pmksa,
2848 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03002849 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03002850#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002851 .suspend = __ath6kl_cfg80211_suspend,
2852 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03002853#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002854 .set_channel = ath6kl_set_channel,
2855 .add_beacon = ath6kl_add_beacon,
2856 .set_beacon = ath6kl_set_beacon,
2857 .del_beacon = ath6kl_del_beacon,
Jouni Malinen33e53082011-12-27 11:02:56 +02002858 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03002859 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002860 .remain_on_channel = ath6kl_remain_on_channel,
2861 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002862 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03002863 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02002864 .sched_scan_start = ath6kl_cfg80211_sscan_start,
2865 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03002866};
2867
Kalle Valo7125f012011-12-13 14:51:37 +02002868void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02002869{
Kalle Valo10509f92011-12-13 14:52:07 +02002870 ath6kl_cfg80211_sscan_disable(vif);
2871
Kalle Valoec4b7f62011-11-01 08:44:04 +02002872 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02002873 case SME_DISCONNECTED:
2874 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02002875 case SME_CONNECTING:
2876 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
2877 NULL, 0,
2878 WLAN_STATUS_UNSPECIFIED_FAILURE,
2879 GFP_KERNEL);
2880 break;
2881 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02002882 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
2883 break;
2884 }
2885
2886 if (test_bit(CONNECTED, &vif->flags) ||
2887 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02002888 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002889
2890 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02002891 clear_bit(CONNECTED, &vif->flags);
2892 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002893
2894 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02002895 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
2896 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
2897 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02002898
2899 ath6kl_cfg80211_scan_complete_event(vif, true);
2900}
2901
Kalle Valo7125f012011-12-13 14:51:37 +02002902void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
2903{
2904 struct ath6kl_vif *vif;
2905
2906 vif = ath6kl_vif_first(ar);
2907 if (!vif) {
2908 /* save the current power mode before enabling power save */
2909 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2910
2911 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
2912 ath6kl_warn("ath6kl_deep_sleep_enable: "
2913 "wmi_powermode_cmd failed\n");
2914 return;
2915 }
2916
2917 /*
2918 * FIXME: we should take ar->list_lock to protect changes in the
2919 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
2920 * sleeps.
2921 */
2922 list_for_each_entry(vif, &ar->vif_list, list)
2923 ath6kl_cfg80211_stop(vif);
2924}
2925
Kalle Valoc25889e2012-01-17 20:08:27 +02002926static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03002927{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05302928 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302929 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302930 ath6kl_err("failed to initialize aggr\n");
2931 return -ENOMEM;
2932 }
Kalle Valobdcd8172011-07-18 00:22:30 +03002933
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302934 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302935 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02002936 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
2937 (unsigned long) vif);
2938
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302939 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05302940 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302941
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05302942 INIT_LIST_HEAD(&vif->mc_filter);
2943
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302944 return 0;
2945}
2946
Kalle Valoc25889e2012-01-17 20:08:27 +02002947void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302948{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302949 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05302950 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302951
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302952 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302953
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302954 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
2955
2956 if (vif->nw_type == ADHOC_NETWORK)
2957 ar->ibss_if_active = false;
2958
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05302959 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
2960 list_del(&mc_filter->list);
2961 kfree(mc_filter);
2962 }
2963
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302964 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302965
2966 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302967}
2968
2969struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302970 enum nl80211_iftype type, u8 fw_vif_idx,
2971 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302972{
2973 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302974 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302975
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302976 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302977 if (!ndev)
2978 return NULL;
2979
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302980 vif = netdev_priv(ndev);
2981 ndev->ieee80211_ptr = &vif->wdev;
2982 vif->wdev.wiphy = ar->wiphy;
2983 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302984 vif->ndev = ndev;
2985 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
2986 vif->wdev.netdev = ndev;
2987 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302988 vif->fw_vif_idx = fw_vif_idx;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302989 vif->nw_type = vif->next_mode = nw_type;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302990
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302991 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
2992 if (fw_vif_idx != 0)
2993 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
2994 0x2;
2995
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302996 init_netdev(ndev);
2997
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05302998 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302999
Kalle Valoc25889e2012-01-17 20:08:27 +02003000 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303001 goto err;
3002
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303003 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303004 goto err;
3005
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303006 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303007 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303008 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303009 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303010 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303011
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303012 if (type == NL80211_IFTYPE_ADHOC)
3013 ar->ibss_if_active = true;
3014
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303015 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303016 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303017 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303018
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303019 return ndev;
3020
3021err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303022 aggr_module_destroy(vif->aggr_cntxt);
3023 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303024 return NULL;
3025}
3026
Kalle Valo46d33a22012-01-17 20:08:40 +02003027int ath6kl_cfg80211_init(struct ath6kl *ar)
3028{
3029 struct wiphy *wiphy = ar->wiphy;
3030 int ret;
3031
3032 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3033
3034 wiphy->max_remain_on_channel_duration = 5000;
3035
3036 /* set device pointer for wiphy */
3037 set_wiphy_dev(wiphy, ar->dev);
3038
3039 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3040 BIT(NL80211_IFTYPE_ADHOC) |
3041 BIT(NL80211_IFTYPE_AP);
3042 if (ar->p2p) {
3043 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3044 BIT(NL80211_IFTYPE_P2P_CLIENT);
3045 }
3046
3047 /* max num of ssids that can be probed during scanning */
3048 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
3049 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
3050 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3051 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3052 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3053
3054 wiphy->cipher_suites = cipher_suites;
3055 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3056
3057 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3058 WIPHY_WOWLAN_DISCONNECT |
3059 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3060 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3061 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3062 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3063 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3064 wiphy->wowlan.pattern_min_len = 1;
3065 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
3066
3067 wiphy->max_sched_scan_ssids = 10;
3068
3069 ret = wiphy_register(wiphy);
3070 if (ret < 0) {
3071 ath6kl_err("couldn't register wiphy device\n");
3072 return ret;
3073 }
3074
3075 return 0;
3076}
3077
3078void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303079{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303080 wiphy_unregister(ar->wiphy);
Kalle Valo45eaa782012-01-17 20:09:05 +02003081}
Kalle Valo46d33a22012-01-17 20:08:40 +02003082
Kalle Valo45eaa782012-01-17 20:09:05 +02003083struct ath6kl *ath6kl_cfg80211_create(void)
3084{
3085 struct ath6kl *ar;
3086 struct wiphy *wiphy;
3087
3088 /* create a new wiphy for use with cfg80211 */
3089 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3090
3091 if (!wiphy) {
3092 ath6kl_err("couldn't allocate wiphy device\n");
3093 return NULL;
3094 }
3095
3096 ar = wiphy_priv(wiphy);
3097 ar->wiphy = wiphy;
3098
3099 return ar;
3100}
3101
3102/* Note: ar variable must not be accessed after calling this! */
3103void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3104{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303105 int i;
3106
3107 for (i = 0; i < AP_MAX_NUM_STA; i++)
3108 kfree(ar->sta_list[i].aggr_conn);
3109
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303110 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003111}
Kalle Valo45eaa782012-01-17 20:09:05 +02003112