blob: bdcc68fb1e37b98a2f1edb14e0eba789491c4863 [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
Vasanthakumar Thiagarajan1b2df402012-02-06 20:15:53 +05303 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
Kalle Valobdcd8172011-07-18 00:22:30 +03004 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
Joe Perches516304b2012-03-18 17:30:52 -070018#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040020#include <linux/moduleparam.h>
Raja Manic08631c2011-12-16 14:24:24 +053021#include <linux/inetdevice.h>
Kalle Valod6a434d2012-01-17 20:09:36 +020022#include <linux/export.h>
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040023
Kalle Valobdcd8172011-07-18 00:22:30 +030024#include "core.h"
25#include "cfg80211.h"
26#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030027#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030028#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030029
30#define RATETAB_ENT(_rate, _rateid, _flags) { \
31 .bitrate = (_rate), \
32 .flags = (_flags), \
33 .hw_value = (_rateid), \
34}
35
36#define CHAN2G(_channel, _freq, _flags) { \
37 .band = IEEE80211_BAND_2GHZ, \
38 .hw_value = (_channel), \
39 .center_freq = (_freq), \
40 .flags = (_flags), \
41 .max_antenna_gain = 0, \
42 .max_power = 30, \
43}
44
45#define CHAN5G(_channel, _flags) { \
46 .band = IEEE80211_BAND_5GHZ, \
47 .hw_value = (_channel), \
48 .center_freq = 5000 + (5 * (_channel)), \
49 .flags = (_flags), \
50 .max_antenna_gain = 0, \
51 .max_power = 30, \
52}
53
54static struct ieee80211_rate ath6kl_rates[] = {
55 RATETAB_ENT(10, 0x1, 0),
56 RATETAB_ENT(20, 0x2, 0),
57 RATETAB_ENT(55, 0x4, 0),
58 RATETAB_ENT(110, 0x8, 0),
59 RATETAB_ENT(60, 0x10, 0),
60 RATETAB_ENT(90, 0x20, 0),
61 RATETAB_ENT(120, 0x40, 0),
62 RATETAB_ENT(180, 0x80, 0),
63 RATETAB_ENT(240, 0x100, 0),
64 RATETAB_ENT(360, 0x200, 0),
65 RATETAB_ENT(480, 0x400, 0),
66 RATETAB_ENT(540, 0x800, 0),
67};
68
69#define ath6kl_a_rates (ath6kl_rates + 4)
70#define ath6kl_a_rates_size 8
71#define ath6kl_g_rates (ath6kl_rates + 0)
72#define ath6kl_g_rates_size 12
73
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +053074#define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
75 IEEE80211_HT_CAP_SGI_20 | \
76 IEEE80211_HT_CAP_SGI_40)
77
Kalle Valobdcd8172011-07-18 00:22:30 +030078static struct ieee80211_channel ath6kl_2ghz_channels[] = {
79 CHAN2G(1, 2412, 0),
80 CHAN2G(2, 2417, 0),
81 CHAN2G(3, 2422, 0),
82 CHAN2G(4, 2427, 0),
83 CHAN2G(5, 2432, 0),
84 CHAN2G(6, 2437, 0),
85 CHAN2G(7, 2442, 0),
86 CHAN2G(8, 2447, 0),
87 CHAN2G(9, 2452, 0),
88 CHAN2G(10, 2457, 0),
89 CHAN2G(11, 2462, 0),
90 CHAN2G(12, 2467, 0),
91 CHAN2G(13, 2472, 0),
92 CHAN2G(14, 2484, 0),
93};
94
95static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
96 CHAN5G(34, 0), CHAN5G(36, 0),
97 CHAN5G(38, 0), CHAN5G(40, 0),
98 CHAN5G(42, 0), CHAN5G(44, 0),
99 CHAN5G(46, 0), CHAN5G(48, 0),
100 CHAN5G(52, 0), CHAN5G(56, 0),
101 CHAN5G(60, 0), CHAN5G(64, 0),
102 CHAN5G(100, 0), CHAN5G(104, 0),
103 CHAN5G(108, 0), CHAN5G(112, 0),
104 CHAN5G(116, 0), CHAN5G(120, 0),
105 CHAN5G(124, 0), CHAN5G(128, 0),
106 CHAN5G(132, 0), CHAN5G(136, 0),
107 CHAN5G(140, 0), CHAN5G(149, 0),
108 CHAN5G(153, 0), CHAN5G(157, 0),
109 CHAN5G(161, 0), CHAN5G(165, 0),
110 CHAN5G(184, 0), CHAN5G(188, 0),
111 CHAN5G(192, 0), CHAN5G(196, 0),
112 CHAN5G(200, 0), CHAN5G(204, 0),
113 CHAN5G(208, 0), CHAN5G(212, 0),
114 CHAN5G(216, 0),
115};
116
117static struct ieee80211_supported_band ath6kl_band_2ghz = {
118 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
119 .channels = ath6kl_2ghz_channels,
120 .n_bitrates = ath6kl_g_rates_size,
121 .bitrates = ath6kl_g_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530122 .ht_cap.cap = ath6kl_g_htcap,
123 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300124};
125
126static struct ieee80211_supported_band ath6kl_band_5ghz = {
127 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
128 .channels = ath6kl_5ghz_a_channels,
129 .n_bitrates = ath6kl_a_rates_size,
130 .bitrates = ath6kl_a_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530131 .ht_cap.cap = ath6kl_g_htcap,
132 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300133};
134
Jouni Malinen837cb972011-10-11 17:31:57 +0300135#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
136
Kalle Valo10509f92011-12-13 14:52:07 +0200137/* returns true if scheduled scan was stopped */
138static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
139{
140 struct ath6kl *ar = vif->ar;
141
142 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
143 return false;
144
145 del_timer_sync(&vif->sched_scan_timer);
146
147 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
148 ATH6KL_HOST_MODE_AWAKE);
149
150 ar->state = ATH6KL_STATE_ON;
151
152 return true;
153}
154
155static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
156{
157 struct ath6kl *ar = vif->ar;
158 bool stopped;
159
160 stopped = __ath6kl_cfg80211_sscan_stop(vif);
161
162 if (!stopped)
163 return;
164
165 cfg80211_sched_scan_stopped(ar->wiphy);
166}
167
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530168static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300169 enum nl80211_wpa_versions wpa_version)
170{
171 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
172
173 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530174 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300175 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530176 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300177 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530178 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300179 } else {
180 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
181 return -ENOTSUPP;
182 }
183
184 return 0;
185}
186
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530187static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300188 enum nl80211_auth_type auth_type)
189{
Kalle Valobdcd8172011-07-18 00:22:30 +0300190 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
191
192 switch (auth_type) {
193 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530194 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300195 break;
196 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530197 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300198 break;
199 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530200 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300201 break;
202
203 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530204 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300205 break;
206
207 default:
Masanari Iida3c325fb2012-01-31 23:32:55 +0900208 ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300209 return -ENOTSUPP;
210 }
211
212 return 0;
213}
214
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530215static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300216{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530217 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
218 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
219 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300220
221 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
222 __func__, cipher, ucast);
223
224 switch (cipher) {
225 case 0:
226 /* our own hack to use value 0 as no crypto used */
227 *ar_cipher = NONE_CRYPT;
228 *ar_cipher_len = 0;
229 break;
230 case WLAN_CIPHER_SUITE_WEP40:
231 *ar_cipher = WEP_CRYPT;
232 *ar_cipher_len = 5;
233 break;
234 case WLAN_CIPHER_SUITE_WEP104:
235 *ar_cipher = WEP_CRYPT;
236 *ar_cipher_len = 13;
237 break;
238 case WLAN_CIPHER_SUITE_TKIP:
239 *ar_cipher = TKIP_CRYPT;
240 *ar_cipher_len = 0;
241 break;
242 case WLAN_CIPHER_SUITE_CCMP:
243 *ar_cipher = AES_CRYPT;
244 *ar_cipher_len = 0;
245 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200246 case WLAN_CIPHER_SUITE_SMS4:
247 *ar_cipher = WAPI_CRYPT;
248 *ar_cipher_len = 0;
249 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300250 default:
251 ath6kl_err("cipher 0x%x not supported\n", cipher);
252 return -ENOTSUPP;
253 }
254
255 return 0;
256}
257
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530258static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300259{
260 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
261
262 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530263 if (vif->auth_mode == WPA_AUTH)
264 vif->auth_mode = WPA_PSK_AUTH;
265 else if (vif->auth_mode == WPA2_AUTH)
266 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300267 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530268 if (vif->auth_mode == WPA_AUTH)
269 vif->auth_mode = WPA_AUTH_CCKM;
270 else if (vif->auth_mode == WPA2_AUTH)
271 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300272 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530273 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300274 }
275}
276
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530277static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300278{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530279 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530280
Kalle Valobdcd8172011-07-18 00:22:30 +0300281 if (!test_bit(WMI_READY, &ar->flag)) {
282 ath6kl_err("wmi is not ready\n");
283 return false;
284 }
285
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530286 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300287 ath6kl_err("wlan disabled\n");
288 return false;
289 }
290
291 return true;
292}
293
Kevin Fang6981ffd2011-10-07 08:51:19 +0800294static bool ath6kl_is_wpa_ie(const u8 *pos)
295{
296 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
297 pos[2] == 0x00 && pos[3] == 0x50 &&
298 pos[4] == 0xf2 && pos[5] == 0x01;
299}
300
301static bool ath6kl_is_rsn_ie(const u8 *pos)
302{
303 return pos[0] == WLAN_EID_RSN;
304}
305
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700306static bool ath6kl_is_wps_ie(const u8 *pos)
307{
308 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
309 pos[1] >= 4 &&
310 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
311 pos[5] == 0x04);
312}
313
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530314static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
315 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800316{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530317 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800318 const u8 *pos;
319 u8 *buf = NULL;
320 size_t len = 0;
321 int ret;
322
323 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700324 * Clear previously set flag
325 */
326
327 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
328
329 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800330 * Filter out RSN/WPA IE(s)
331 */
332
333 if (ies && ies_len) {
334 buf = kmalloc(ies_len, GFP_KERNEL);
335 if (buf == NULL)
336 return -ENOMEM;
337 pos = ies;
338
339 while (pos + 1 < ies + ies_len) {
340 if (pos + 2 + pos[1] > ies + ies_len)
341 break;
342 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
343 memcpy(buf + len, pos, 2 + pos[1]);
344 len += 2 + pos[1];
345 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700346
347 if (ath6kl_is_wps_ie(pos))
348 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
349
Kevin Fang6981ffd2011-10-07 08:51:19 +0800350 pos += 2 + pos[1];
351 }
352 }
353
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530354 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
355 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800356 kfree(buf);
357 return ret;
358}
359
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530360static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
361{
362 switch (type) {
363 case NL80211_IFTYPE_STATION:
364 *nw_type = INFRA_NETWORK;
365 break;
366 case NL80211_IFTYPE_ADHOC:
367 *nw_type = ADHOC_NETWORK;
368 break;
369 case NL80211_IFTYPE_AP:
370 *nw_type = AP_NETWORK;
371 break;
372 case NL80211_IFTYPE_P2P_CLIENT:
373 *nw_type = INFRA_NETWORK;
374 break;
375 case NL80211_IFTYPE_P2P_GO:
376 *nw_type = AP_NETWORK;
377 break;
378 default:
379 ath6kl_err("invalid interface type %u\n", type);
380 return -ENOTSUPP;
381 }
382
383 return 0;
384}
385
386static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
387 u8 *if_idx, u8 *nw_type)
388{
389 int i;
390
391 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
392 return false;
393
394 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
Kalle Valo96f1fad2012-03-07 20:03:57 +0200395 ar->num_vif))
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530396 return false;
397
398 if (type == NL80211_IFTYPE_STATION ||
399 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200400 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530401 if ((ar->avail_idx_map >> i) & BIT(0)) {
402 *if_idx = i;
403 return true;
404 }
405 }
406 }
407
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530408 if (type == NL80211_IFTYPE_P2P_CLIENT ||
409 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200410 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530411 if ((ar->avail_idx_map >> i) & BIT(0)) {
412 *if_idx = i;
413 return true;
414 }
415 }
416 }
417
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530418 return false;
419}
420
Kalle Valo8c9bb052012-03-07 20:04:00 +0200421static bool ath6kl_is_tx_pending(struct ath6kl *ar)
422{
423 return ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0;
424}
425
426
Kalle Valobdcd8172011-07-18 00:22:30 +0300427static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
428 struct cfg80211_connect_params *sme)
429{
430 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530431 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300432 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800433 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Raja Manice0dc0c2012-02-20 19:08:08 +0530434 u16 interval;
Kalle Valobdcd8172011-07-18 00:22:30 +0300435
Kalle Valo10509f92011-12-13 14:52:07 +0200436 ath6kl_cfg80211_sscan_disable(vif);
437
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530438 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300439
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530440 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300441 return -EIO;
442
443 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
444 ath6kl_err("destroy in progress\n");
445 return -EBUSY;
446 }
447
448 if (test_bit(SKIP_SCAN, &ar->flag) &&
449 ((sme->channel && sme->channel->center_freq == 0) ||
450 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
451 ath6kl_err("SkipScan: channel or bssid invalid\n");
452 return -EINVAL;
453 }
454
455 if (down_interruptible(&ar->sem)) {
456 ath6kl_err("busy, couldn't get access\n");
457 return -ERESTARTSYS;
458 }
459
460 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
461 ath6kl_err("busy, destroy in progress\n");
462 up(&ar->sem);
463 return -EBUSY;
464 }
465
466 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
467 /*
468 * sleep until the command queue drains
469 */
470 wait_event_interruptible_timeout(ar->event_wq,
Kalle Valo8c9bb052012-03-07 20:04:00 +0200471 ath6kl_is_tx_pending(ar),
472 WMI_TIMEOUT);
Kalle Valobdcd8172011-07-18 00:22:30 +0300473 if (signal_pending(current)) {
474 ath6kl_err("cmd queue drain timeout\n");
475 up(&ar->sem);
476 return -EINTR;
477 }
478 }
479
Jouni Malinen6e786cb2011-12-15 14:16:00 +0200480 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
481 if (status) {
482 up(&ar->sem);
483 return status;
484 }
485
486 if (sme->ie == NULL || sme->ie_len == 0)
Raja Mani542c5192011-11-15 14:14:56 +0530487 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800488
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530489 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530490 vif->ssid_len == sme->ssid_len &&
491 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530492 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530493 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
494 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530495 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300496
497 up(&ar->sem);
498 if (status) {
499 ath6kl_err("wmi_reconnect_cmd failed\n");
500 return -EIO;
501 }
502 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530503 } else if (vif->ssid_len == sme->ssid_len &&
504 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530505 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300506 }
507
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530508 memset(vif->ssid, 0, sizeof(vif->ssid));
509 vif->ssid_len = sme->ssid_len;
510 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300511
512 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530513 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300514
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530515 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300516 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530517 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300518
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530519 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300520
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530521 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300522 if (status) {
523 up(&ar->sem);
524 return status;
525 }
526
527 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530528 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300529 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530530 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300531
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530532 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300533
534 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530535 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300536
537 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530538 (vif->auth_mode == NONE_AUTH) &&
539 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300540 struct ath6kl_key *key = NULL;
541
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530542 if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300543 ath6kl_err("key index %d out of bounds\n",
544 sme->key_idx);
545 up(&ar->sem);
546 return -ENOENT;
547 }
548
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530549 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300550 key->key_len = sme->key_len;
551 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530552 key->cipher = vif->prwise_crypto;
553 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300554
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530555 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530556 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300557 GROUP_USAGE | TX_USAGE,
558 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200559 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300560 key->key, KEY_OP_INIT_VAL, NULL,
561 NO_SYNC_WMIFLAG);
562 }
563
564 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530565 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530566 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200567 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300568 ath6kl_err("couldn't set bss filtering\n");
569 up(&ar->sem);
570 return -EIO;
571 }
572 }
573
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530574 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300575
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800576 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
577 nw_subtype = SUBTYPE_P2PCLIENT;
578
Kalle Valobdcd8172011-07-18 00:22:30 +0300579 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
580 "%s: connect called with authmode %d dot11 auth %d"
581 " PW crypto %d PW crypto len %d GRP crypto %d"
582 " GRP crypto len %d channel hint %u\n",
583 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530584 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
585 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530586 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300587
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530588 vif->reconnect_flag = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +0530589
590 if (vif->nw_type == INFRA_NETWORK) {
Kalle Valob5283872012-03-12 13:23:23 +0200591 interval = max_t(u16, vif->listen_intvl_t,
592 ATH6KL_MAX_WOW_LISTEN_INTL);
Raja Manice0dc0c2012-02-20 19:08:08 +0530593 status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
594 interval,
595 0);
596 if (status) {
597 ath6kl_err("couldn't set listen intervel\n");
598 up(&ar->sem);
599 return status;
600 }
601 }
602
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530603 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530604 vif->dot11_auth_mode, vif->auth_mode,
605 vif->prwise_crypto,
606 vif->prwise_crypto_len,
607 vif->grp_crypto, vif->grp_crypto_len,
608 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530609 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800610 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300611
612 up(&ar->sem);
613
614 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530615 memset(vif->ssid, 0, sizeof(vif->ssid));
616 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300617 ath6kl_err("invalid request\n");
618 return -ENOENT;
619 } else if (status) {
620 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
621 return -EIO;
622 }
623
624 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Kalle Valoddc3d772012-03-07 20:03:58 +0200625 ((vif->auth_mode == WPA_PSK_AUTH) ||
626 (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530627 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300628 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
629 }
630
631 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530632 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300633
634 return 0;
635}
636
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530637static struct cfg80211_bss *
638ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
639 enum network_type nw_type,
640 const u8 *bssid,
641 struct ieee80211_channel *chan,
642 const u8 *beacon_ie,
643 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300644{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530645 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300646 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530647 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300648 u8 *ie;
649
Raja Mani4eab6f42011-11-09 17:02:23 +0530650 if (nw_type & ADHOC_NETWORK) {
651 cap_mask = WLAN_CAPABILITY_IBSS;
652 cap_val = WLAN_CAPABILITY_IBSS;
653 } else {
654 cap_mask = WLAN_CAPABILITY_ESS;
655 cap_val = WLAN_CAPABILITY_ESS;
656 }
657
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530658 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530659 vif->ssid, vif->ssid_len,
660 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300661 if (bss == NULL) {
662 /*
663 * Since cfg80211 may not yet know about the BSS,
664 * generate a partial entry until the first BSS info
665 * event becomes available.
666 *
667 * Prepend SSID element since it is not included in the Beacon
668 * IEs from the target.
669 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530670 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300671 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530672 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300673 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530674 ie[1] = vif->ssid_len;
675 memcpy(ie + 2, vif->ssid, vif->ssid_len);
676 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530677 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530678 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530679 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300680 0, GFP_KERNEL);
681 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530682 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
683 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300684 kfree(ie);
685 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530686 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300687
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530688 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300689}
690
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530691void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300692 u8 *bssid, u16 listen_intvl,
693 u16 beacon_intvl,
694 enum network_type nw_type,
695 u8 beacon_ie_len, u8 assoc_req_len,
696 u8 assoc_resp_len, u8 *assoc_info)
697{
Jouni Malinen01cac472011-09-19 19:14:59 +0300698 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530699 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530700 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300701
702 /* capinfo + listen interval */
703 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
704
705 /* capinfo + status code + associd */
706 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
707
708 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
709 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
710 assoc_resp_ie_offset;
711
712 assoc_req_len -= assoc_req_ie_offset;
713 assoc_resp_len -= assoc_resp_ie_offset;
714
Jouni Malinen32c10872011-09-19 19:15:07 +0300715 /*
716 * Store Beacon interval here; DTIM period will be available only once
717 * a Beacon frame from the AP is seen.
718 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530719 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530720 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300721
Kalle Valobdcd8172011-07-18 00:22:30 +0300722 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530723 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300724 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
725 "%s: ath6k not in ibss mode\n", __func__);
726 return;
727 }
728 }
729
730 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530731 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
732 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300733 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
734 "%s: ath6k not in station mode\n", __func__);
735 return;
736 }
737 }
738
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530739 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300740
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530741 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
742 assoc_info, beacon_ie_len);
743 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530744 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300745 return;
746 }
747
Raja Mani4eab6f42011-11-09 17:02:23 +0530748 if (nw_type & ADHOC_NETWORK) {
749 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
750 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
751 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530752 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300753 return;
754 }
755
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530756 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300757 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530758 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530759 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300760 assoc_req_ie, assoc_req_len,
761 assoc_resp_ie, assoc_resp_len,
762 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530763 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530764 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300765 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530766 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
767 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300768 }
769}
770
771static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
772 struct net_device *dev, u16 reason_code)
773{
Kalle Valod6d5c062011-11-25 13:17:37 +0200774 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530775 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300776
777 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
778 reason_code);
779
Kalle Valo10509f92011-12-13 14:52:07 +0200780 ath6kl_cfg80211_sscan_disable(vif);
781
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530782 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300783 return -EIO;
784
785 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
786 ath6kl_err("busy, destroy in progress\n");
787 return -EBUSY;
788 }
789
790 if (down_interruptible(&ar->sem)) {
791 ath6kl_err("busy, couldn't get access\n");
792 return -ERESTARTSYS;
793 }
794
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530795 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530796 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530797 memset(vif->ssid, 0, sizeof(vif->ssid));
798 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300799
800 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530801 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300802
803 up(&ar->sem);
804
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530805 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530806
Kalle Valobdcd8172011-07-18 00:22:30 +0300807 return 0;
808}
809
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530810void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300811 u8 *bssid, u8 assoc_resp_len,
812 u8 *assoc_info, u16 proto_reason)
813{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530814 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530815
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530816 if (vif->scan_req) {
817 cfg80211_scan_done(vif->scan_req, true);
818 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300819 }
820
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530821 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530822 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300823 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
824 "%s: ath6k not in ibss mode\n", __func__);
825 return;
826 }
827 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530828 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300829 return;
830 }
831
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530832 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530833 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
834 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300835 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
836 "%s: ath6k not in station mode\n", __func__);
837 return;
838 }
839 }
840
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530841 /*
842 * Send a disconnect command to target when a disconnect event is
843 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
844 * request from host) to make the firmware stop trying to connect even
845 * after giving disconnect event. There will be one more disconnect
846 * event for this disconnect command with reason code DISCONNECT_CMD
847 * which will be notified to cfg80211.
848 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300849
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530850 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530851 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300852 return;
853 }
854
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530855 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300856
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530857 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530858 cfg80211_connect_result(vif->ndev,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200859 bssid, NULL, 0,
860 NULL, 0,
861 WLAN_STATUS_UNSPECIFIED_FAILURE,
862 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530863 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530864 cfg80211_disconnected(vif->ndev, reason,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200865 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300866 }
867
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530868 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300869}
870
Kalle Valobdcd8172011-07-18 00:22:30 +0300871static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
872 struct cfg80211_scan_request *request)
873{
Kalle Valod6d5c062011-11-25 13:17:37 +0200874 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530875 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300876 s8 n_channels = 0;
877 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300878 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530879 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300880
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530881 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300882 return -EIO;
883
Kalle Valo10509f92011-12-13 14:52:07 +0200884 ath6kl_cfg80211_sscan_disable(vif);
885
Kalle Valobdcd8172011-07-18 00:22:30 +0300886 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530887 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300888 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530889 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530890 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300891 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
892 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300893 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300894 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300895 }
896 }
897
898 if (request->n_ssids && request->ssids[0].ssid_len) {
899 u8 i;
900
901 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
902 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
903
904 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530905 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
906 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300907 request->ssids[i].ssid_len,
908 request->ssids[i].ssid);
909 }
910
Aarthi Thiruvengadam080eec42012-02-28 09:17:04 -0800911 /* this also clears IE in fw if it's not set */
912 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
913 WMI_FRAME_PROBE_REQ,
914 request->ie, request->ie_len);
915 if (ret) {
916 ath6kl_err("failed to set Probe Request appie for "
917 "scan");
918 return ret;
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300919 }
920
Jouni Malinen11869be2011-09-02 20:07:06 +0300921 /*
922 * Scan only the requested channels if the request specifies a set of
923 * channels. If the list is longer than the target supports, do not
924 * configure the list and instead, scan all available channels.
925 */
926 if (request->n_channels > 0 &&
927 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300928 u8 i;
929
Jouni Malinen11869be2011-09-02 20:07:06 +0300930 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300931
932 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
933 if (channels == NULL) {
934 ath6kl_warn("failed to set scan channels, "
935 "scan all channels");
936 n_channels = 0;
937 }
938
939 for (i = 0; i < n_channels; i++)
940 channels[i] = request->channels[i]->center_freq;
941 }
942
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530943 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530944 force_fg_scan = 1;
945
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800946 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200947 ar->fw_capabilities)) {
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800948 /*
949 * If capable of doing P2P mgmt operations using
950 * station interface, send additional information like
951 * supported rates to advertise and xmit rates for
952 * probe requests
953 */
954 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
955 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530956 false, 0,
957 ATH6KL_FG_SCAN_INTERVAL,
958 n_channels, channels,
959 request->no_cck,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800960 request->rates);
961 } else {
962 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
963 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530964 false, 0,
965 ATH6KL_FG_SCAN_INTERVAL,
966 n_channels, channels);
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800967 }
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300968 if (ret)
Kalle Valobdcd8172011-07-18 00:22:30 +0300969 ath6kl_err("wmi_startscan_cmd failed\n");
Jouni Malinen11869be2011-09-02 20:07:06 +0300970 else
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530971 vif->scan_req = request;
Kalle Valobdcd8172011-07-18 00:22:30 +0300972
Edward Lu1276c9e2011-08-30 21:58:00 +0300973 kfree(channels);
974
Kalle Valobdcd8172011-07-18 00:22:30 +0300975 return ret;
976}
977
Kalle Valo1c17d312011-11-01 08:43:56 +0200978void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300979{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530980 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300981 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300982
Kalle Valo1c17d312011-11-01 08:43:56 +0200983 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
984 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300985
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530986 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300987 return;
Kalle Valobdcd8172011-07-18 00:22:30 +0300988
Kalle Valo1c17d312011-11-01 08:43:56 +0200989 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300990 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300991
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530992 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
993 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530994 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
995 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300996 0, NULL);
997 }
998 }
999
1000out:
Kalle Valocb938212011-10-27 18:47:46 +03001001 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301002 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001003}
1004
1005static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1006 u8 key_index, bool pairwise,
1007 const u8 *mac_addr,
1008 struct key_params *params)
1009{
Kalle Valod6d5c062011-11-25 13:17:37 +02001010 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301011 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001012 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301013 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001014 u8 key_usage;
1015 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001016
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301017 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001018 return -EIO;
1019
Jouni Malinen837cb972011-10-11 17:31:57 +03001020 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
1021 if (params->key_len != WMI_KRK_LEN)
1022 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301023 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
1024 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +03001025 }
1026
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301027 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001028 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1029 "%s: key index %d out of bounds\n", __func__,
1030 key_index);
1031 return -ENOENT;
1032 }
1033
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301034 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001035 memset(key, 0, sizeof(struct ath6kl_key));
1036
1037 if (pairwise)
1038 key_usage = PAIRWISE_USAGE;
1039 else
1040 key_usage = GROUP_USAGE;
1041
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301042 seq_len = params->seq_len;
1043 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1044 seq_len > ATH6KL_KEY_SEQ_LEN) {
1045 /* Only first half of the WPI PN is configured */
1046 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001047 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301048 if (params->key_len > WLAN_MAX_KEY_LEN ||
1049 seq_len > sizeof(key->seq))
1050 return -EINVAL;
1051
1052 key->key_len = params->key_len;
1053 memcpy(key->key, params->key, key->key_len);
1054 key->seq_len = seq_len;
1055 memcpy(key->seq, params->seq, key->seq_len);
1056 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001057
1058 switch (key->cipher) {
1059 case WLAN_CIPHER_SUITE_WEP40:
1060 case WLAN_CIPHER_SUITE_WEP104:
1061 key_type = WEP_CRYPT;
1062 break;
1063
1064 case WLAN_CIPHER_SUITE_TKIP:
1065 key_type = TKIP_CRYPT;
1066 break;
1067
1068 case WLAN_CIPHER_SUITE_CCMP:
1069 key_type = AES_CRYPT;
1070 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001071 case WLAN_CIPHER_SUITE_SMS4:
1072 key_type = WAPI_CRYPT;
1073 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001074
1075 default:
1076 return -ENOTSUPP;
1077 }
1078
Kalle Valoddc3d772012-03-07 20:03:58 +02001079 if (((vif->auth_mode == WPA_PSK_AUTH) ||
1080 (vif->auth_mode == WPA2_PSK_AUTH)) &&
1081 (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301082 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001083
1084 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1085 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1086 __func__, key_index, key->key_len, key_type,
1087 key_usage, key->seq_len);
1088
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301089 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001090 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
Vasanthakumar Thiagarajancc4d6232012-02-14 20:33:00 +05301091 key_type == WAPI_CRYPT)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001092 ar->ap_mode_bkey.valid = true;
1093 ar->ap_mode_bkey.key_index = key_index;
1094 ar->ap_mode_bkey.key_type = key_type;
1095 ar->ap_mode_bkey.key_len = key->key_len;
1096 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301097 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001098 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1099 "key configuration until AP mode has been "
1100 "started\n");
1101 /*
1102 * The key will be set in ath6kl_connect_ap_mode() once
1103 * the connected event is received from the target.
1104 */
1105 return 0;
1106 }
1107 }
1108
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301109 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301110 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001111 /*
1112 * Store the key locally so that it can be re-configured after
1113 * the AP mode has properly started
1114 * (ath6kl_install_statioc_wep_keys).
1115 */
1116 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1117 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301118 vif->wep_key_list[key_index].key_len = key->key_len;
1119 memcpy(vif->wep_key_list[key_index].key, key->key,
1120 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001121 return 0;
1122 }
1123
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301124 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001125 key_type, key_usage, key->key_len,
1126 key->seq, key->seq_len, key->key,
1127 KEY_OP_INIT_VAL,
1128 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001129}
1130
1131static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1132 u8 key_index, bool pairwise,
1133 const u8 *mac_addr)
1134{
Kalle Valod6d5c062011-11-25 13:17:37 +02001135 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301136 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001137
1138 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1139
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301140 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001141 return -EIO;
1142
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301143 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001144 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1145 "%s: key index %d out of bounds\n", __func__,
1146 key_index);
1147 return -ENOENT;
1148 }
1149
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301150 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001151 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1152 "%s: index %d is empty\n", __func__, key_index);
1153 return 0;
1154 }
1155
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301156 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001157
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301158 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001159}
1160
1161static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1162 u8 key_index, bool pairwise,
1163 const u8 *mac_addr, void *cookie,
1164 void (*callback) (void *cookie,
1165 struct key_params *))
1166{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301167 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001168 struct ath6kl_key *key = NULL;
1169 struct key_params params;
1170
1171 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1172
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301173 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001174 return -EIO;
1175
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301176 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001177 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1178 "%s: key index %d out of bounds\n", __func__,
1179 key_index);
1180 return -ENOENT;
1181 }
1182
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301183 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001184 memset(&params, 0, sizeof(params));
1185 params.cipher = key->cipher;
1186 params.key_len = key->key_len;
1187 params.seq_len = key->seq_len;
1188 params.seq = key->seq;
1189 params.key = key->key;
1190
1191 callback(cookie, &params);
1192
1193 return key->key_len ? 0 : -ENOENT;
1194}
1195
1196static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1197 struct net_device *ndev,
1198 u8 key_index, bool unicast,
1199 bool multicast)
1200{
Kalle Valod6d5c062011-11-25 13:17:37 +02001201 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301202 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001203 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001204 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001205 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001206
1207 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1208
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301209 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001210 return -EIO;
1211
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301212 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001213 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1214 "%s: key index %d out of bounds\n",
1215 __func__, key_index);
1216 return -ENOENT;
1217 }
1218
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301219 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001220 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1221 __func__, key_index);
1222 return -EINVAL;
1223 }
1224
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301225 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301226 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001227 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301228 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001229 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001230 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301231 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001232 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301233 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001234
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301235 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001236 return 0; /* Delay until AP mode has been started */
1237
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001238 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1239 vif->def_txkey_index,
1240 key_type, key_usage,
1241 key->key_len, key->seq, key->seq_len,
1242 key->key,
1243 KEY_OP_INIT_VAL, NULL,
1244 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001245}
1246
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301247void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001248 bool ismcast)
1249{
1250 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1251 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1252
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301253 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001254 (ismcast ? NL80211_KEYTYPE_GROUP :
1255 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1256 GFP_KERNEL);
1257}
1258
1259static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1260{
1261 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301262 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001263 int ret;
1264
1265 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1266 changed);
1267
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301268 vif = ath6kl_vif_first(ar);
1269 if (!vif)
1270 return -EIO;
1271
1272 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001273 return -EIO;
1274
1275 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1276 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1277 if (ret != 0) {
1278 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1279 return -EIO;
1280 }
1281 }
1282
1283 return 0;
1284}
1285
1286/*
1287 * The type nl80211_tx_power_setting replaces the following
1288 * data type from 2.6.36 onwards
1289*/
1290static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1291 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001292 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001293{
1294 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301295 struct ath6kl_vif *vif;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001296 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001297
1298 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1299 type, dbm);
1300
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
1308 switch (type) {
1309 case NL80211_TX_POWER_AUTOMATIC:
1310 return 0;
1311 case NL80211_TX_POWER_LIMITED:
Kalle Valod0d670a2012-03-07 20:03:58 +02001312 ar->tx_pwr = dbm;
Kalle Valobdcd8172011-07-18 00:22:30 +03001313 break;
1314 default:
1315 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1316 __func__, type);
1317 return -EOPNOTSUPP;
1318 }
1319
Kalle Valod0d670a2012-03-07 20:03:58 +02001320 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001321
1322 return 0;
1323}
1324
1325static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1326{
1327 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301328 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001329
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301330 vif = ath6kl_vif_first(ar);
1331 if (!vif)
1332 return -EIO;
1333
1334 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001335 return -EIO;
1336
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301337 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001338 ar->tx_pwr = 0;
1339
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301340 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001341 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1342 return -EIO;
1343 }
1344
1345 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1346 5 * HZ);
1347
1348 if (signal_pending(current)) {
1349 ath6kl_err("target did not respond\n");
1350 return -EINTR;
1351 }
1352 }
1353
1354 *dbm = ar->tx_pwr;
1355 return 0;
1356}
1357
1358static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1359 struct net_device *dev,
1360 bool pmgmt, int timeout)
1361{
1362 struct ath6kl *ar = ath6kl_priv(dev);
1363 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301364 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001365
1366 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1367 __func__, pmgmt, timeout);
1368
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301369 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001370 return -EIO;
1371
1372 if (pmgmt) {
1373 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1374 mode.pwr_mode = REC_POWER;
1375 } else {
1376 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1377 mode.pwr_mode = MAX_PERF_POWER;
1378 }
1379
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301380 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001381 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001382 ath6kl_err("wmi_powermode_cmd failed\n");
1383 return -EIO;
1384 }
1385
1386 return 0;
1387}
1388
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301389static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1390 char *name,
1391 enum nl80211_iftype type,
1392 u32 *flags,
1393 struct vif_params *params)
1394{
1395 struct ath6kl *ar = wiphy_priv(wiphy);
1396 struct net_device *ndev;
1397 u8 if_idx, nw_type;
1398
Kalle Valo71f96ee2011-11-14 19:31:30 +02001399 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301400 ath6kl_err("Reached maximum number of supported vif\n");
1401 return ERR_PTR(-EINVAL);
1402 }
1403
1404 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1405 ath6kl_err("Not a supported interface type\n");
1406 return ERR_PTR(-EINVAL);
1407 }
1408
1409 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1410 if (!ndev)
1411 return ERR_PTR(-ENOMEM);
1412
1413 ar->num_vif++;
1414
1415 return ndev;
1416}
1417
1418static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1419 struct net_device *ndev)
1420{
1421 struct ath6kl *ar = wiphy_priv(wiphy);
1422 struct ath6kl_vif *vif = netdev_priv(ndev);
1423
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301424 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301425 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301426 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301427
1428 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1429
Kalle Valoc25889e2012-01-17 20:08:27 +02001430 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301431
1432 return 0;
1433}
1434
Kalle Valobdcd8172011-07-18 00:22:30 +03001435static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1436 struct net_device *ndev,
1437 enum nl80211_iftype type, u32 *flags,
1438 struct vif_params *params)
1439{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301440 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001441
1442 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1443
Kalle Valobdcd8172011-07-18 00:22:30 +03001444 switch (type) {
1445 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301446 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001447 break;
1448 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301449 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001450 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001451 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301452 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001453 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001454 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301455 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001456 break;
1457 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301458 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001459 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001460 default:
1461 ath6kl_err("invalid interface type %u\n", type);
1462 return -EOPNOTSUPP;
1463 }
1464
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301465 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001466
1467 return 0;
1468}
1469
1470static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1471 struct net_device *dev,
1472 struct cfg80211_ibss_params *ibss_param)
1473{
1474 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301475 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001476 int status;
1477
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301478 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001479 return -EIO;
1480
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301481 vif->ssid_len = ibss_param->ssid_len;
1482 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001483
1484 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301485 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001486
1487 if (ibss_param->channel_fixed) {
1488 /*
1489 * TODO: channel_fixed: The channel should be fixed, do not
1490 * search for IBSSs to join on other channels. Target
1491 * firmware does not support this feature, needs to be
1492 * updated.
1493 */
1494 return -EOPNOTSUPP;
1495 }
1496
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301497 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001498 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301499 memcpy(vif->req_bssid, ibss_param->bssid,
1500 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001501
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301502 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001503
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301504 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001505 if (status)
1506 return status;
1507
1508 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301509 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1510 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001511 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301512 ath6kl_set_cipher(vif, 0, true);
1513 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001514 }
1515
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301516 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001517
1518 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1519 "%s: connect called with authmode %d dot11 auth %d"
1520 " PW crypto %d PW crypto len %d GRP crypto %d"
1521 " GRP crypto len %d channel hint %u\n",
1522 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301523 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1524 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301525 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001526
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301527 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301528 vif->dot11_auth_mode, vif->auth_mode,
1529 vif->prwise_crypto,
1530 vif->prwise_crypto_len,
1531 vif->grp_crypto, vif->grp_crypto_len,
1532 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301533 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001534 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301535 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001536
1537 return 0;
1538}
1539
1540static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1541 struct net_device *dev)
1542{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301543 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001544
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301545 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001546 return -EIO;
1547
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301548 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301549 memset(vif->ssid, 0, sizeof(vif->ssid));
1550 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001551
1552 return 0;
1553}
1554
1555static const u32 cipher_suites[] = {
1556 WLAN_CIPHER_SUITE_WEP40,
1557 WLAN_CIPHER_SUITE_WEP104,
1558 WLAN_CIPHER_SUITE_TKIP,
1559 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001560 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001561 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001562};
1563
1564static bool is_rate_legacy(s32 rate)
1565{
1566 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1567 6000, 9000, 12000, 18000, 24000,
1568 36000, 48000, 54000
1569 };
1570 u8 i;
1571
1572 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1573 if (rate == legacy[i])
1574 return true;
1575
1576 return false;
1577}
1578
1579static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1580{
1581 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1582 52000, 58500, 65000, 72200
1583 };
1584 u8 i;
1585
1586 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1587 if (rate == ht20[i]) {
1588 if (i == ARRAY_SIZE(ht20) - 1)
1589 /* last rate uses sgi */
1590 *sgi = true;
1591 else
1592 *sgi = false;
1593
1594 *mcs = i;
1595 return true;
1596 }
1597 }
1598 return false;
1599}
1600
1601static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1602{
1603 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1604 81000, 108000, 121500, 135000,
1605 150000
1606 };
1607 u8 i;
1608
1609 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1610 if (rate == ht40[i]) {
1611 if (i == ARRAY_SIZE(ht40) - 1)
1612 /* last rate uses sgi */
1613 *sgi = true;
1614 else
1615 *sgi = false;
1616
1617 *mcs = i;
1618 return true;
1619 }
1620 }
1621
1622 return false;
1623}
1624
1625static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1626 u8 *mac, struct station_info *sinfo)
1627{
1628 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301629 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001630 long left;
1631 bool sgi;
1632 s32 rate;
1633 int ret;
1634 u8 mcs;
1635
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301636 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001637 return -ENOENT;
1638
1639 if (down_interruptible(&ar->sem))
1640 return -EBUSY;
1641
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301642 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001643
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301644 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001645
1646 if (ret != 0) {
1647 up(&ar->sem);
1648 return -EIO;
1649 }
1650
1651 left = wait_event_interruptible_timeout(ar->event_wq,
1652 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301653 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001654 WMI_TIMEOUT);
1655
1656 up(&ar->sem);
1657
1658 if (left == 0)
1659 return -ETIMEDOUT;
1660 else if (left < 0)
1661 return left;
1662
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301663 if (vif->target_stats.rx_byte) {
1664 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001665 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301666 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001667 sinfo->filled |= STATION_INFO_RX_PACKETS;
1668 }
1669
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301670 if (vif->target_stats.tx_byte) {
1671 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001672 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301673 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001674 sinfo->filled |= STATION_INFO_TX_PACKETS;
1675 }
1676
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301677 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001678 sinfo->filled |= STATION_INFO_SIGNAL;
1679
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301680 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001681
1682 if (is_rate_legacy(rate)) {
1683 sinfo->txrate.legacy = rate / 100;
1684 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1685 if (sgi) {
1686 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1687 sinfo->txrate.mcs = mcs - 1;
1688 } else {
1689 sinfo->txrate.mcs = mcs;
1690 }
1691
1692 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1693 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1694 if (sgi) {
1695 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1696 sinfo->txrate.mcs = mcs - 1;
1697 } else {
1698 sinfo->txrate.mcs = mcs;
1699 }
1700
1701 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1702 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1703 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001704 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1705 "invalid rate from stats: %d\n", rate);
1706 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001707 return 0;
1708 }
1709
1710 sinfo->filled |= STATION_INFO_TX_BITRATE;
1711
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301712 if (test_bit(CONNECTED, &vif->flags) &&
1713 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301714 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001715 sinfo->filled |= STATION_INFO_BSS_PARAM;
1716 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301717 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1718 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001719 }
1720
Kalle Valobdcd8172011-07-18 00:22:30 +03001721 return 0;
1722}
1723
1724static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1725 struct cfg80211_pmksa *pmksa)
1726{
1727 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301728 struct ath6kl_vif *vif = netdev_priv(netdev);
1729
1730 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001731 pmksa->pmkid, true);
1732}
1733
1734static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1735 struct cfg80211_pmksa *pmksa)
1736{
1737 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301738 struct ath6kl_vif *vif = netdev_priv(netdev);
1739
1740 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001741 pmksa->pmkid, false);
1742}
1743
1744static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1745{
1746 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301747 struct ath6kl_vif *vif = netdev_priv(netdev);
1748
1749 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301750 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1751 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001752 return 0;
1753}
1754
Raja Manid91e8ee2012-01-30 17:13:10 +05301755static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1756 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001757{
Raja Manid91e8ee2012-01-30 17:13:10 +05301758 int ret, pos;
1759 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001760 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001761
Raja Manid91e8ee2012-01-30 17:13:10 +05301762 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001763 for (i = 0; i < wow->n_patterns; i++) {
1764
1765 /*
1766 * Convert given nl80211 specific mask value to equivalent
1767 * driver specific mask value and send it to the chip along
1768 * with patterns. For example, If the mask value defined in
1769 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1770 * then equivalent driver specific mask value is
1771 * "0xFF 0x00 0xFF 0x00".
1772 */
1773 memset(&mask, 0, sizeof(mask));
1774 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1775 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1776 mask[pos] = 0xFF;
1777 }
1778 /*
1779 * Note: Pattern's offset is not passed as part of wowlan
1780 * parameter from CFG layer. So it's always passed as ZERO
1781 * to the firmware. It means, given WOW patterns are always
1782 * matched from the first byte of received pkt in the firmware.
1783 */
1784 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301785 vif->fw_vif_idx, WOW_LIST_ID,
1786 wow->patterns[i].pattern_len,
1787 0 /* pattern offset */,
1788 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001789 if (ret)
1790 return ret;
1791 }
1792
Raja Manid91e8ee2012-01-30 17:13:10 +05301793 if (wow->disconnect)
1794 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1795
1796 if (wow->magic_pkt)
1797 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1798
1799 if (wow->gtk_rekey_failure)
1800 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1801
1802 if (wow->eap_identity_req)
1803 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1804
1805 if (wow->four_way_handshake)
1806 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1807
1808 return 0;
1809}
1810
1811static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1812{
1813 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1814 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1815 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1816 0x00, 0x08 };
1817 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1818 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1819 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1820 0x00, 0x7f };
1821 u8 unicst_offset = 0;
1822 static const u8 arp_pattern[] = { 0x08, 0x06 };
1823 static const u8 arp_mask[] = { 0xff, 0xff };
1824 u8 arp_offset = 20;
1825 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1826 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1827 u8 discvr_offset = 38;
1828 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1829 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1830 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1831 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1832 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1833 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1834 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1835 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1836 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1837 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1838 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1839 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1840 u8 dhcp_offset = 0;
1841 int ret;
1842
1843 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1844 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1845 vif->fw_vif_idx, WOW_LIST_ID,
1846 sizeof(unicst_pattern), unicst_offset,
1847 unicst_pattern, unicst_mask);
1848 if (ret) {
1849 ath6kl_err("failed to add WOW unicast IP pattern\n");
1850 return ret;
1851 }
1852
1853 /* Setup all ARP pkt pattern */
1854 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1855 vif->fw_vif_idx, WOW_LIST_ID,
1856 sizeof(arp_pattern), arp_offset,
1857 arp_pattern, arp_mask);
1858 if (ret) {
1859 ath6kl_err("failed to add WOW ARP pattern\n");
1860 return ret;
1861 }
1862
1863 /*
1864 * Setup multicast pattern for mDNS 224.0.0.251,
1865 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1866 */
1867 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1868 vif->fw_vif_idx, WOW_LIST_ID,
1869 sizeof(discvr_pattern), discvr_offset,
1870 discvr_pattern, discvr_mask);
1871 if (ret) {
1872 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1873 return ret;
1874 }
1875
1876 /* Setup all DHCP broadcast pkt pattern */
1877 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1878 vif->fw_vif_idx, WOW_LIST_ID,
1879 sizeof(dhcp_pattern), dhcp_offset,
1880 dhcp_pattern, dhcp_mask);
1881 if (ret) {
1882 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
1883 return ret;
1884 }
1885
1886 return 0;
1887}
1888
1889static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
1890{
1891 struct net_device *ndev = vif->ndev;
1892 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1893 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1894 u8 discvr_offset = 38;
1895 u8 mac_mask[ETH_ALEN];
1896 int ret;
1897
1898 /* Setup unicast pkt pattern */
1899 memset(mac_mask, 0xff, ETH_ALEN);
1900 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1901 vif->fw_vif_idx, WOW_LIST_ID,
1902 ETH_ALEN, 0, ndev->dev_addr,
1903 mac_mask);
1904 if (ret) {
1905 ath6kl_err("failed to add WOW unicast pattern\n");
1906 return ret;
1907 }
1908
1909 /*
1910 * Setup multicast pattern for mDNS 224.0.0.251,
1911 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1912 */
1913 if ((ndev->flags & IFF_ALLMULTI) ||
1914 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
1915 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1916 vif->fw_vif_idx, WOW_LIST_ID,
1917 sizeof(discvr_pattern), discvr_offset,
1918 discvr_pattern, discvr_mask);
1919 if (ret) {
1920 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
1921 "pattern\n");
1922 return ret;
1923 }
1924 }
1925
1926 return 0;
1927}
1928
1929static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1930{
1931 struct in_device *in_dev;
1932 struct in_ifaddr *ifa;
1933 struct ath6kl_vif *vif;
1934 int ret, left;
1935 u32 filter = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +05301936 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05301937 u8 index = 0;
1938 __be32 ips[MAX_IP_ADDRS];
1939
1940 vif = ath6kl_vif_first(ar);
1941 if (!vif)
1942 return -EIO;
1943
1944 if (!ath6kl_cfg80211_ready(vif))
1945 return -EIO;
1946
1947 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05301948 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05301949
1950 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
1951 return -EINVAL;
1952
1953 /* Clear existing WOW patterns */
1954 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
1955 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
1956 WOW_LIST_ID, i);
1957
1958 /*
1959 * Skip the default WOW pattern configuration
1960 * if the driver receives any WOW patterns from
1961 * the user.
1962 */
1963 if (wow)
1964 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
1965 else if (vif->nw_type == AP_NETWORK)
1966 ret = ath6kl_wow_ap(ar, vif);
1967 else
1968 ret = ath6kl_wow_sta(ar, vif);
1969
1970 if (ret)
1971 return ret;
1972
Raja Mani390a8c82012-03-07 11:35:04 +05301973 netif_stop_queue(vif->ndev);
1974
Raja Manice0dc0c2012-02-20 19:08:08 +05301975 if (vif->nw_type != AP_NETWORK) {
1976 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
1977 ATH6KL_MAX_WOW_LISTEN_INTL,
1978 0);
1979 if (ret)
1980 return ret;
1981
1982 /* Set listen interval x 15 times as bmiss time */
1983 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
1984 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
1985 bmiss_time = ATH6KL_MAX_BMISS_TIME;
1986
1987 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
1988 bmiss_time, 0);
1989 if (ret)
1990 return ret;
1991
1992 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
1993 0xFFFF, 0, 0xFFFF, 0, 0, 0,
1994 0, 0, 0, 0);
1995 if (ret)
1996 return ret;
1997 }
1998
Raja Mani390a8c82012-03-07 11:35:04 +05301999 ar->state = ATH6KL_STATE_SUSPENDING;
2000
Raja Manic08631c2011-12-16 14:24:24 +05302001 /* Setup own IP addr for ARP agent. */
2002 in_dev = __in_dev_get_rtnl(vif->ndev);
2003 if (!in_dev)
2004 goto skip_arp;
2005
2006 ifa = in_dev->ifa_list;
2007 memset(&ips, 0, sizeof(ips));
2008
2009 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2010 while (index < MAX_IP_ADDRS && ifa) {
2011 ips[index] = ifa->ifa_local;
2012 ifa = ifa->ifa_next;
2013 index++;
2014 }
2015
2016 if (ifa) {
2017 ath6kl_err("total IP addr count is exceeding fw limit\n");
2018 return -EINVAL;
2019 }
2020
2021 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2022 if (ret) {
2023 ath6kl_err("fail to setup ip for arp agent\n");
2024 return ret;
2025 }
2026
2027skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02002028 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2029 ATH6KL_WOW_MODE_ENABLE,
2030 filter,
2031 WOW_HOST_REQ_DELAY);
2032 if (ret)
2033 return ret;
2034
Raja Mani081c7a82012-01-30 17:13:11 +05302035 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
2036
Raja Mani6cb3c712011-11-07 22:52:45 +02002037 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2038 ATH6KL_HOST_MODE_ASLEEP);
2039 if (ret)
2040 return ret;
2041
Raja Mani081c7a82012-01-30 17:13:11 +05302042 left = wait_event_interruptible_timeout(ar->event_wq,
2043 test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags),
2044 WMI_TIMEOUT);
2045 if (left == 0) {
2046 ath6kl_warn("timeout, didn't get host sleep cmd "
2047 "processed event\n");
2048 ret = -ETIMEDOUT;
2049 } else if (left < 0) {
2050 ath6kl_warn("error while waiting for host sleep cmd "
2051 "processed event %d\n", left);
2052 ret = left;
2053 }
2054
Raja Mani6cb3c712011-11-07 22:52:45 +02002055 if (ar->tx_pending[ar->ctrl_ep]) {
2056 left = wait_event_interruptible_timeout(ar->event_wq,
2057 ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
2058 if (left == 0) {
2059 ath6kl_warn("clear wmi ctrl data timeout\n");
2060 ret = -ETIMEDOUT;
2061 } else if (left < 0) {
2062 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
2063 ret = left;
2064 }
2065 }
2066
2067 return ret;
2068}
2069
2070static int ath6kl_wow_resume(struct ath6kl *ar)
2071{
2072 struct ath6kl_vif *vif;
2073 int ret;
2074
2075 vif = ath6kl_vif_first(ar);
2076 if (!vif)
2077 return -EIO;
2078
Raja Mani390a8c82012-03-07 11:35:04 +05302079 ar->state = ATH6KL_STATE_RESUMING;
2080
Raja Mani6cb3c712011-11-07 22:52:45 +02002081 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2082 ATH6KL_HOST_MODE_AWAKE);
Raja Mani390a8c82012-03-07 11:35:04 +05302083 if (ret) {
2084 ath6kl_warn("Failed to configure host sleep mode for "
2085 "wow resume: %d\n", ret);
2086 ar->state = ATH6KL_STATE_WOW;
2087 return ret;
2088 }
2089
Raja Manice0dc0c2012-02-20 19:08:08 +05302090 if (vif->nw_type != AP_NETWORK) {
2091 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2092 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2093 if (ret)
2094 return ret;
2095
2096 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2097 vif->listen_intvl_t, 0);
2098 if (ret)
2099 return ret;
2100
2101 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2102 vif->bmiss_time_t, 0);
2103 if (ret)
2104 return ret;
2105 }
2106
Raja Mani390a8c82012-03-07 11:35:04 +05302107 ar->state = ATH6KL_STATE_ON;
2108
2109 netif_wake_queue(vif->ndev);
2110
2111 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002112}
2113
Kalle Valo52d81a62011-11-01 08:44:21 +02002114int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002115 enum ath6kl_cfg_suspend_mode mode,
2116 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002117{
Raja Mani390a8c82012-03-07 11:35:04 +05302118 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002119 int ret;
2120
Kalle Valo52d81a62011-11-01 08:44:21 +02002121 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002122 case ATH6KL_CFG_SUSPEND_WOW:
2123
2124 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2125
2126 /* Flush all non control pkts in TX path */
2127 ath6kl_tx_data_cleanup(ar);
2128
Raja Mani390a8c82012-03-07 11:35:04 +05302129 prev_state = ar->state;
2130
Raja Manid7c44e02011-11-07 22:52:46 +02002131 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302132 if (ret) {
2133 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002134 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302135 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302136
Raja Manid7c44e02011-11-07 22:52:46 +02002137 ar->state = ATH6KL_STATE_WOW;
2138 break;
2139
Kalle Valo52d81a62011-11-01 08:44:21 +02002140 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002141
Kalle Valo7125f012011-12-13 14:51:37 +02002142 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002143
Kalle Valo52d81a62011-11-01 08:44:21 +02002144 /* save the current power mode before enabling power save */
2145 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2146
2147 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2148 if (ret) {
2149 ath6kl_warn("wmi powermode command failed during suspend: %d\n",
2150 ret);
2151 }
2152
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002153 ar->state = ATH6KL_STATE_DEEPSLEEP;
2154
Kalle Valo52d81a62011-11-01 08:44:21 +02002155 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002156
2157 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002158
Kalle Valo7125f012011-12-13 14:51:37 +02002159 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002160
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002161 if (ar->state == ATH6KL_STATE_OFF) {
2162 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2163 "suspend hw off, no action for cutpower\n");
2164 break;
2165 }
2166
2167 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2168
2169 ret = ath6kl_init_hw_stop(ar);
2170 if (ret) {
2171 ath6kl_warn("failed to stop hw during suspend: %d\n",
2172 ret);
2173 }
2174
2175 ar->state = ATH6KL_STATE_CUTPOWER;
2176
2177 break;
2178
Kalle Valo10509f92011-12-13 14:52:07 +02002179 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2180 /*
2181 * Nothing needed for schedule scan, firmware is already in
2182 * wow mode and sleeping most of the time.
2183 */
2184 break;
2185
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002186 default:
2187 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002188 }
2189
2190 return 0;
2191}
Kalle Valod6a434d2012-01-17 20:09:36 +02002192EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002193
2194int ath6kl_cfg80211_resume(struct ath6kl *ar)
2195{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002196 int ret;
2197
2198 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002199 case ATH6KL_STATE_WOW:
2200 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2201
2202 ret = ath6kl_wow_resume(ar);
2203 if (ret) {
2204 ath6kl_warn("wow mode resume failed: %d\n", ret);
2205 return ret;
2206 }
2207
Raja Manid7c44e02011-11-07 22:52:46 +02002208 break;
2209
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002210 case ATH6KL_STATE_DEEPSLEEP:
2211 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2212 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2213 ar->wmi->saved_pwr_mode);
2214 if (ret) {
2215 ath6kl_warn("wmi powermode command failed during resume: %d\n",
2216 ret);
2217 }
2218 }
2219
2220 ar->state = ATH6KL_STATE_ON;
2221
2222 break;
2223
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002224 case ATH6KL_STATE_CUTPOWER:
2225 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2226
2227 ret = ath6kl_init_hw_start(ar);
2228 if (ret) {
2229 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2230 return ret;
2231 }
Raja Manid7c44e02011-11-07 22:52:46 +02002232 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002233
Kalle Valo10509f92011-12-13 14:52:07 +02002234 case ATH6KL_STATE_SCHED_SCAN:
2235 break;
2236
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002237 default:
2238 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002239 }
2240
2241 return 0;
2242}
Kalle Valod6a434d2012-01-17 20:09:36 +02002243EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002244
Kalle Valoabcb3442011-07-22 08:26:20 +03002245#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002246
2247/* hif layer decides what suspend mode to use */
2248static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002249 struct cfg80211_wowlan *wow)
2250{
2251 struct ath6kl *ar = wiphy_priv(wiphy);
2252
Raja Mani0f60e9f2011-11-07 22:52:45 +02002253 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002254}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002255
Kalle Valo52d81a62011-11-01 08:44:21 +02002256static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002257{
2258 struct ath6kl *ar = wiphy_priv(wiphy);
2259
2260 return ath6kl_hif_resume(ar);
2261}
Raja Mania918fb32011-11-07 22:52:46 +02002262
2263/*
2264 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2265 * both sdio irq wake up and keep power. The target pulls sdio data line to
2266 * wake up the host when WOW pattern matches. This causes sdio irq handler
2267 * is being called in the host side which internally hits ath6kl's RX path.
2268 *
2269 * Since sdio interrupt is not disabled, RX path executes even before
2270 * the host executes the actual resume operation from PM module.
2271 *
2272 * In the current scenario, WOW resume should happen before start processing
2273 * any data from the target. So It's required to perform WOW resume in RX path.
2274 * Ideally we should perform WOW resume only in the actual platform
2275 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2276 *
2277 * ath6kl_check_wow_status() is called from ath6kl_rx().
2278 */
2279void ath6kl_check_wow_status(struct ath6kl *ar)
2280{
Raja Mani390a8c82012-03-07 11:35:04 +05302281 if (ar->state == ATH6KL_STATE_SUSPENDING)
2282 return;
2283
Raja Mania918fb32011-11-07 22:52:46 +02002284 if (ar->state == ATH6KL_STATE_WOW)
2285 ath6kl_cfg80211_resume(ar);
2286}
2287
2288#else
2289
2290void ath6kl_check_wow_status(struct ath6kl *ar)
2291{
2292}
Kalle Valoabcb3442011-07-22 08:26:20 +03002293#endif
2294
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002295static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2296 struct ieee80211_channel *chan,
2297 enum nl80211_channel_type channel_type)
2298{
Sujith Manoharane68f6752011-12-22 12:15:27 +05302299 struct ath6kl_vif *vif;
2300
2301 /*
2302 * 'dev' could be NULL if a channel change is required for the hardware
2303 * device itself, instead of a particular VIF.
2304 *
2305 * FIXME: To be handled properly when monitor mode is supported.
2306 */
2307 if (!dev)
2308 return -EBUSY;
2309
2310 vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002311
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302312 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002313 return -EIO;
2314
2315 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2316 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302317 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002318
2319 return 0;
2320}
2321
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002322static bool ath6kl_is_p2p_ie(const u8 *pos)
2323{
2324 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2325 pos[2] == 0x50 && pos[3] == 0x6f &&
2326 pos[4] == 0x9a && pos[5] == 0x09;
2327}
2328
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302329static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2330 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002331{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302332 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002333 const u8 *pos;
2334 u8 *buf = NULL;
2335 size_t len = 0;
2336 int ret;
2337
2338 /*
2339 * Filter out P2P IE(s) since they will be included depending on
2340 * the Probe Request frame in ath6kl_send_go_probe_resp().
2341 */
2342
2343 if (ies && ies_len) {
2344 buf = kmalloc(ies_len, GFP_KERNEL);
2345 if (buf == NULL)
2346 return -ENOMEM;
2347 pos = ies;
2348 while (pos + 1 < ies + ies_len) {
2349 if (pos + 2 + pos[1] > ies + ies_len)
2350 break;
2351 if (!ath6kl_is_p2p_ie(pos)) {
2352 memcpy(buf + len, pos, 2 + pos[1]);
2353 len += 2 + pos[1];
2354 }
2355 pos += 2 + pos[1];
2356 }
2357 }
2358
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302359 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2360 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002361 kfree(buf);
2362 return ret;
2363}
2364
Johannes Berg88600202012-02-13 15:17:18 +01002365static int ath6kl_set_ies(struct ath6kl_vif *vif,
2366 struct cfg80211_beacon_data *info)
2367{
2368 struct ath6kl *ar = vif->ar;
2369 int res;
2370
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002371 /* this also clears IE in fw if it's not set */
2372 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2373 WMI_FRAME_BEACON,
2374 info->beacon_ies,
2375 info->beacon_ies_len);
2376 if (res)
2377 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002378
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002379 /* this also clears IE in fw if it's not set */
2380 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2381 info->proberesp_ies_len);
2382 if (res)
2383 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002384
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002385 /* this also clears IE in fw if it's not set */
2386 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2387 WMI_FRAME_ASSOC_RESP,
2388 info->assocresp_ies,
2389 info->assocresp_ies_len);
2390 if (res)
2391 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002392
2393 return 0;
2394}
2395
2396static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2397 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002398{
2399 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302400 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002401 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002402 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002403 u8 *ies;
2404 int ies_len;
2405 struct wmi_connect_cmd p;
2406 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302407 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002408
Johannes Berg88600202012-02-13 15:17:18 +01002409 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002410
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302411 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002412 return -EIO;
2413
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302414 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002415 return -EOPNOTSUPP;
2416
Johannes Berg88600202012-02-13 15:17:18 +01002417 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002418
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002419 ar->ap_mode_bkey.valid = false;
2420
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002421 /* TODO:
2422 * info->interval
2423 * info->dtim_period
2424 */
2425
Johannes Berg88600202012-02-13 15:17:18 +01002426 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002427 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002428 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002429 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002430 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002431 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002432 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002433
2434 if (info->ssid == NULL)
2435 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302436 memcpy(vif->ssid, info->ssid, info->ssid_len);
2437 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002438 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002439 hidden = true;
2440
2441 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2442 if (res)
2443 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002444
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302445 ret = ath6kl_set_auth_type(vif, info->auth_type);
2446 if (ret)
2447 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002448
2449 memset(&p, 0, sizeof(p));
2450
2451 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2452 switch (info->crypto.akm_suites[i]) {
2453 case WLAN_AKM_SUITE_8021X:
2454 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2455 p.auth_mode |= WPA_AUTH;
2456 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2457 p.auth_mode |= WPA2_AUTH;
2458 break;
2459 case WLAN_AKM_SUITE_PSK:
2460 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2461 p.auth_mode |= WPA_PSK_AUTH;
2462 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2463 p.auth_mode |= WPA2_PSK_AUTH;
2464 break;
2465 }
2466 }
2467 if (p.auth_mode == 0)
2468 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302469 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002470
2471 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2472 switch (info->crypto.ciphers_pairwise[i]) {
2473 case WLAN_CIPHER_SUITE_WEP40:
2474 case WLAN_CIPHER_SUITE_WEP104:
2475 p.prwise_crypto_type |= WEP_CRYPT;
2476 break;
2477 case WLAN_CIPHER_SUITE_TKIP:
2478 p.prwise_crypto_type |= TKIP_CRYPT;
2479 break;
2480 case WLAN_CIPHER_SUITE_CCMP:
2481 p.prwise_crypto_type |= AES_CRYPT;
2482 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002483 case WLAN_CIPHER_SUITE_SMS4:
2484 p.prwise_crypto_type |= WAPI_CRYPT;
2485 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002486 }
2487 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002488 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002489 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302490 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002491 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302492 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002493
2494 switch (info->crypto.cipher_group) {
2495 case WLAN_CIPHER_SUITE_WEP40:
2496 case WLAN_CIPHER_SUITE_WEP104:
2497 p.grp_crypto_type = WEP_CRYPT;
2498 break;
2499 case WLAN_CIPHER_SUITE_TKIP:
2500 p.grp_crypto_type = TKIP_CRYPT;
2501 break;
2502 case WLAN_CIPHER_SUITE_CCMP:
2503 p.grp_crypto_type = AES_CRYPT;
2504 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002505 case WLAN_CIPHER_SUITE_SMS4:
2506 p.grp_crypto_type = WAPI_CRYPT;
2507 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002508 default:
2509 p.grp_crypto_type = NONE_CRYPT;
2510 break;
2511 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302512 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002513
2514 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302515 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002516
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302517 p.ssid_len = vif->ssid_len;
2518 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2519 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302520 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002521
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302522 /* Enable uAPSD support by default */
2523 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2524 if (res < 0)
2525 return res;
2526
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002527 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2528 p.nw_subtype = SUBTYPE_P2PGO;
2529 } else {
2530 /*
2531 * Due to firmware limitation, it is not possible to
2532 * do P2P mgmt operations in AP mode
2533 */
2534 p.nw_subtype = SUBTYPE_NONE;
2535 }
2536
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302537 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002538 if (res < 0)
2539 return res;
2540
2541 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002542}
2543
Johannes Berg88600202012-02-13 15:17:18 +01002544static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2545 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002546{
Johannes Berg88600202012-02-13 15:17:18 +01002547 struct ath6kl_vif *vif = netdev_priv(dev);
2548
2549 if (!ath6kl_cfg80211_ready(vif))
2550 return -EIO;
2551
2552 if (vif->next_mode != AP_NETWORK)
2553 return -EOPNOTSUPP;
2554
2555 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002556}
2557
Johannes Berg88600202012-02-13 15:17:18 +01002558static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002559{
2560 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302561 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002562
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302563 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002564 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302565 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002566 return -ENOTCONN;
2567
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302568 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302569 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002570
2571 return 0;
2572}
2573
Jouni Malinen33e53082011-12-27 11:02:56 +02002574static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2575
2576static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2577 u8 *mac)
2578{
2579 struct ath6kl *ar = ath6kl_priv(dev);
2580 struct ath6kl_vif *vif = netdev_priv(dev);
2581 const u8 *addr = mac ? mac : bcast_addr;
2582
2583 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2584 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2585}
2586
Jouni Malinen23875132011-08-30 21:57:53 +03002587static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2588 u8 *mac, struct station_parameters *params)
2589{
2590 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302591 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002592
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302593 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002594 return -EOPNOTSUPP;
2595
2596 /* Use this only for authorizing/unauthorizing a station */
2597 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2598 return -EOPNOTSUPP;
2599
2600 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302601 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2602 WMI_AP_MLME_AUTHORIZE, mac, 0);
2603 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2604 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002605}
2606
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002607static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2608 struct net_device *dev,
2609 struct ieee80211_channel *chan,
2610 enum nl80211_channel_type channel_type,
2611 unsigned int duration,
2612 u64 *cookie)
2613{
2614 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302615 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002616 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002617
2618 /* TODO: if already pending or ongoing remain-on-channel,
2619 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002620 id = ++vif->last_roc_id;
2621 if (id == 0) {
2622 /* Do not use 0 as the cookie value */
2623 id = ++vif->last_roc_id;
2624 }
2625 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002626
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302627 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2628 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002629}
2630
2631static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2632 struct net_device *dev,
2633 u64 cookie)
2634{
2635 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302636 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002637
Jouni Malinen10522612011-10-27 16:00:13 +03002638 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002639 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002640 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002641
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302642 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002643}
2644
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302645static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2646 const u8 *buf, size_t len,
2647 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002648{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302649 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002650 const u8 *pos;
2651 u8 *p2p;
2652 int p2p_len;
2653 int ret;
2654 const struct ieee80211_mgmt *mgmt;
2655
2656 mgmt = (const struct ieee80211_mgmt *) buf;
2657
2658 /* Include P2P IE(s) from the frame generated in user space. */
2659
2660 p2p = kmalloc(len, GFP_KERNEL);
2661 if (p2p == NULL)
2662 return -ENOMEM;
2663 p2p_len = 0;
2664
2665 pos = mgmt->u.probe_resp.variable;
2666 while (pos + 1 < buf + len) {
2667 if (pos + 2 + pos[1] > buf + len)
2668 break;
2669 if (ath6kl_is_p2p_ie(pos)) {
2670 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2671 p2p_len += 2 + pos[1];
2672 }
2673 pos += 2 + pos[1];
2674 }
2675
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302676 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2677 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002678 kfree(p2p);
2679 return ret;
2680}
2681
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002682static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
2683 u32 id,
2684 u32 freq,
2685 u32 wait,
2686 const u8 *buf,
2687 size_t len,
2688 bool *more_data,
2689 bool no_cck)
2690{
2691 struct ieee80211_mgmt *mgmt;
2692 struct ath6kl_sta *conn;
2693 bool is_psq_empty = false;
2694 struct ath6kl_mgmt_buff *mgmt_buf;
2695 size_t mgmt_buf_size;
2696 struct ath6kl *ar = vif->ar;
2697
2698 mgmt = (struct ieee80211_mgmt *) buf;
2699 if (is_multicast_ether_addr(mgmt->da))
2700 return false;
2701
2702 conn = ath6kl_find_sta(vif, mgmt->da);
2703 if (!conn)
2704 return false;
2705
2706 if (conn->sta_flags & STA_PS_SLEEP) {
2707 if (!(conn->sta_flags & STA_PS_POLLED)) {
2708 /* Queue the frames if the STA is sleeping */
2709 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
2710 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
2711 if (!mgmt_buf)
2712 return false;
2713
2714 INIT_LIST_HEAD(&mgmt_buf->list);
2715 mgmt_buf->id = id;
2716 mgmt_buf->freq = freq;
2717 mgmt_buf->wait = wait;
2718 mgmt_buf->len = len;
2719 mgmt_buf->no_cck = no_cck;
2720 memcpy(mgmt_buf->buf, buf, len);
2721 spin_lock_bh(&conn->psq_lock);
2722 is_psq_empty = skb_queue_empty(&conn->psq) &&
2723 (conn->mgmt_psq_len == 0);
2724 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
2725 conn->mgmt_psq_len++;
2726 spin_unlock_bh(&conn->psq_lock);
2727
2728 /*
2729 * If this is the first pkt getting queued
2730 * for this STA, update the PVB for this
2731 * STA.
2732 */
2733 if (is_psq_empty)
2734 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
2735 conn->aid, 1);
2736 return true;
2737 }
2738
2739 /*
2740 * This tx is because of a PsPoll.
2741 * Determine if MoreData bit has to be set.
2742 */
2743 spin_lock_bh(&conn->psq_lock);
2744 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
2745 *more_data = true;
2746 spin_unlock_bh(&conn->psq_lock);
2747 }
2748
2749 return false;
2750}
2751
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002752static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2753 struct ieee80211_channel *chan, bool offchan,
2754 enum nl80211_channel_type channel_type,
2755 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002756 const u8 *buf, size_t len, bool no_cck,
2757 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002758{
2759 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302760 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002761 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002762 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002763 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002764
2765 mgmt = (const struct ieee80211_mgmt *) buf;
2766 if (buf + len >= mgmt->u.probe_resp.variable &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302767 vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002768 ieee80211_is_probe_resp(mgmt->frame_control)) {
2769 /*
2770 * Send Probe Response frame in AP mode using a separate WMI
2771 * command to allow the target to fill in the generic IEs.
2772 */
2773 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302774 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002775 chan->center_freq);
2776 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002777
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302778 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002779 if (id == 0) {
2780 /*
2781 * 0 is a reserved value in the WMI command and shall not be
2782 * used for the command.
2783 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302784 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002785 }
2786
2787 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002788
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002789 /* AP mode Power saving processing */
2790 if (vif->nw_type == AP_NETWORK) {
2791 queued = ath6kl_mgmt_powersave_ap(vif,
2792 id, chan->center_freq,
2793 wait, buf,
2794 len, &more_data, no_cck);
2795 if (queued)
2796 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002797 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002798
2799 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2800 chan->center_freq, wait,
2801 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002802}
2803
Jouni Malinenae32c302011-08-30 21:58:01 +03002804static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2805 struct net_device *dev,
2806 u16 frame_type, bool reg)
2807{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302808 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002809
2810 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2811 __func__, frame_type, reg);
2812 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2813 /*
2814 * Note: This notification callback is not allowed to sleep, so
2815 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2816 * hardcode target to report Probe Request frames all the time.
2817 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302818 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002819 }
2820}
2821
Kalle Valo10509f92011-12-13 14:52:07 +02002822static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2823 struct net_device *dev,
2824 struct cfg80211_sched_scan_request *request)
2825{
2826 struct ath6kl *ar = ath6kl_priv(dev);
2827 struct ath6kl_vif *vif = netdev_priv(dev);
2828 u16 interval;
2829 int ret;
2830 u8 i;
2831
2832 if (ar->state != ATH6KL_STATE_ON)
2833 return -EIO;
2834
2835 if (vif->sme_state != SME_DISCONNECTED)
2836 return -EBUSY;
2837
2838 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2839 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2840 i, DISABLE_SSID_FLAG,
2841 0, NULL);
2842 }
2843
2844 /* fw uses seconds, also make sure that it's >0 */
2845 interval = max_t(u16, 1, request->interval / 1000);
2846
2847 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2848 interval, interval,
2849 10, 0, 0, 0, 3, 0, 0, 0);
2850
2851 if (request->n_ssids && request->ssids[0].ssid_len) {
2852 for (i = 0; i < request->n_ssids; i++) {
2853 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2854 i, SPECIFIC_SSID_FLAG,
2855 request->ssids[i].ssid_len,
2856 request->ssids[i].ssid);
2857 }
2858 }
2859
2860 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2861 ATH6KL_WOW_MODE_ENABLE,
2862 WOW_FILTER_SSID,
2863 WOW_HOST_REQ_DELAY);
2864 if (ret) {
2865 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2866 return ret;
2867 }
2868
2869 /* this also clears IE in fw if it's not set */
2870 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2871 WMI_FRAME_PROBE_REQ,
2872 request->ie, request->ie_len);
2873 if (ret) {
2874 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
2875 ret);
2876 return ret;
2877 }
2878
2879 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2880 ATH6KL_HOST_MODE_ASLEEP);
2881 if (ret) {
2882 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
2883 ret);
2884 return ret;
2885 }
2886
2887 ar->state = ATH6KL_STATE_SCHED_SCAN;
2888
2889 return ret;
2890}
2891
2892static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
2893 struct net_device *dev)
2894{
2895 struct ath6kl_vif *vif = netdev_priv(dev);
2896 bool stopped;
2897
2898 stopped = __ath6kl_cfg80211_sscan_stop(vif);
2899
2900 if (!stopped)
2901 return -EIO;
2902
2903 return 0;
2904}
2905
Jouni Malinenf80574a2011-08-30 21:58:04 +03002906static const struct ieee80211_txrx_stypes
2907ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2908 [NL80211_IFTYPE_STATION] = {
2909 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2910 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2911 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2912 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2913 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02002914 [NL80211_IFTYPE_AP] = {
2915 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2916 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2917 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2918 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2919 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03002920 [NL80211_IFTYPE_P2P_CLIENT] = {
2921 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2922 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2923 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2924 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2925 },
2926 [NL80211_IFTYPE_P2P_GO] = {
2927 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2928 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2929 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2930 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2931 },
2932};
2933
Kalle Valobdcd8172011-07-18 00:22:30 +03002934static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302935 .add_virtual_intf = ath6kl_cfg80211_add_iface,
2936 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03002937 .change_virtual_intf = ath6kl_cfg80211_change_iface,
2938 .scan = ath6kl_cfg80211_scan,
2939 .connect = ath6kl_cfg80211_connect,
2940 .disconnect = ath6kl_cfg80211_disconnect,
2941 .add_key = ath6kl_cfg80211_add_key,
2942 .get_key = ath6kl_cfg80211_get_key,
2943 .del_key = ath6kl_cfg80211_del_key,
2944 .set_default_key = ath6kl_cfg80211_set_default_key,
2945 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
2946 .set_tx_power = ath6kl_cfg80211_set_txpower,
2947 .get_tx_power = ath6kl_cfg80211_get_txpower,
2948 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
2949 .join_ibss = ath6kl_cfg80211_join_ibss,
2950 .leave_ibss = ath6kl_cfg80211_leave_ibss,
2951 .get_station = ath6kl_get_station,
2952 .set_pmksa = ath6kl_set_pmksa,
2953 .del_pmksa = ath6kl_del_pmksa,
2954 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03002955 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03002956#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002957 .suspend = __ath6kl_cfg80211_suspend,
2958 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03002959#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002960 .set_channel = ath6kl_set_channel,
Johannes Berg88600202012-02-13 15:17:18 +01002961 .start_ap = ath6kl_start_ap,
2962 .change_beacon = ath6kl_change_beacon,
2963 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02002964 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03002965 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002966 .remain_on_channel = ath6kl_remain_on_channel,
2967 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002968 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03002969 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02002970 .sched_scan_start = ath6kl_cfg80211_sscan_start,
2971 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03002972};
2973
Kalle Valo7125f012011-12-13 14:51:37 +02002974void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02002975{
Kalle Valo10509f92011-12-13 14:52:07 +02002976 ath6kl_cfg80211_sscan_disable(vif);
2977
Kalle Valoec4b7f62011-11-01 08:44:04 +02002978 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02002979 case SME_DISCONNECTED:
2980 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02002981 case SME_CONNECTING:
2982 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
2983 NULL, 0,
2984 WLAN_STATUS_UNSPECIFIED_FAILURE,
2985 GFP_KERNEL);
2986 break;
2987 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02002988 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
2989 break;
2990 }
2991
2992 if (test_bit(CONNECTED, &vif->flags) ||
2993 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02002994 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002995
2996 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02002997 clear_bit(CONNECTED, &vif->flags);
2998 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002999
3000 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02003001 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
3002 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
3003 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003004
3005 ath6kl_cfg80211_scan_complete_event(vif, true);
3006}
3007
Kalle Valo7125f012011-12-13 14:51:37 +02003008void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3009{
3010 struct ath6kl_vif *vif;
3011
3012 vif = ath6kl_vif_first(ar);
3013 if (!vif) {
3014 /* save the current power mode before enabling power save */
3015 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3016
3017 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
3018 ath6kl_warn("ath6kl_deep_sleep_enable: "
3019 "wmi_powermode_cmd failed\n");
3020 return;
3021 }
3022
3023 /*
3024 * FIXME: we should take ar->list_lock to protect changes in the
3025 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3026 * sleeps.
3027 */
3028 list_for_each_entry(vif, &ar->vif_list, list)
3029 ath6kl_cfg80211_stop(vif);
3030}
3031
Kalle Valoc25889e2012-01-17 20:08:27 +02003032static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003033{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303034 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303035 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303036 ath6kl_err("failed to initialize aggr\n");
3037 return -ENOMEM;
3038 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003039
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303040 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303041 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003042 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3043 (unsigned long) vif);
3044
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303045 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303046 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303047
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303048 INIT_LIST_HEAD(&vif->mc_filter);
3049
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303050 return 0;
3051}
3052
Kalle Valoc25889e2012-01-17 20:08:27 +02003053void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303054{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303055 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303056 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303057
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303058 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303059
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303060 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3061
3062 if (vif->nw_type == ADHOC_NETWORK)
3063 ar->ibss_if_active = false;
3064
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303065 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3066 list_del(&mc_filter->list);
3067 kfree(mc_filter);
3068 }
3069
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303070 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303071
3072 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303073}
3074
3075struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303076 enum nl80211_iftype type, u8 fw_vif_idx,
3077 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303078{
3079 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303080 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303081
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303082 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303083 if (!ndev)
3084 return NULL;
3085
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303086 vif = netdev_priv(ndev);
3087 ndev->ieee80211_ptr = &vif->wdev;
3088 vif->wdev.wiphy = ar->wiphy;
3089 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303090 vif->ndev = ndev;
3091 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3092 vif->wdev.netdev = ndev;
3093 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303094 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003095 vif->nw_type = nw_type;
3096 vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303097 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303098 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303099
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303100 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
3101 if (fw_vif_idx != 0)
3102 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3103 0x2;
3104
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303105 init_netdev(ndev);
3106
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303107 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303108
Kalle Valoc25889e2012-01-17 20:08:27 +02003109 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303110 goto err;
3111
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303112 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303113 goto err;
3114
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303115 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303116 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303117 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303118 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303119 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303120
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303121 if (type == NL80211_IFTYPE_ADHOC)
3122 ar->ibss_if_active = true;
3123
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303124 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303125 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303126 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303127
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303128 return ndev;
3129
3130err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303131 aggr_module_destroy(vif->aggr_cntxt);
3132 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303133 return NULL;
3134}
3135
Kalle Valo46d33a22012-01-17 20:08:40 +02003136int ath6kl_cfg80211_init(struct ath6kl *ar)
3137{
3138 struct wiphy *wiphy = ar->wiphy;
3139 int ret;
3140
3141 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3142
3143 wiphy->max_remain_on_channel_duration = 5000;
3144
3145 /* set device pointer for wiphy */
3146 set_wiphy_dev(wiphy, ar->dev);
3147
3148 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3149 BIT(NL80211_IFTYPE_ADHOC) |
3150 BIT(NL80211_IFTYPE_AP);
3151 if (ar->p2p) {
3152 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3153 BIT(NL80211_IFTYPE_P2P_CLIENT);
3154 }
3155
3156 /* max num of ssids that can be probed during scanning */
3157 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
3158 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
3159 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3160 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3161 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3162
3163 wiphy->cipher_suites = cipher_suites;
3164 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3165
3166 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3167 WIPHY_WOWLAN_DISCONNECT |
3168 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3169 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3170 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3171 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3172 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3173 wiphy->wowlan.pattern_min_len = 1;
3174 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
3175
3176 wiphy->max_sched_scan_ssids = 10;
3177
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303178 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3179 WIPHY_FLAG_HAVE_AP_SME |
3180 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3181 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3182
3183 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3184 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3185
3186 ar->wiphy->probe_resp_offload =
3187 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3188 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
3189 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
3190 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
3191
Kalle Valo46d33a22012-01-17 20:08:40 +02003192 ret = wiphy_register(wiphy);
3193 if (ret < 0) {
3194 ath6kl_err("couldn't register wiphy device\n");
3195 return ret;
3196 }
3197
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303198 ar->wiphy_registered = true;
3199
Kalle Valo46d33a22012-01-17 20:08:40 +02003200 return 0;
3201}
3202
3203void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303204{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303205 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303206
3207 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003208}
Kalle Valo46d33a22012-01-17 20:08:40 +02003209
Kalle Valo45eaa782012-01-17 20:09:05 +02003210struct ath6kl *ath6kl_cfg80211_create(void)
3211{
3212 struct ath6kl *ar;
3213 struct wiphy *wiphy;
3214
3215 /* create a new wiphy for use with cfg80211 */
3216 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3217
3218 if (!wiphy) {
3219 ath6kl_err("couldn't allocate wiphy device\n");
3220 return NULL;
3221 }
3222
3223 ar = wiphy_priv(wiphy);
3224 ar->wiphy = wiphy;
3225
3226 return ar;
3227}
3228
3229/* Note: ar variable must not be accessed after calling this! */
3230void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3231{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303232 int i;
3233
3234 for (i = 0; i < AP_MAX_NUM_STA; i++)
3235 kfree(ar->sta_list[i].aggr_conn);
3236
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303237 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003238}
Kalle Valo45eaa782012-01-17 20:09:05 +02003239