blob: 5370333883e4632c03e9ca9958c43631d0072119 [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040017#include <linux/moduleparam.h>
Raja Manic08631c2011-12-16 14:24:24 +053018#include <linux/inetdevice.h>
Kalle Valod6a434d2012-01-17 20:09:36 +020019#include <linux/export.h>
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040020
Kalle Valobdcd8172011-07-18 00:22:30 +030021#include "core.h"
22#include "cfg80211.h"
23#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030024#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030025#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030026
27#define RATETAB_ENT(_rate, _rateid, _flags) { \
28 .bitrate = (_rate), \
29 .flags = (_flags), \
30 .hw_value = (_rateid), \
31}
32
33#define CHAN2G(_channel, _freq, _flags) { \
34 .band = IEEE80211_BAND_2GHZ, \
35 .hw_value = (_channel), \
36 .center_freq = (_freq), \
37 .flags = (_flags), \
38 .max_antenna_gain = 0, \
39 .max_power = 30, \
40}
41
42#define CHAN5G(_channel, _flags) { \
43 .band = IEEE80211_BAND_5GHZ, \
44 .hw_value = (_channel), \
45 .center_freq = 5000 + (5 * (_channel)), \
46 .flags = (_flags), \
47 .max_antenna_gain = 0, \
48 .max_power = 30, \
49}
50
51static struct ieee80211_rate ath6kl_rates[] = {
52 RATETAB_ENT(10, 0x1, 0),
53 RATETAB_ENT(20, 0x2, 0),
54 RATETAB_ENT(55, 0x4, 0),
55 RATETAB_ENT(110, 0x8, 0),
56 RATETAB_ENT(60, 0x10, 0),
57 RATETAB_ENT(90, 0x20, 0),
58 RATETAB_ENT(120, 0x40, 0),
59 RATETAB_ENT(180, 0x80, 0),
60 RATETAB_ENT(240, 0x100, 0),
61 RATETAB_ENT(360, 0x200, 0),
62 RATETAB_ENT(480, 0x400, 0),
63 RATETAB_ENT(540, 0x800, 0),
64};
65
66#define ath6kl_a_rates (ath6kl_rates + 4)
67#define ath6kl_a_rates_size 8
68#define ath6kl_g_rates (ath6kl_rates + 0)
69#define ath6kl_g_rates_size 12
70
71static struct ieee80211_channel ath6kl_2ghz_channels[] = {
72 CHAN2G(1, 2412, 0),
73 CHAN2G(2, 2417, 0),
74 CHAN2G(3, 2422, 0),
75 CHAN2G(4, 2427, 0),
76 CHAN2G(5, 2432, 0),
77 CHAN2G(6, 2437, 0),
78 CHAN2G(7, 2442, 0),
79 CHAN2G(8, 2447, 0),
80 CHAN2G(9, 2452, 0),
81 CHAN2G(10, 2457, 0),
82 CHAN2G(11, 2462, 0),
83 CHAN2G(12, 2467, 0),
84 CHAN2G(13, 2472, 0),
85 CHAN2G(14, 2484, 0),
86};
87
88static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
89 CHAN5G(34, 0), CHAN5G(36, 0),
90 CHAN5G(38, 0), CHAN5G(40, 0),
91 CHAN5G(42, 0), CHAN5G(44, 0),
92 CHAN5G(46, 0), CHAN5G(48, 0),
93 CHAN5G(52, 0), CHAN5G(56, 0),
94 CHAN5G(60, 0), CHAN5G(64, 0),
95 CHAN5G(100, 0), CHAN5G(104, 0),
96 CHAN5G(108, 0), CHAN5G(112, 0),
97 CHAN5G(116, 0), CHAN5G(120, 0),
98 CHAN5G(124, 0), CHAN5G(128, 0),
99 CHAN5G(132, 0), CHAN5G(136, 0),
100 CHAN5G(140, 0), CHAN5G(149, 0),
101 CHAN5G(153, 0), CHAN5G(157, 0),
102 CHAN5G(161, 0), CHAN5G(165, 0),
103 CHAN5G(184, 0), CHAN5G(188, 0),
104 CHAN5G(192, 0), CHAN5G(196, 0),
105 CHAN5G(200, 0), CHAN5G(204, 0),
106 CHAN5G(208, 0), CHAN5G(212, 0),
107 CHAN5G(216, 0),
108};
109
110static struct ieee80211_supported_band ath6kl_band_2ghz = {
111 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
112 .channels = ath6kl_2ghz_channels,
113 .n_bitrates = ath6kl_g_rates_size,
114 .bitrates = ath6kl_g_rates,
115};
116
117static struct ieee80211_supported_band ath6kl_band_5ghz = {
118 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
119 .channels = ath6kl_5ghz_a_channels,
120 .n_bitrates = ath6kl_a_rates_size,
121 .bitrates = ath6kl_a_rates,
122};
123
Jouni Malinen837cb972011-10-11 17:31:57 +0300124#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
125
Kalle Valo10509f92011-12-13 14:52:07 +0200126/* returns true if scheduled scan was stopped */
127static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
128{
129 struct ath6kl *ar = vif->ar;
130
131 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
132 return false;
133
134 del_timer_sync(&vif->sched_scan_timer);
135
136 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
137 ATH6KL_HOST_MODE_AWAKE);
138
139 ar->state = ATH6KL_STATE_ON;
140
141 return true;
142}
143
144static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
145{
146 struct ath6kl *ar = vif->ar;
147 bool stopped;
148
149 stopped = __ath6kl_cfg80211_sscan_stop(vif);
150
151 if (!stopped)
152 return;
153
154 cfg80211_sched_scan_stopped(ar->wiphy);
155}
156
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530157static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300158 enum nl80211_wpa_versions wpa_version)
159{
160 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
161
162 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530163 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300164 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530165 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300166 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530167 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300168 } else {
169 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
170 return -ENOTSUPP;
171 }
172
173 return 0;
174}
175
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530176static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300177 enum nl80211_auth_type auth_type)
178{
Kalle Valobdcd8172011-07-18 00:22:30 +0300179 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
180
181 switch (auth_type) {
182 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530183 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300184 break;
185 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530186 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300187 break;
188 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530189 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300190 break;
191
192 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530193 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300194 break;
195
196 default:
Masanari Iida3c325fb2012-01-31 23:32:55 +0900197 ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300198 return -ENOTSUPP;
199 }
200
201 return 0;
202}
203
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530204static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300205{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530206 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
207 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
208 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300209
210 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
211 __func__, cipher, ucast);
212
213 switch (cipher) {
214 case 0:
215 /* our own hack to use value 0 as no crypto used */
216 *ar_cipher = NONE_CRYPT;
217 *ar_cipher_len = 0;
218 break;
219 case WLAN_CIPHER_SUITE_WEP40:
220 *ar_cipher = WEP_CRYPT;
221 *ar_cipher_len = 5;
222 break;
223 case WLAN_CIPHER_SUITE_WEP104:
224 *ar_cipher = WEP_CRYPT;
225 *ar_cipher_len = 13;
226 break;
227 case WLAN_CIPHER_SUITE_TKIP:
228 *ar_cipher = TKIP_CRYPT;
229 *ar_cipher_len = 0;
230 break;
231 case WLAN_CIPHER_SUITE_CCMP:
232 *ar_cipher = AES_CRYPT;
233 *ar_cipher_len = 0;
234 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200235 case WLAN_CIPHER_SUITE_SMS4:
236 *ar_cipher = WAPI_CRYPT;
237 *ar_cipher_len = 0;
238 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300239 default:
240 ath6kl_err("cipher 0x%x not supported\n", cipher);
241 return -ENOTSUPP;
242 }
243
244 return 0;
245}
246
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530247static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300248{
249 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
250
251 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530252 if (vif->auth_mode == WPA_AUTH)
253 vif->auth_mode = WPA_PSK_AUTH;
254 else if (vif->auth_mode == WPA2_AUTH)
255 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300256 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530257 if (vif->auth_mode == WPA_AUTH)
258 vif->auth_mode = WPA_AUTH_CCKM;
259 else if (vif->auth_mode == WPA2_AUTH)
260 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300261 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530262 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300263 }
264}
265
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530266static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300267{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530268 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530269
Kalle Valobdcd8172011-07-18 00:22:30 +0300270 if (!test_bit(WMI_READY, &ar->flag)) {
271 ath6kl_err("wmi is not ready\n");
272 return false;
273 }
274
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530275 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300276 ath6kl_err("wlan disabled\n");
277 return false;
278 }
279
280 return true;
281}
282
Kevin Fang6981ffd2011-10-07 08:51:19 +0800283static bool ath6kl_is_wpa_ie(const u8 *pos)
284{
285 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
286 pos[2] == 0x00 && pos[3] == 0x50 &&
287 pos[4] == 0xf2 && pos[5] == 0x01;
288}
289
290static bool ath6kl_is_rsn_ie(const u8 *pos)
291{
292 return pos[0] == WLAN_EID_RSN;
293}
294
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700295static bool ath6kl_is_wps_ie(const u8 *pos)
296{
297 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
298 pos[1] >= 4 &&
299 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
300 pos[5] == 0x04);
301}
302
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530303static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
304 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800305{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530306 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800307 const u8 *pos;
308 u8 *buf = NULL;
309 size_t len = 0;
310 int ret;
311
312 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700313 * Clear previously set flag
314 */
315
316 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
317
318 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800319 * Filter out RSN/WPA IE(s)
320 */
321
322 if (ies && ies_len) {
323 buf = kmalloc(ies_len, GFP_KERNEL);
324 if (buf == NULL)
325 return -ENOMEM;
326 pos = ies;
327
328 while (pos + 1 < ies + ies_len) {
329 if (pos + 2 + pos[1] > ies + ies_len)
330 break;
331 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
332 memcpy(buf + len, pos, 2 + pos[1]);
333 len += 2 + pos[1];
334 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700335
336 if (ath6kl_is_wps_ie(pos))
337 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
338
Kevin Fang6981ffd2011-10-07 08:51:19 +0800339 pos += 2 + pos[1];
340 }
341 }
342
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530343 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
344 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800345 kfree(buf);
346 return ret;
347}
348
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530349static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
350{
351 switch (type) {
352 case NL80211_IFTYPE_STATION:
353 *nw_type = INFRA_NETWORK;
354 break;
355 case NL80211_IFTYPE_ADHOC:
356 *nw_type = ADHOC_NETWORK;
357 break;
358 case NL80211_IFTYPE_AP:
359 *nw_type = AP_NETWORK;
360 break;
361 case NL80211_IFTYPE_P2P_CLIENT:
362 *nw_type = INFRA_NETWORK;
363 break;
364 case NL80211_IFTYPE_P2P_GO:
365 *nw_type = AP_NETWORK;
366 break;
367 default:
368 ath6kl_err("invalid interface type %u\n", type);
369 return -ENOTSUPP;
370 }
371
372 return 0;
373}
374
375static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
376 u8 *if_idx, u8 *nw_type)
377{
378 int i;
379
380 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
381 return false;
382
383 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
384 ar->num_vif))
385 return false;
386
387 if (type == NL80211_IFTYPE_STATION ||
388 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200389 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530390 if ((ar->avail_idx_map >> i) & BIT(0)) {
391 *if_idx = i;
392 return true;
393 }
394 }
395 }
396
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530397 if (type == NL80211_IFTYPE_P2P_CLIENT ||
398 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200399 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530400 if ((ar->avail_idx_map >> i) & BIT(0)) {
401 *if_idx = i;
402 return true;
403 }
404 }
405 }
406
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530407 return false;
408}
409
Kalle Valobdcd8172011-07-18 00:22:30 +0300410static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
411 struct cfg80211_connect_params *sme)
412{
413 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530414 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300415 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800416 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Kalle Valobdcd8172011-07-18 00:22:30 +0300417
Kalle Valo10509f92011-12-13 14:52:07 +0200418 ath6kl_cfg80211_sscan_disable(vif);
419
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530420 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300421
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530422 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300423 return -EIO;
424
425 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
426 ath6kl_err("destroy in progress\n");
427 return -EBUSY;
428 }
429
430 if (test_bit(SKIP_SCAN, &ar->flag) &&
431 ((sme->channel && sme->channel->center_freq == 0) ||
432 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
433 ath6kl_err("SkipScan: channel or bssid invalid\n");
434 return -EINVAL;
435 }
436
437 if (down_interruptible(&ar->sem)) {
438 ath6kl_err("busy, couldn't get access\n");
439 return -ERESTARTSYS;
440 }
441
442 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
443 ath6kl_err("busy, destroy in progress\n");
444 up(&ar->sem);
445 return -EBUSY;
446 }
447
448 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
449 /*
450 * sleep until the command queue drains
451 */
452 wait_event_interruptible_timeout(ar->event_wq,
453 ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
454 WMI_TIMEOUT);
455 if (signal_pending(current)) {
456 ath6kl_err("cmd queue drain timeout\n");
457 up(&ar->sem);
458 return -EINTR;
459 }
460 }
461
Jouni Malinen6e786cb2011-12-15 14:16:00 +0200462 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
463 if (status) {
464 up(&ar->sem);
465 return status;
466 }
467
468 if (sme->ie == NULL || sme->ie_len == 0)
Raja Mani542c5192011-11-15 14:14:56 +0530469 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800470
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530471 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530472 vif->ssid_len == sme->ssid_len &&
473 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530474 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530475 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
476 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530477 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300478
479 up(&ar->sem);
480 if (status) {
481 ath6kl_err("wmi_reconnect_cmd failed\n");
482 return -EIO;
483 }
484 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530485 } else if (vif->ssid_len == sme->ssid_len &&
486 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530487 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300488 }
489
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530490 memset(vif->ssid, 0, sizeof(vif->ssid));
491 vif->ssid_len = sme->ssid_len;
492 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300493
494 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530495 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300496
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530497 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300498 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530499 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300500
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530501 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300502
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530503 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300504 if (status) {
505 up(&ar->sem);
506 return status;
507 }
508
509 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530510 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300511 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530512 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300513
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530514 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300515
516 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530517 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300518
519 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530520 (vif->auth_mode == NONE_AUTH) &&
521 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300522 struct ath6kl_key *key = NULL;
523
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530524 if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300525 ath6kl_err("key index %d out of bounds\n",
526 sme->key_idx);
527 up(&ar->sem);
528 return -ENOENT;
529 }
530
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530531 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300532 key->key_len = sme->key_len;
533 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530534 key->cipher = vif->prwise_crypto;
535 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300536
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530537 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530538 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300539 GROUP_USAGE | TX_USAGE,
540 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200541 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300542 key->key, KEY_OP_INIT_VAL, NULL,
543 NO_SYNC_WMIFLAG);
544 }
545
546 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530547 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530548 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
549 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300550 ath6kl_err("couldn't set bss filtering\n");
551 up(&ar->sem);
552 return -EIO;
553 }
554 }
555
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530556 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300557
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800558 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
559 nw_subtype = SUBTYPE_P2PCLIENT;
560
Kalle Valobdcd8172011-07-18 00:22:30 +0300561 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
562 "%s: connect called with authmode %d dot11 auth %d"
563 " PW crypto %d PW crypto len %d GRP crypto %d"
564 " GRP crypto len %d channel hint %u\n",
565 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530566 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
567 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530568 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300569
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530570 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530571 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530572 vif->dot11_auth_mode, vif->auth_mode,
573 vif->prwise_crypto,
574 vif->prwise_crypto_len,
575 vif->grp_crypto, vif->grp_crypto_len,
576 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530577 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800578 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300579
580 up(&ar->sem);
581
582 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530583 memset(vif->ssid, 0, sizeof(vif->ssid));
584 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300585 ath6kl_err("invalid request\n");
586 return -ENOENT;
587 } else if (status) {
588 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
589 return -EIO;
590 }
591
592 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530593 ((vif->auth_mode == WPA_PSK_AUTH)
594 || (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530595 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300596 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
597 }
598
599 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530600 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300601
602 return 0;
603}
604
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530605static struct cfg80211_bss *
606ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
607 enum network_type nw_type,
608 const u8 *bssid,
609 struct ieee80211_channel *chan,
610 const u8 *beacon_ie,
611 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300612{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530613 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300614 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530615 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300616 u8 *ie;
617
Raja Mani4eab6f42011-11-09 17:02:23 +0530618 if (nw_type & ADHOC_NETWORK) {
619 cap_mask = WLAN_CAPABILITY_IBSS;
620 cap_val = WLAN_CAPABILITY_IBSS;
621 } else {
622 cap_mask = WLAN_CAPABILITY_ESS;
623 cap_val = WLAN_CAPABILITY_ESS;
624 }
625
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530626 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530627 vif->ssid, vif->ssid_len,
628 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300629 if (bss == NULL) {
630 /*
631 * Since cfg80211 may not yet know about the BSS,
632 * generate a partial entry until the first BSS info
633 * event becomes available.
634 *
635 * Prepend SSID element since it is not included in the Beacon
636 * IEs from the target.
637 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530638 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300639 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530640 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300641 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530642 ie[1] = vif->ssid_len;
643 memcpy(ie + 2, vif->ssid, vif->ssid_len);
644 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530645 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530646 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530647 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300648 0, GFP_KERNEL);
649 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530650 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
651 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300652 kfree(ie);
653 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530654 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300655
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530656 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300657}
658
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530659void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300660 u8 *bssid, u16 listen_intvl,
661 u16 beacon_intvl,
662 enum network_type nw_type,
663 u8 beacon_ie_len, u8 assoc_req_len,
664 u8 assoc_resp_len, u8 *assoc_info)
665{
Jouni Malinen01cac472011-09-19 19:14:59 +0300666 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530667 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530668 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300669
670 /* capinfo + listen interval */
671 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
672
673 /* capinfo + status code + associd */
674 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
675
676 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
677 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
678 assoc_resp_ie_offset;
679
680 assoc_req_len -= assoc_req_ie_offset;
681 assoc_resp_len -= assoc_resp_ie_offset;
682
Jouni Malinen32c10872011-09-19 19:15:07 +0300683 /*
684 * Store Beacon interval here; DTIM period will be available only once
685 * a Beacon frame from the AP is seen.
686 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530687 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530688 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300689
Kalle Valobdcd8172011-07-18 00:22:30 +0300690 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530691 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300692 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
693 "%s: ath6k not in ibss mode\n", __func__);
694 return;
695 }
696 }
697
698 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530699 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
700 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300701 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
702 "%s: ath6k not in station mode\n", __func__);
703 return;
704 }
705 }
706
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530707 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300708
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530709 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
710 assoc_info, beacon_ie_len);
711 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530712 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300713 return;
714 }
715
Raja Mani4eab6f42011-11-09 17:02:23 +0530716 if (nw_type & ADHOC_NETWORK) {
717 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
718 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
719 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530720 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300721 return;
722 }
723
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530724 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300725 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530726 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530727 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300728 assoc_req_ie, assoc_req_len,
729 assoc_resp_ie, assoc_resp_len,
730 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530731 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530732 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300733 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530734 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
735 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300736 }
737}
738
739static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
740 struct net_device *dev, u16 reason_code)
741{
Kalle Valod6d5c062011-11-25 13:17:37 +0200742 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530743 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300744
745 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
746 reason_code);
747
Kalle Valo10509f92011-12-13 14:52:07 +0200748 ath6kl_cfg80211_sscan_disable(vif);
749
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530750 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300751 return -EIO;
752
753 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
754 ath6kl_err("busy, destroy in progress\n");
755 return -EBUSY;
756 }
757
758 if (down_interruptible(&ar->sem)) {
759 ath6kl_err("busy, couldn't get access\n");
760 return -ERESTARTSYS;
761 }
762
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530763 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530764 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530765 memset(vif->ssid, 0, sizeof(vif->ssid));
766 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300767
768 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530769 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300770
771 up(&ar->sem);
772
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530773 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530774
Kalle Valobdcd8172011-07-18 00:22:30 +0300775 return 0;
776}
777
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530778void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300779 u8 *bssid, u8 assoc_resp_len,
780 u8 *assoc_info, u16 proto_reason)
781{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530782 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530783
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530784 if (vif->scan_req) {
785 cfg80211_scan_done(vif->scan_req, true);
786 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300787 }
788
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530789 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530790 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300791 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
792 "%s: ath6k not in ibss mode\n", __func__);
793 return;
794 }
795 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530796 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300797 return;
798 }
799
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530800 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530801 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
802 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300803 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
804 "%s: ath6k not in station mode\n", __func__);
805 return;
806 }
807 }
808
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530809 /*
810 * Send a disconnect command to target when a disconnect event is
811 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
812 * request from host) to make the firmware stop trying to connect even
813 * after giving disconnect event. There will be one more disconnect
814 * event for this disconnect command with reason code DISCONNECT_CMD
815 * which will be notified to cfg80211.
816 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300817
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530818 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530819 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300820 return;
821 }
822
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530823 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300824
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530825 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530826 cfg80211_connect_result(vif->ndev,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530827 bssid, NULL, 0,
828 NULL, 0,
829 WLAN_STATUS_UNSPECIFIED_FAILURE,
830 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530831 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530832 cfg80211_disconnected(vif->ndev, reason,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530833 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300834 }
835
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530836 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300837}
838
Kalle Valobdcd8172011-07-18 00:22:30 +0300839static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
840 struct cfg80211_scan_request *request)
841{
Kalle Valod6d5c062011-11-25 13:17:37 +0200842 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530843 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300844 s8 n_channels = 0;
845 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300846 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530847 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300848
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530849 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300850 return -EIO;
851
Kalle Valo10509f92011-12-13 14:52:07 +0200852 ath6kl_cfg80211_sscan_disable(vif);
853
Kalle Valobdcd8172011-07-18 00:22:30 +0300854 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530855 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300856 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530857 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530858 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300859 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
860 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300861 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300862 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300863 }
864 }
865
866 if (request->n_ssids && request->ssids[0].ssid_len) {
867 u8 i;
868
869 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
870 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
871
872 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530873 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
874 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300875 request->ssids[i].ssid_len,
876 request->ssids[i].ssid);
877 }
878
Kalle Valo10509f92011-12-13 14:52:07 +0200879 /*
880 * FIXME: we should clear the IE in fw if it's not set so just
881 * remove the check altogether
882 */
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300883 if (request->ie) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530884 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
885 WMI_FRAME_PROBE_REQ,
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300886 request->ie, request->ie_len);
887 if (ret) {
888 ath6kl_err("failed to set Probe Request appie for "
889 "scan");
890 return ret;
891 }
892 }
893
Jouni Malinen11869be2011-09-02 20:07:06 +0300894 /*
895 * Scan only the requested channels if the request specifies a set of
896 * channels. If the list is longer than the target supports, do not
897 * configure the list and instead, scan all available channels.
898 */
899 if (request->n_channels > 0 &&
900 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300901 u8 i;
902
Jouni Malinen11869be2011-09-02 20:07:06 +0300903 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300904
905 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
906 if (channels == NULL) {
907 ath6kl_warn("failed to set scan channels, "
908 "scan all channels");
909 n_channels = 0;
910 }
911
912 for (i = 0; i < n_channels; i++)
913 channels[i] = request->channels[i]->center_freq;
914 }
915
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530916 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530917 force_fg_scan = 1;
918
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800919 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
920 ar->fw_capabilities)) {
921 /*
922 * If capable of doing P2P mgmt operations using
923 * station interface, send additional information like
924 * supported rates to advertise and xmit rates for
925 * probe requests
926 */
927 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
928 WMI_LONG_SCAN, force_fg_scan,
929 false, 0, 0, n_channels,
930 channels, request->no_cck,
931 request->rates);
932 } else {
933 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
934 WMI_LONG_SCAN, force_fg_scan,
935 false, 0, 0, n_channels,
936 channels);
937 }
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300938 if (ret)
Kalle Valobdcd8172011-07-18 00:22:30 +0300939 ath6kl_err("wmi_startscan_cmd failed\n");
Jouni Malinen11869be2011-09-02 20:07:06 +0300940 else
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530941 vif->scan_req = request;
Kalle Valobdcd8172011-07-18 00:22:30 +0300942
Edward Lu1276c9e2011-08-30 21:58:00 +0300943 kfree(channels);
944
Kalle Valobdcd8172011-07-18 00:22:30 +0300945 return ret;
946}
947
Kalle Valo1c17d312011-11-01 08:43:56 +0200948void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300949{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530950 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300951 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300952
Kalle Valo1c17d312011-11-01 08:43:56 +0200953 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
954 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300955
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530956 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300957 return;
Kalle Valobdcd8172011-07-18 00:22:30 +0300958
Kalle Valo1c17d312011-11-01 08:43:56 +0200959 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300960 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300961
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530962 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
963 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530964 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
965 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300966 0, NULL);
967 }
968 }
969
970out:
Kalle Valocb938212011-10-27 18:47:46 +0300971 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530972 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300973}
974
975static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
976 u8 key_index, bool pairwise,
977 const u8 *mac_addr,
978 struct key_params *params)
979{
Kalle Valod6d5c062011-11-25 13:17:37 +0200980 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530981 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300982 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +0530983 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300984 u8 key_usage;
985 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +0300986
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530987 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300988 return -EIO;
989
Jouni Malinen837cb972011-10-11 17:31:57 +0300990 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
991 if (params->key_len != WMI_KRK_LEN)
992 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530993 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
994 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +0300995 }
996
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530997 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300998 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
999 "%s: key index %d out of bounds\n", __func__,
1000 key_index);
1001 return -ENOENT;
1002 }
1003
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301004 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001005 memset(key, 0, sizeof(struct ath6kl_key));
1006
1007 if (pairwise)
1008 key_usage = PAIRWISE_USAGE;
1009 else
1010 key_usage = GROUP_USAGE;
1011
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301012 seq_len = params->seq_len;
1013 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1014 seq_len > ATH6KL_KEY_SEQ_LEN) {
1015 /* Only first half of the WPI PN is configured */
1016 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001017 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301018 if (params->key_len > WLAN_MAX_KEY_LEN ||
1019 seq_len > sizeof(key->seq))
1020 return -EINVAL;
1021
1022 key->key_len = params->key_len;
1023 memcpy(key->key, params->key, key->key_len);
1024 key->seq_len = seq_len;
1025 memcpy(key->seq, params->seq, key->seq_len);
1026 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001027
1028 switch (key->cipher) {
1029 case WLAN_CIPHER_SUITE_WEP40:
1030 case WLAN_CIPHER_SUITE_WEP104:
1031 key_type = WEP_CRYPT;
1032 break;
1033
1034 case WLAN_CIPHER_SUITE_TKIP:
1035 key_type = TKIP_CRYPT;
1036 break;
1037
1038 case WLAN_CIPHER_SUITE_CCMP:
1039 key_type = AES_CRYPT;
1040 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001041 case WLAN_CIPHER_SUITE_SMS4:
1042 key_type = WAPI_CRYPT;
1043 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001044
1045 default:
1046 return -ENOTSUPP;
1047 }
1048
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301049 if (((vif->auth_mode == WPA_PSK_AUTH)
1050 || (vif->auth_mode == WPA2_PSK_AUTH))
Kalle Valobdcd8172011-07-18 00:22:30 +03001051 && (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301052 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001053
1054 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1055 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1056 __func__, key_index, key->key_len, key_type,
1057 key_usage, key->seq_len);
1058
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301059 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001060 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
1061 key_type == WAPI_CRYPT) && params) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001062 ar->ap_mode_bkey.valid = true;
1063 ar->ap_mode_bkey.key_index = key_index;
1064 ar->ap_mode_bkey.key_type = key_type;
1065 ar->ap_mode_bkey.key_len = key->key_len;
1066 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301067 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001068 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1069 "key configuration until AP mode has been "
1070 "started\n");
1071 /*
1072 * The key will be set in ath6kl_connect_ap_mode() once
1073 * the connected event is received from the target.
1074 */
1075 return 0;
1076 }
1077 }
1078
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301079 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301080 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001081 /*
1082 * Store the key locally so that it can be re-configured after
1083 * the AP mode has properly started
1084 * (ath6kl_install_statioc_wep_keys).
1085 */
1086 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1087 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301088 vif->wep_key_list[key_index].key_len = key->key_len;
1089 memcpy(vif->wep_key_list[key_index].key, key->key,
1090 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001091 return 0;
1092 }
1093
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301094 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001095 key_type, key_usage, key->key_len,
1096 key->seq, key->seq_len, key->key,
1097 KEY_OP_INIT_VAL,
1098 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001099}
1100
1101static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1102 u8 key_index, bool pairwise,
1103 const u8 *mac_addr)
1104{
Kalle Valod6d5c062011-11-25 13:17:37 +02001105 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301106 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001107
1108 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1109
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301110 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001111 return -EIO;
1112
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301113 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001114 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1115 "%s: key index %d out of bounds\n", __func__,
1116 key_index);
1117 return -ENOENT;
1118 }
1119
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301120 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001121 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1122 "%s: index %d is empty\n", __func__, key_index);
1123 return 0;
1124 }
1125
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301126 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001127
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301128 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001129}
1130
1131static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1132 u8 key_index, bool pairwise,
1133 const u8 *mac_addr, void *cookie,
1134 void (*callback) (void *cookie,
1135 struct key_params *))
1136{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301137 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001138 struct ath6kl_key *key = NULL;
1139 struct key_params params;
1140
1141 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1142
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301143 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001144 return -EIO;
1145
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301146 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001147 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1148 "%s: key index %d out of bounds\n", __func__,
1149 key_index);
1150 return -ENOENT;
1151 }
1152
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301153 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001154 memset(&params, 0, sizeof(params));
1155 params.cipher = key->cipher;
1156 params.key_len = key->key_len;
1157 params.seq_len = key->seq_len;
1158 params.seq = key->seq;
1159 params.key = key->key;
1160
1161 callback(cookie, &params);
1162
1163 return key->key_len ? 0 : -ENOENT;
1164}
1165
1166static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1167 struct net_device *ndev,
1168 u8 key_index, bool unicast,
1169 bool multicast)
1170{
Kalle Valod6d5c062011-11-25 13:17:37 +02001171 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301172 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001173 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001174 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001175 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001176
1177 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1178
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301179 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001180 return -EIO;
1181
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301182 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001183 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1184 "%s: key index %d out of bounds\n",
1185 __func__, key_index);
1186 return -ENOENT;
1187 }
1188
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301189 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001190 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1191 __func__, key_index);
1192 return -EINVAL;
1193 }
1194
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301195 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301196 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001197 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301198 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001199 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001200 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301201 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001202 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301203 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001204
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301205 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001206 return 0; /* Delay until AP mode has been started */
1207
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001208 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1209 vif->def_txkey_index,
1210 key_type, key_usage,
1211 key->key_len, key->seq, key->seq_len,
1212 key->key,
1213 KEY_OP_INIT_VAL, NULL,
1214 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001215}
1216
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301217void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001218 bool ismcast)
1219{
1220 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1221 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1222
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301223 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001224 (ismcast ? NL80211_KEYTYPE_GROUP :
1225 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1226 GFP_KERNEL);
1227}
1228
1229static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1230{
1231 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301232 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001233 int ret;
1234
1235 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1236 changed);
1237
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301238 vif = ath6kl_vif_first(ar);
1239 if (!vif)
1240 return -EIO;
1241
1242 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001243 return -EIO;
1244
1245 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1246 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1247 if (ret != 0) {
1248 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1249 return -EIO;
1250 }
1251 }
1252
1253 return 0;
1254}
1255
1256/*
1257 * The type nl80211_tx_power_setting replaces the following
1258 * data type from 2.6.36 onwards
1259*/
1260static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1261 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001262 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001263{
1264 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301265 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001266 u8 ath6kl_dbm;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001267 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001268
1269 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1270 type, dbm);
1271
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301272 vif = ath6kl_vif_first(ar);
1273 if (!vif)
1274 return -EIO;
1275
1276 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001277 return -EIO;
1278
1279 switch (type) {
1280 case NL80211_TX_POWER_AUTOMATIC:
1281 return 0;
1282 case NL80211_TX_POWER_LIMITED:
1283 ar->tx_pwr = ath6kl_dbm = dbm;
1284 break;
1285 default:
1286 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1287 __func__, type);
1288 return -EOPNOTSUPP;
1289 }
1290
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301291 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001292
1293 return 0;
1294}
1295
1296static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1297{
1298 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301299 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001300
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301301 vif = ath6kl_vif_first(ar);
1302 if (!vif)
1303 return -EIO;
1304
1305 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001306 return -EIO;
1307
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301308 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001309 ar->tx_pwr = 0;
1310
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301311 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001312 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1313 return -EIO;
1314 }
1315
1316 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1317 5 * HZ);
1318
1319 if (signal_pending(current)) {
1320 ath6kl_err("target did not respond\n");
1321 return -EINTR;
1322 }
1323 }
1324
1325 *dbm = ar->tx_pwr;
1326 return 0;
1327}
1328
1329static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1330 struct net_device *dev,
1331 bool pmgmt, int timeout)
1332{
1333 struct ath6kl *ar = ath6kl_priv(dev);
1334 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301335 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001336
1337 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1338 __func__, pmgmt, timeout);
1339
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301340 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001341 return -EIO;
1342
1343 if (pmgmt) {
1344 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1345 mode.pwr_mode = REC_POWER;
1346 } else {
1347 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1348 mode.pwr_mode = MAX_PERF_POWER;
1349 }
1350
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301351 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
1352 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001353 ath6kl_err("wmi_powermode_cmd failed\n");
1354 return -EIO;
1355 }
1356
1357 return 0;
1358}
1359
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301360static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1361 char *name,
1362 enum nl80211_iftype type,
1363 u32 *flags,
1364 struct vif_params *params)
1365{
1366 struct ath6kl *ar = wiphy_priv(wiphy);
1367 struct net_device *ndev;
1368 u8 if_idx, nw_type;
1369
Kalle Valo71f96ee2011-11-14 19:31:30 +02001370 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301371 ath6kl_err("Reached maximum number of supported vif\n");
1372 return ERR_PTR(-EINVAL);
1373 }
1374
1375 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1376 ath6kl_err("Not a supported interface type\n");
1377 return ERR_PTR(-EINVAL);
1378 }
1379
1380 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1381 if (!ndev)
1382 return ERR_PTR(-ENOMEM);
1383
1384 ar->num_vif++;
1385
1386 return ndev;
1387}
1388
1389static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1390 struct net_device *ndev)
1391{
1392 struct ath6kl *ar = wiphy_priv(wiphy);
1393 struct ath6kl_vif *vif = netdev_priv(ndev);
1394
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301395 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301396 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301397 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301398
1399 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1400
Kalle Valoc25889e2012-01-17 20:08:27 +02001401 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301402
1403 return 0;
1404}
1405
Kalle Valobdcd8172011-07-18 00:22:30 +03001406static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1407 struct net_device *ndev,
1408 enum nl80211_iftype type, u32 *flags,
1409 struct vif_params *params)
1410{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301411 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001412
1413 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1414
Kalle Valobdcd8172011-07-18 00:22:30 +03001415 switch (type) {
1416 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301417 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001418 break;
1419 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301420 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001421 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001422 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301423 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001424 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001425 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301426 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001427 break;
1428 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301429 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001430 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001431 default:
1432 ath6kl_err("invalid interface type %u\n", type);
1433 return -EOPNOTSUPP;
1434 }
1435
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301436 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001437
1438 return 0;
1439}
1440
1441static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1442 struct net_device *dev,
1443 struct cfg80211_ibss_params *ibss_param)
1444{
1445 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301446 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001447 int status;
1448
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301449 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001450 return -EIO;
1451
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301452 vif->ssid_len = ibss_param->ssid_len;
1453 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001454
1455 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301456 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001457
1458 if (ibss_param->channel_fixed) {
1459 /*
1460 * TODO: channel_fixed: The channel should be fixed, do not
1461 * search for IBSSs to join on other channels. Target
1462 * firmware does not support this feature, needs to be
1463 * updated.
1464 */
1465 return -EOPNOTSUPP;
1466 }
1467
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301468 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001469 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301470 memcpy(vif->req_bssid, ibss_param->bssid,
1471 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001472
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301473 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001474
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301475 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001476 if (status)
1477 return status;
1478
1479 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301480 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1481 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001482 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301483 ath6kl_set_cipher(vif, 0, true);
1484 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001485 }
1486
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301487 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001488
1489 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1490 "%s: connect called with authmode %d dot11 auth %d"
1491 " PW crypto %d PW crypto len %d GRP crypto %d"
1492 " GRP crypto len %d channel hint %u\n",
1493 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301494 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1495 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301496 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001497
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301498 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301499 vif->dot11_auth_mode, vif->auth_mode,
1500 vif->prwise_crypto,
1501 vif->prwise_crypto_len,
1502 vif->grp_crypto, vif->grp_crypto_len,
1503 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301504 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001505 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301506 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001507
1508 return 0;
1509}
1510
1511static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1512 struct net_device *dev)
1513{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301514 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001515
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301516 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001517 return -EIO;
1518
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301519 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301520 memset(vif->ssid, 0, sizeof(vif->ssid));
1521 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001522
1523 return 0;
1524}
1525
1526static const u32 cipher_suites[] = {
1527 WLAN_CIPHER_SUITE_WEP40,
1528 WLAN_CIPHER_SUITE_WEP104,
1529 WLAN_CIPHER_SUITE_TKIP,
1530 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001531 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001532 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001533};
1534
1535static bool is_rate_legacy(s32 rate)
1536{
1537 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1538 6000, 9000, 12000, 18000, 24000,
1539 36000, 48000, 54000
1540 };
1541 u8 i;
1542
1543 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1544 if (rate == legacy[i])
1545 return true;
1546
1547 return false;
1548}
1549
1550static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1551{
1552 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1553 52000, 58500, 65000, 72200
1554 };
1555 u8 i;
1556
1557 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1558 if (rate == ht20[i]) {
1559 if (i == ARRAY_SIZE(ht20) - 1)
1560 /* last rate uses sgi */
1561 *sgi = true;
1562 else
1563 *sgi = false;
1564
1565 *mcs = i;
1566 return true;
1567 }
1568 }
1569 return false;
1570}
1571
1572static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1573{
1574 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1575 81000, 108000, 121500, 135000,
1576 150000
1577 };
1578 u8 i;
1579
1580 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1581 if (rate == ht40[i]) {
1582 if (i == ARRAY_SIZE(ht40) - 1)
1583 /* last rate uses sgi */
1584 *sgi = true;
1585 else
1586 *sgi = false;
1587
1588 *mcs = i;
1589 return true;
1590 }
1591 }
1592
1593 return false;
1594}
1595
1596static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1597 u8 *mac, struct station_info *sinfo)
1598{
1599 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301600 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001601 long left;
1602 bool sgi;
1603 s32 rate;
1604 int ret;
1605 u8 mcs;
1606
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301607 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001608 return -ENOENT;
1609
1610 if (down_interruptible(&ar->sem))
1611 return -EBUSY;
1612
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301613 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001614
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301615 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001616
1617 if (ret != 0) {
1618 up(&ar->sem);
1619 return -EIO;
1620 }
1621
1622 left = wait_event_interruptible_timeout(ar->event_wq,
1623 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301624 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001625 WMI_TIMEOUT);
1626
1627 up(&ar->sem);
1628
1629 if (left == 0)
1630 return -ETIMEDOUT;
1631 else if (left < 0)
1632 return left;
1633
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301634 if (vif->target_stats.rx_byte) {
1635 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001636 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301637 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001638 sinfo->filled |= STATION_INFO_RX_PACKETS;
1639 }
1640
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301641 if (vif->target_stats.tx_byte) {
1642 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001643 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301644 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001645 sinfo->filled |= STATION_INFO_TX_PACKETS;
1646 }
1647
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301648 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001649 sinfo->filled |= STATION_INFO_SIGNAL;
1650
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301651 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001652
1653 if (is_rate_legacy(rate)) {
1654 sinfo->txrate.legacy = rate / 100;
1655 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1656 if (sgi) {
1657 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1658 sinfo->txrate.mcs = mcs - 1;
1659 } else {
1660 sinfo->txrate.mcs = mcs;
1661 }
1662
1663 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1664 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1665 if (sgi) {
1666 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1667 sinfo->txrate.mcs = mcs - 1;
1668 } else {
1669 sinfo->txrate.mcs = mcs;
1670 }
1671
1672 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1673 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1674 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001675 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1676 "invalid rate from stats: %d\n", rate);
1677 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001678 return 0;
1679 }
1680
1681 sinfo->filled |= STATION_INFO_TX_BITRATE;
1682
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301683 if (test_bit(CONNECTED, &vif->flags) &&
1684 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301685 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001686 sinfo->filled |= STATION_INFO_BSS_PARAM;
1687 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301688 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1689 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001690 }
1691
Kalle Valobdcd8172011-07-18 00:22:30 +03001692 return 0;
1693}
1694
1695static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1696 struct cfg80211_pmksa *pmksa)
1697{
1698 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301699 struct ath6kl_vif *vif = netdev_priv(netdev);
1700
1701 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001702 pmksa->pmkid, true);
1703}
1704
1705static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1706 struct cfg80211_pmksa *pmksa)
1707{
1708 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301709 struct ath6kl_vif *vif = netdev_priv(netdev);
1710
1711 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001712 pmksa->pmkid, false);
1713}
1714
1715static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1716{
1717 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301718 struct ath6kl_vif *vif = netdev_priv(netdev);
1719
1720 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301721 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1722 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001723 return 0;
1724}
1725
Raja Manid91e8ee2012-01-30 17:13:10 +05301726static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1727 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001728{
Raja Manid91e8ee2012-01-30 17:13:10 +05301729 int ret, pos;
1730 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001731 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001732
Raja Manid91e8ee2012-01-30 17:13:10 +05301733 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001734 for (i = 0; i < wow->n_patterns; i++) {
1735
1736 /*
1737 * Convert given nl80211 specific mask value to equivalent
1738 * driver specific mask value and send it to the chip along
1739 * with patterns. For example, If the mask value defined in
1740 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1741 * then equivalent driver specific mask value is
1742 * "0xFF 0x00 0xFF 0x00".
1743 */
1744 memset(&mask, 0, sizeof(mask));
1745 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1746 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1747 mask[pos] = 0xFF;
1748 }
1749 /*
1750 * Note: Pattern's offset is not passed as part of wowlan
1751 * parameter from CFG layer. So it's always passed as ZERO
1752 * to the firmware. It means, given WOW patterns are always
1753 * matched from the first byte of received pkt in the firmware.
1754 */
1755 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301756 vif->fw_vif_idx, WOW_LIST_ID,
1757 wow->patterns[i].pattern_len,
1758 0 /* pattern offset */,
1759 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001760 if (ret)
1761 return ret;
1762 }
1763
Raja Manid91e8ee2012-01-30 17:13:10 +05301764 if (wow->disconnect)
1765 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1766
1767 if (wow->magic_pkt)
1768 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1769
1770 if (wow->gtk_rekey_failure)
1771 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1772
1773 if (wow->eap_identity_req)
1774 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1775
1776 if (wow->four_way_handshake)
1777 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1778
1779 return 0;
1780}
1781
1782static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1783{
1784 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1785 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1786 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1787 0x00, 0x08 };
1788 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1789 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1790 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1791 0x00, 0x7f };
1792 u8 unicst_offset = 0;
1793 static const u8 arp_pattern[] = { 0x08, 0x06 };
1794 static const u8 arp_mask[] = { 0xff, 0xff };
1795 u8 arp_offset = 20;
1796 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1797 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1798 u8 discvr_offset = 38;
1799 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1800 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1801 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1802 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1803 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1804 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1805 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1806 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1807 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1808 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1809 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1810 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1811 u8 dhcp_offset = 0;
1812 int ret;
1813
1814 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1815 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1816 vif->fw_vif_idx, WOW_LIST_ID,
1817 sizeof(unicst_pattern), unicst_offset,
1818 unicst_pattern, unicst_mask);
1819 if (ret) {
1820 ath6kl_err("failed to add WOW unicast IP pattern\n");
1821 return ret;
1822 }
1823
1824 /* Setup all ARP pkt pattern */
1825 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1826 vif->fw_vif_idx, WOW_LIST_ID,
1827 sizeof(arp_pattern), arp_offset,
1828 arp_pattern, arp_mask);
1829 if (ret) {
1830 ath6kl_err("failed to add WOW ARP pattern\n");
1831 return ret;
1832 }
1833
1834 /*
1835 * Setup multicast pattern for mDNS 224.0.0.251,
1836 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1837 */
1838 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1839 vif->fw_vif_idx, WOW_LIST_ID,
1840 sizeof(discvr_pattern), discvr_offset,
1841 discvr_pattern, discvr_mask);
1842 if (ret) {
1843 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1844 return ret;
1845 }
1846
1847 /* Setup all DHCP broadcast pkt pattern */
1848 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1849 vif->fw_vif_idx, WOW_LIST_ID,
1850 sizeof(dhcp_pattern), dhcp_offset,
1851 dhcp_pattern, dhcp_mask);
1852 if (ret) {
1853 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
1854 return ret;
1855 }
1856
1857 return 0;
1858}
1859
1860static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
1861{
1862 struct net_device *ndev = vif->ndev;
1863 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1864 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1865 u8 discvr_offset = 38;
1866 u8 mac_mask[ETH_ALEN];
1867 int ret;
1868
1869 /* Setup unicast pkt pattern */
1870 memset(mac_mask, 0xff, ETH_ALEN);
1871 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1872 vif->fw_vif_idx, WOW_LIST_ID,
1873 ETH_ALEN, 0, ndev->dev_addr,
1874 mac_mask);
1875 if (ret) {
1876 ath6kl_err("failed to add WOW unicast pattern\n");
1877 return ret;
1878 }
1879
1880 /*
1881 * Setup multicast pattern for mDNS 224.0.0.251,
1882 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1883 */
1884 if ((ndev->flags & IFF_ALLMULTI) ||
1885 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
1886 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1887 vif->fw_vif_idx, WOW_LIST_ID,
1888 sizeof(discvr_pattern), discvr_offset,
1889 discvr_pattern, discvr_mask);
1890 if (ret) {
1891 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
1892 "pattern\n");
1893 return ret;
1894 }
1895 }
1896
1897 return 0;
1898}
1899
1900static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1901{
1902 struct in_device *in_dev;
1903 struct in_ifaddr *ifa;
1904 struct ath6kl_vif *vif;
1905 int ret, left;
1906 u32 filter = 0;
1907 u16 i;
1908 u8 index = 0;
1909 __be32 ips[MAX_IP_ADDRS];
1910
1911 vif = ath6kl_vif_first(ar);
1912 if (!vif)
1913 return -EIO;
1914
1915 if (!ath6kl_cfg80211_ready(vif))
1916 return -EIO;
1917
1918 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05301919 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05301920
1921 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
1922 return -EINVAL;
1923
1924 /* Clear existing WOW patterns */
1925 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
1926 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
1927 WOW_LIST_ID, i);
1928
1929 /*
1930 * Skip the default WOW pattern configuration
1931 * if the driver receives any WOW patterns from
1932 * the user.
1933 */
1934 if (wow)
1935 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
1936 else if (vif->nw_type == AP_NETWORK)
1937 ret = ath6kl_wow_ap(ar, vif);
1938 else
1939 ret = ath6kl_wow_sta(ar, vif);
1940
1941 if (ret)
1942 return ret;
1943
Raja Manic08631c2011-12-16 14:24:24 +05301944 /* Setup own IP addr for ARP agent. */
1945 in_dev = __in_dev_get_rtnl(vif->ndev);
1946 if (!in_dev)
1947 goto skip_arp;
1948
1949 ifa = in_dev->ifa_list;
1950 memset(&ips, 0, sizeof(ips));
1951
1952 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
1953 while (index < MAX_IP_ADDRS && ifa) {
1954 ips[index] = ifa->ifa_local;
1955 ifa = ifa->ifa_next;
1956 index++;
1957 }
1958
1959 if (ifa) {
1960 ath6kl_err("total IP addr count is exceeding fw limit\n");
1961 return -EINVAL;
1962 }
1963
1964 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
1965 if (ret) {
1966 ath6kl_err("fail to setup ip for arp agent\n");
1967 return ret;
1968 }
1969
1970skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02001971 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
1972 ATH6KL_WOW_MODE_ENABLE,
1973 filter,
1974 WOW_HOST_REQ_DELAY);
1975 if (ret)
1976 return ret;
1977
Raja Mani081c7a82012-01-30 17:13:11 +05301978 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
1979
Raja Mani6cb3c712011-11-07 22:52:45 +02001980 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1981 ATH6KL_HOST_MODE_ASLEEP);
1982 if (ret)
1983 return ret;
1984
Raja Mani081c7a82012-01-30 17:13:11 +05301985 left = wait_event_interruptible_timeout(ar->event_wq,
1986 test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags),
1987 WMI_TIMEOUT);
1988 if (left == 0) {
1989 ath6kl_warn("timeout, didn't get host sleep cmd "
1990 "processed event\n");
1991 ret = -ETIMEDOUT;
1992 } else if (left < 0) {
1993 ath6kl_warn("error while waiting for host sleep cmd "
1994 "processed event %d\n", left);
1995 ret = left;
1996 }
1997
Raja Mani6cb3c712011-11-07 22:52:45 +02001998 if (ar->tx_pending[ar->ctrl_ep]) {
1999 left = wait_event_interruptible_timeout(ar->event_wq,
2000 ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
2001 if (left == 0) {
2002 ath6kl_warn("clear wmi ctrl data timeout\n");
2003 ret = -ETIMEDOUT;
2004 } else if (left < 0) {
2005 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
2006 ret = left;
2007 }
2008 }
2009
2010 return ret;
2011}
2012
2013static int ath6kl_wow_resume(struct ath6kl *ar)
2014{
2015 struct ath6kl_vif *vif;
2016 int ret;
2017
2018 vif = ath6kl_vif_first(ar);
2019 if (!vif)
2020 return -EIO;
2021
2022 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2023 ATH6KL_HOST_MODE_AWAKE);
2024 return ret;
2025}
2026
Kalle Valo52d81a62011-11-01 08:44:21 +02002027int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002028 enum ath6kl_cfg_suspend_mode mode,
2029 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002030{
2031 int ret;
2032
Kalle Valo52d81a62011-11-01 08:44:21 +02002033 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002034 case ATH6KL_CFG_SUSPEND_WOW:
2035
2036 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2037
2038 /* Flush all non control pkts in TX path */
2039 ath6kl_tx_data_cleanup(ar);
2040
2041 ret = ath6kl_wow_suspend(ar, wow);
2042 if (ret) {
2043 ath6kl_err("wow suspend failed: %d\n", ret);
2044 return ret;
2045 }
2046 ar->state = ATH6KL_STATE_WOW;
2047 break;
2048
Kalle Valo52d81a62011-11-01 08:44:21 +02002049 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002050
Kalle Valo7125f012011-12-13 14:51:37 +02002051 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002052
Kalle Valo52d81a62011-11-01 08:44:21 +02002053 /* save the current power mode before enabling power save */
2054 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2055
2056 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2057 if (ret) {
2058 ath6kl_warn("wmi powermode command failed during suspend: %d\n",
2059 ret);
2060 }
2061
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002062 ar->state = ATH6KL_STATE_DEEPSLEEP;
2063
Kalle Valo52d81a62011-11-01 08:44:21 +02002064 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002065
2066 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002067
Kalle Valo7125f012011-12-13 14:51:37 +02002068 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002069
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002070 if (ar->state == ATH6KL_STATE_OFF) {
2071 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2072 "suspend hw off, no action for cutpower\n");
2073 break;
2074 }
2075
2076 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2077
2078 ret = ath6kl_init_hw_stop(ar);
2079 if (ret) {
2080 ath6kl_warn("failed to stop hw during suspend: %d\n",
2081 ret);
2082 }
2083
2084 ar->state = ATH6KL_STATE_CUTPOWER;
2085
2086 break;
2087
Kalle Valo10509f92011-12-13 14:52:07 +02002088 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2089 /*
2090 * Nothing needed for schedule scan, firmware is already in
2091 * wow mode and sleeping most of the time.
2092 */
2093 break;
2094
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002095 default:
2096 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002097 }
2098
2099 return 0;
2100}
Kalle Valod6a434d2012-01-17 20:09:36 +02002101EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002102
2103int ath6kl_cfg80211_resume(struct ath6kl *ar)
2104{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002105 int ret;
2106
2107 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002108 case ATH6KL_STATE_WOW:
2109 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2110
2111 ret = ath6kl_wow_resume(ar);
2112 if (ret) {
2113 ath6kl_warn("wow mode resume failed: %d\n", ret);
2114 return ret;
2115 }
2116
2117 ar->state = ATH6KL_STATE_ON;
2118 break;
2119
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002120 case ATH6KL_STATE_DEEPSLEEP:
2121 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2122 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2123 ar->wmi->saved_pwr_mode);
2124 if (ret) {
2125 ath6kl_warn("wmi powermode command failed during resume: %d\n",
2126 ret);
2127 }
2128 }
2129
2130 ar->state = ATH6KL_STATE_ON;
2131
2132 break;
2133
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002134 case ATH6KL_STATE_CUTPOWER:
2135 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2136
2137 ret = ath6kl_init_hw_start(ar);
2138 if (ret) {
2139 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2140 return ret;
2141 }
Raja Manid7c44e02011-11-07 22:52:46 +02002142 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002143
Kalle Valo10509f92011-12-13 14:52:07 +02002144 case ATH6KL_STATE_SCHED_SCAN:
2145 break;
2146
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002147 default:
2148 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002149 }
2150
2151 return 0;
2152}
Kalle Valod6a434d2012-01-17 20:09:36 +02002153EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002154
Kalle Valoabcb3442011-07-22 08:26:20 +03002155#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002156
2157/* hif layer decides what suspend mode to use */
2158static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002159 struct cfg80211_wowlan *wow)
2160{
2161 struct ath6kl *ar = wiphy_priv(wiphy);
2162
Raja Mani0f60e9f2011-11-07 22:52:45 +02002163 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002164}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002165
Kalle Valo52d81a62011-11-01 08:44:21 +02002166static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002167{
2168 struct ath6kl *ar = wiphy_priv(wiphy);
2169
2170 return ath6kl_hif_resume(ar);
2171}
Raja Mania918fb32011-11-07 22:52:46 +02002172
2173/*
2174 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2175 * both sdio irq wake up and keep power. The target pulls sdio data line to
2176 * wake up the host when WOW pattern matches. This causes sdio irq handler
2177 * is being called in the host side which internally hits ath6kl's RX path.
2178 *
2179 * Since sdio interrupt is not disabled, RX path executes even before
2180 * the host executes the actual resume operation from PM module.
2181 *
2182 * In the current scenario, WOW resume should happen before start processing
2183 * any data from the target. So It's required to perform WOW resume in RX path.
2184 * Ideally we should perform WOW resume only in the actual platform
2185 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2186 *
2187 * ath6kl_check_wow_status() is called from ath6kl_rx().
2188 */
2189void ath6kl_check_wow_status(struct ath6kl *ar)
2190{
2191 if (ar->state == ATH6KL_STATE_WOW)
2192 ath6kl_cfg80211_resume(ar);
2193}
2194
2195#else
2196
2197void ath6kl_check_wow_status(struct ath6kl *ar)
2198{
2199}
Kalle Valoabcb3442011-07-22 08:26:20 +03002200#endif
2201
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002202static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2203 struct ieee80211_channel *chan,
2204 enum nl80211_channel_type channel_type)
2205{
Sujith Manoharane68f6752011-12-22 12:15:27 +05302206 struct ath6kl_vif *vif;
2207
2208 /*
2209 * 'dev' could be NULL if a channel change is required for the hardware
2210 * device itself, instead of a particular VIF.
2211 *
2212 * FIXME: To be handled properly when monitor mode is supported.
2213 */
2214 if (!dev)
2215 return -EBUSY;
2216
2217 vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002218
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302219 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002220 return -EIO;
2221
2222 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2223 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302224 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002225
2226 return 0;
2227}
2228
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002229static bool ath6kl_is_p2p_ie(const u8 *pos)
2230{
2231 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2232 pos[2] == 0x50 && pos[3] == 0x6f &&
2233 pos[4] == 0x9a && pos[5] == 0x09;
2234}
2235
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302236static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2237 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002238{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302239 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002240 const u8 *pos;
2241 u8 *buf = NULL;
2242 size_t len = 0;
2243 int ret;
2244
2245 /*
2246 * Filter out P2P IE(s) since they will be included depending on
2247 * the Probe Request frame in ath6kl_send_go_probe_resp().
2248 */
2249
2250 if (ies && ies_len) {
2251 buf = kmalloc(ies_len, GFP_KERNEL);
2252 if (buf == NULL)
2253 return -ENOMEM;
2254 pos = ies;
2255 while (pos + 1 < ies + ies_len) {
2256 if (pos + 2 + pos[1] > ies + ies_len)
2257 break;
2258 if (!ath6kl_is_p2p_ie(pos)) {
2259 memcpy(buf + len, pos, 2 + pos[1]);
2260 len += 2 + pos[1];
2261 }
2262 pos += 2 + pos[1];
2263 }
2264 }
2265
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302266 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2267 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002268 kfree(buf);
2269 return ret;
2270}
2271
Johannes Berg88600202012-02-13 15:17:18 +01002272static int ath6kl_set_ies(struct ath6kl_vif *vif,
2273 struct cfg80211_beacon_data *info)
2274{
2275 struct ath6kl *ar = vif->ar;
2276 int res;
2277
2278 if (info->beacon_ies) {
2279 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2280 WMI_FRAME_BEACON,
2281 info->beacon_ies,
2282 info->beacon_ies_len);
2283 if (res)
2284 return res;
2285 }
2286
2287 if (info->proberesp_ies) {
2288 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2289 info->proberesp_ies_len);
2290 if (res)
2291 return res;
2292 }
2293
2294 if (info->assocresp_ies) {
2295 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2296 WMI_FRAME_ASSOC_RESP,
2297 info->assocresp_ies,
2298 info->assocresp_ies_len);
2299 if (res)
2300 return res;
2301 }
2302
2303 return 0;
2304}
2305
2306static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2307 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002308{
2309 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302310 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002311 struct ieee80211_mgmt *mgmt;
2312 u8 *ies;
2313 int ies_len;
2314 struct wmi_connect_cmd p;
2315 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302316 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002317
Johannes Berg88600202012-02-13 15:17:18 +01002318 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002319
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302320 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002321 return -EIO;
2322
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302323 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002324 return -EOPNOTSUPP;
2325
Johannes Berg88600202012-02-13 15:17:18 +01002326 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002327
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002328 ar->ap_mode_bkey.valid = false;
2329
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002330 /* TODO:
2331 * info->interval
2332 * info->dtim_period
2333 */
2334
Johannes Berg88600202012-02-13 15:17:18 +01002335 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002336 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002337 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002338 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002339 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002340 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002341 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002342
2343 if (info->ssid == NULL)
2344 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302345 memcpy(vif->ssid, info->ssid, info->ssid_len);
2346 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002347 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
2348 return -EOPNOTSUPP; /* TODO */
2349
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302350 ret = ath6kl_set_auth_type(vif, info->auth_type);
2351 if (ret)
2352 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002353
2354 memset(&p, 0, sizeof(p));
2355
2356 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2357 switch (info->crypto.akm_suites[i]) {
2358 case WLAN_AKM_SUITE_8021X:
2359 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2360 p.auth_mode |= WPA_AUTH;
2361 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2362 p.auth_mode |= WPA2_AUTH;
2363 break;
2364 case WLAN_AKM_SUITE_PSK:
2365 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2366 p.auth_mode |= WPA_PSK_AUTH;
2367 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2368 p.auth_mode |= WPA2_PSK_AUTH;
2369 break;
2370 }
2371 }
2372 if (p.auth_mode == 0)
2373 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302374 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002375
2376 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2377 switch (info->crypto.ciphers_pairwise[i]) {
2378 case WLAN_CIPHER_SUITE_WEP40:
2379 case WLAN_CIPHER_SUITE_WEP104:
2380 p.prwise_crypto_type |= WEP_CRYPT;
2381 break;
2382 case WLAN_CIPHER_SUITE_TKIP:
2383 p.prwise_crypto_type |= TKIP_CRYPT;
2384 break;
2385 case WLAN_CIPHER_SUITE_CCMP:
2386 p.prwise_crypto_type |= AES_CRYPT;
2387 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002388 case WLAN_CIPHER_SUITE_SMS4:
2389 p.prwise_crypto_type |= WAPI_CRYPT;
2390 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002391 }
2392 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002393 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002394 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302395 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002396 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302397 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002398
2399 switch (info->crypto.cipher_group) {
2400 case WLAN_CIPHER_SUITE_WEP40:
2401 case WLAN_CIPHER_SUITE_WEP104:
2402 p.grp_crypto_type = WEP_CRYPT;
2403 break;
2404 case WLAN_CIPHER_SUITE_TKIP:
2405 p.grp_crypto_type = TKIP_CRYPT;
2406 break;
2407 case WLAN_CIPHER_SUITE_CCMP:
2408 p.grp_crypto_type = AES_CRYPT;
2409 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002410 case WLAN_CIPHER_SUITE_SMS4:
2411 p.grp_crypto_type = WAPI_CRYPT;
2412 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002413 default:
2414 p.grp_crypto_type = NONE_CRYPT;
2415 break;
2416 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302417 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002418
2419 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302420 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002421
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302422 p.ssid_len = vif->ssid_len;
2423 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2424 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302425 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002426
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302427 /* Enable uAPSD support by default */
2428 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2429 if (res < 0)
2430 return res;
2431
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002432 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2433 p.nw_subtype = SUBTYPE_P2PGO;
2434 } else {
2435 /*
2436 * Due to firmware limitation, it is not possible to
2437 * do P2P mgmt operations in AP mode
2438 */
2439 p.nw_subtype = SUBTYPE_NONE;
2440 }
2441
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302442 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002443 if (res < 0)
2444 return res;
2445
2446 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002447}
2448
Johannes Berg88600202012-02-13 15:17:18 +01002449static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2450 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002451{
Johannes Berg88600202012-02-13 15:17:18 +01002452 struct ath6kl_vif *vif = netdev_priv(dev);
2453
2454 if (!ath6kl_cfg80211_ready(vif))
2455 return -EIO;
2456
2457 if (vif->next_mode != AP_NETWORK)
2458 return -EOPNOTSUPP;
2459
2460 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002461}
2462
Johannes Berg88600202012-02-13 15:17:18 +01002463static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002464{
2465 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302466 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002467
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302468 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002469 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302470 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002471 return -ENOTCONN;
2472
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302473 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302474 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002475
2476 return 0;
2477}
2478
Jouni Malinen33e53082011-12-27 11:02:56 +02002479static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2480
2481static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2482 u8 *mac)
2483{
2484 struct ath6kl *ar = ath6kl_priv(dev);
2485 struct ath6kl_vif *vif = netdev_priv(dev);
2486 const u8 *addr = mac ? mac : bcast_addr;
2487
2488 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2489 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2490}
2491
Jouni Malinen23875132011-08-30 21:57:53 +03002492static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2493 u8 *mac, struct station_parameters *params)
2494{
2495 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302496 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002497
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302498 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002499 return -EOPNOTSUPP;
2500
2501 /* Use this only for authorizing/unauthorizing a station */
2502 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2503 return -EOPNOTSUPP;
2504
2505 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302506 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2507 WMI_AP_MLME_AUTHORIZE, mac, 0);
2508 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2509 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002510}
2511
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002512static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2513 struct net_device *dev,
2514 struct ieee80211_channel *chan,
2515 enum nl80211_channel_type channel_type,
2516 unsigned int duration,
2517 u64 *cookie)
2518{
2519 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302520 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002521 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002522
2523 /* TODO: if already pending or ongoing remain-on-channel,
2524 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002525 id = ++vif->last_roc_id;
2526 if (id == 0) {
2527 /* Do not use 0 as the cookie value */
2528 id = ++vif->last_roc_id;
2529 }
2530 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002531
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302532 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2533 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002534}
2535
2536static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2537 struct net_device *dev,
2538 u64 cookie)
2539{
2540 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302541 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002542
Jouni Malinen10522612011-10-27 16:00:13 +03002543 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002544 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002545 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002546
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302547 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002548}
2549
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302550static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2551 const u8 *buf, size_t len,
2552 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002553{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302554 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002555 const u8 *pos;
2556 u8 *p2p;
2557 int p2p_len;
2558 int ret;
2559 const struct ieee80211_mgmt *mgmt;
2560
2561 mgmt = (const struct ieee80211_mgmt *) buf;
2562
2563 /* Include P2P IE(s) from the frame generated in user space. */
2564
2565 p2p = kmalloc(len, GFP_KERNEL);
2566 if (p2p == NULL)
2567 return -ENOMEM;
2568 p2p_len = 0;
2569
2570 pos = mgmt->u.probe_resp.variable;
2571 while (pos + 1 < buf + len) {
2572 if (pos + 2 + pos[1] > buf + len)
2573 break;
2574 if (ath6kl_is_p2p_ie(pos)) {
2575 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2576 p2p_len += 2 + pos[1];
2577 }
2578 pos += 2 + pos[1];
2579 }
2580
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302581 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2582 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002583 kfree(p2p);
2584 return ret;
2585}
2586
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002587static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2588 struct ieee80211_channel *chan, bool offchan,
2589 enum nl80211_channel_type channel_type,
2590 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002591 const u8 *buf, size_t len, bool no_cck,
2592 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002593{
2594 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302595 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002596 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002597 const struct ieee80211_mgmt *mgmt;
2598
2599 mgmt = (const struct ieee80211_mgmt *) buf;
2600 if (buf + len >= mgmt->u.probe_resp.variable &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302601 vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002602 ieee80211_is_probe_resp(mgmt->frame_control)) {
2603 /*
2604 * Send Probe Response frame in AP mode using a separate WMI
2605 * command to allow the target to fill in the generic IEs.
2606 */
2607 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302608 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002609 chan->center_freq);
2610 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002611
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302612 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002613 if (id == 0) {
2614 /*
2615 * 0 is a reserved value in the WMI command and shall not be
2616 * used for the command.
2617 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302618 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002619 }
2620
2621 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002622
2623 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
2624 ar->fw_capabilities)) {
2625 /*
2626 * If capable of doing P2P mgmt operations using
2627 * station interface, send additional information like
2628 * supported rates to advertise and xmit rates for
2629 * probe requests
2630 */
2631 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2632 chan->center_freq, wait,
2633 buf, len, no_cck);
2634 } else {
2635 return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
2636 chan->center_freq, wait,
2637 buf, len);
2638 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002639}
2640
Jouni Malinenae32c302011-08-30 21:58:01 +03002641static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2642 struct net_device *dev,
2643 u16 frame_type, bool reg)
2644{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302645 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002646
2647 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2648 __func__, frame_type, reg);
2649 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2650 /*
2651 * Note: This notification callback is not allowed to sleep, so
2652 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2653 * hardcode target to report Probe Request frames all the time.
2654 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302655 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002656 }
2657}
2658
Kalle Valo10509f92011-12-13 14:52:07 +02002659static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2660 struct net_device *dev,
2661 struct cfg80211_sched_scan_request *request)
2662{
2663 struct ath6kl *ar = ath6kl_priv(dev);
2664 struct ath6kl_vif *vif = netdev_priv(dev);
2665 u16 interval;
2666 int ret;
2667 u8 i;
2668
2669 if (ar->state != ATH6KL_STATE_ON)
2670 return -EIO;
2671
2672 if (vif->sme_state != SME_DISCONNECTED)
2673 return -EBUSY;
2674
2675 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2676 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2677 i, DISABLE_SSID_FLAG,
2678 0, NULL);
2679 }
2680
2681 /* fw uses seconds, also make sure that it's >0 */
2682 interval = max_t(u16, 1, request->interval / 1000);
2683
2684 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2685 interval, interval,
2686 10, 0, 0, 0, 3, 0, 0, 0);
2687
2688 if (request->n_ssids && request->ssids[0].ssid_len) {
2689 for (i = 0; i < request->n_ssids; i++) {
2690 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2691 i, SPECIFIC_SSID_FLAG,
2692 request->ssids[i].ssid_len,
2693 request->ssids[i].ssid);
2694 }
2695 }
2696
2697 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2698 ATH6KL_WOW_MODE_ENABLE,
2699 WOW_FILTER_SSID,
2700 WOW_HOST_REQ_DELAY);
2701 if (ret) {
2702 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2703 return ret;
2704 }
2705
2706 /* this also clears IE in fw if it's not set */
2707 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2708 WMI_FRAME_PROBE_REQ,
2709 request->ie, request->ie_len);
2710 if (ret) {
2711 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
2712 ret);
2713 return ret;
2714 }
2715
2716 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2717 ATH6KL_HOST_MODE_ASLEEP);
2718 if (ret) {
2719 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
2720 ret);
2721 return ret;
2722 }
2723
2724 ar->state = ATH6KL_STATE_SCHED_SCAN;
2725
2726 return ret;
2727}
2728
2729static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
2730 struct net_device *dev)
2731{
2732 struct ath6kl_vif *vif = netdev_priv(dev);
2733 bool stopped;
2734
2735 stopped = __ath6kl_cfg80211_sscan_stop(vif);
2736
2737 if (!stopped)
2738 return -EIO;
2739
2740 return 0;
2741}
2742
Jouni Malinenf80574a2011-08-30 21:58:04 +03002743static const struct ieee80211_txrx_stypes
2744ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2745 [NL80211_IFTYPE_STATION] = {
2746 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2747 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2748 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2749 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2750 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02002751 [NL80211_IFTYPE_AP] = {
2752 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2753 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2754 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2755 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2756 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03002757 [NL80211_IFTYPE_P2P_CLIENT] = {
2758 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2759 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2760 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2761 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2762 },
2763 [NL80211_IFTYPE_P2P_GO] = {
2764 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2765 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2766 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2767 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2768 },
2769};
2770
Kalle Valobdcd8172011-07-18 00:22:30 +03002771static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302772 .add_virtual_intf = ath6kl_cfg80211_add_iface,
2773 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03002774 .change_virtual_intf = ath6kl_cfg80211_change_iface,
2775 .scan = ath6kl_cfg80211_scan,
2776 .connect = ath6kl_cfg80211_connect,
2777 .disconnect = ath6kl_cfg80211_disconnect,
2778 .add_key = ath6kl_cfg80211_add_key,
2779 .get_key = ath6kl_cfg80211_get_key,
2780 .del_key = ath6kl_cfg80211_del_key,
2781 .set_default_key = ath6kl_cfg80211_set_default_key,
2782 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
2783 .set_tx_power = ath6kl_cfg80211_set_txpower,
2784 .get_tx_power = ath6kl_cfg80211_get_txpower,
2785 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
2786 .join_ibss = ath6kl_cfg80211_join_ibss,
2787 .leave_ibss = ath6kl_cfg80211_leave_ibss,
2788 .get_station = ath6kl_get_station,
2789 .set_pmksa = ath6kl_set_pmksa,
2790 .del_pmksa = ath6kl_del_pmksa,
2791 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03002792 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03002793#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002794 .suspend = __ath6kl_cfg80211_suspend,
2795 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03002796#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002797 .set_channel = ath6kl_set_channel,
Johannes Berg88600202012-02-13 15:17:18 +01002798 .start_ap = ath6kl_start_ap,
2799 .change_beacon = ath6kl_change_beacon,
2800 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02002801 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03002802 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002803 .remain_on_channel = ath6kl_remain_on_channel,
2804 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002805 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03002806 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02002807 .sched_scan_start = ath6kl_cfg80211_sscan_start,
2808 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03002809};
2810
Kalle Valo7125f012011-12-13 14:51:37 +02002811void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02002812{
Kalle Valo10509f92011-12-13 14:52:07 +02002813 ath6kl_cfg80211_sscan_disable(vif);
2814
Kalle Valoec4b7f62011-11-01 08:44:04 +02002815 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02002816 case SME_DISCONNECTED:
2817 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02002818 case SME_CONNECTING:
2819 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
2820 NULL, 0,
2821 WLAN_STATUS_UNSPECIFIED_FAILURE,
2822 GFP_KERNEL);
2823 break;
2824 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02002825 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
2826 break;
2827 }
2828
2829 if (test_bit(CONNECTED, &vif->flags) ||
2830 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02002831 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002832
2833 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02002834 clear_bit(CONNECTED, &vif->flags);
2835 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002836
2837 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02002838 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
2839 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
2840 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02002841
2842 ath6kl_cfg80211_scan_complete_event(vif, true);
2843}
2844
Kalle Valo7125f012011-12-13 14:51:37 +02002845void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
2846{
2847 struct ath6kl_vif *vif;
2848
2849 vif = ath6kl_vif_first(ar);
2850 if (!vif) {
2851 /* save the current power mode before enabling power save */
2852 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2853
2854 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
2855 ath6kl_warn("ath6kl_deep_sleep_enable: "
2856 "wmi_powermode_cmd failed\n");
2857 return;
2858 }
2859
2860 /*
2861 * FIXME: we should take ar->list_lock to protect changes in the
2862 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
2863 * sleeps.
2864 */
2865 list_for_each_entry(vif, &ar->vif_list, list)
2866 ath6kl_cfg80211_stop(vif);
2867}
2868
Kalle Valoc25889e2012-01-17 20:08:27 +02002869static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03002870{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05302871 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302872 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302873 ath6kl_err("failed to initialize aggr\n");
2874 return -ENOMEM;
2875 }
Kalle Valobdcd8172011-07-18 00:22:30 +03002876
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302877 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302878 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02002879 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
2880 (unsigned long) vif);
2881
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302882 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05302883 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302884
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05302885 INIT_LIST_HEAD(&vif->mc_filter);
2886
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302887 return 0;
2888}
2889
Kalle Valoc25889e2012-01-17 20:08:27 +02002890void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302891{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302892 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05302893 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302894
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302895 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302896
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302897 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
2898
2899 if (vif->nw_type == ADHOC_NETWORK)
2900 ar->ibss_if_active = false;
2901
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05302902 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
2903 list_del(&mc_filter->list);
2904 kfree(mc_filter);
2905 }
2906
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302907 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302908
2909 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302910}
2911
2912struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302913 enum nl80211_iftype type, u8 fw_vif_idx,
2914 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302915{
2916 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302917 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302918
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302919 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302920 if (!ndev)
2921 return NULL;
2922
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302923 vif = netdev_priv(ndev);
2924 ndev->ieee80211_ptr = &vif->wdev;
2925 vif->wdev.wiphy = ar->wiphy;
2926 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302927 vif->ndev = ndev;
2928 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
2929 vif->wdev.netdev = ndev;
2930 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302931 vif->fw_vif_idx = fw_vif_idx;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302932 vif->nw_type = vif->next_mode = nw_type;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302933
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302934 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
2935 if (fw_vif_idx != 0)
2936 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
2937 0x2;
2938
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302939 init_netdev(ndev);
2940
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05302941 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302942
Kalle Valoc25889e2012-01-17 20:08:27 +02002943 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302944 goto err;
2945
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302946 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302947 goto err;
2948
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302949 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05302950 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302951 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302952 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302953 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302954
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302955 if (type == NL80211_IFTYPE_ADHOC)
2956 ar->ibss_if_active = true;
2957
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05302958 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302959 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05302960 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302961
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302962 return ndev;
2963
2964err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302965 aggr_module_destroy(vif->aggr_cntxt);
2966 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302967 return NULL;
2968}
2969
Kalle Valo46d33a22012-01-17 20:08:40 +02002970int ath6kl_cfg80211_init(struct ath6kl *ar)
2971{
2972 struct wiphy *wiphy = ar->wiphy;
2973 int ret;
2974
2975 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
2976
2977 wiphy->max_remain_on_channel_duration = 5000;
2978
2979 /* set device pointer for wiphy */
2980 set_wiphy_dev(wiphy, ar->dev);
2981
2982 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
2983 BIT(NL80211_IFTYPE_ADHOC) |
2984 BIT(NL80211_IFTYPE_AP);
2985 if (ar->p2p) {
2986 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
2987 BIT(NL80211_IFTYPE_P2P_CLIENT);
2988 }
2989
2990 /* max num of ssids that can be probed during scanning */
2991 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
2992 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
2993 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
2994 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
2995 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
2996
2997 wiphy->cipher_suites = cipher_suites;
2998 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
2999
3000 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3001 WIPHY_WOWLAN_DISCONNECT |
3002 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3003 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3004 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3005 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3006 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3007 wiphy->wowlan.pattern_min_len = 1;
3008 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
3009
3010 wiphy->max_sched_scan_ssids = 10;
3011
3012 ret = wiphy_register(wiphy);
3013 if (ret < 0) {
3014 ath6kl_err("couldn't register wiphy device\n");
3015 return ret;
3016 }
3017
3018 return 0;
3019}
3020
3021void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303022{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303023 wiphy_unregister(ar->wiphy);
Kalle Valo45eaa782012-01-17 20:09:05 +02003024}
Kalle Valo46d33a22012-01-17 20:08:40 +02003025
Kalle Valo45eaa782012-01-17 20:09:05 +02003026struct ath6kl *ath6kl_cfg80211_create(void)
3027{
3028 struct ath6kl *ar;
3029 struct wiphy *wiphy;
3030
3031 /* create a new wiphy for use with cfg80211 */
3032 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3033
3034 if (!wiphy) {
3035 ath6kl_err("couldn't allocate wiphy device\n");
3036 return NULL;
3037 }
3038
3039 ar = wiphy_priv(wiphy);
3040 ar->wiphy = wiphy;
3041
3042 return ar;
3043}
3044
3045/* Note: ar variable must not be accessed after calling this! */
3046void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3047{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303048 int i;
3049
3050 for (i = 0; i < AP_MAX_NUM_STA; i++)
3051 kfree(ar->sta_list[i].aggr_conn);
3052
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303053 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003054}
Kalle Valo45eaa782012-01-17 20:09:05 +02003055