blob: 48d414acefeef102907a2762ae1ab5da3b0f39ca [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "core.h"
18#include "cfg80211.h"
19#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030020#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030021#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030022
Jouni Malinen6bbc7c32011-09-05 17:38:47 +030023static unsigned int ath6kl_p2p;
24
25module_param(ath6kl_p2p, uint, 0644);
26
Kalle Valobdcd8172011-07-18 00:22:30 +030027#define RATETAB_ENT(_rate, _rateid, _flags) { \
28 .bitrate = (_rate), \
29 .flags = (_flags), \
30 .hw_value = (_rateid), \
31}
32
33#define CHAN2G(_channel, _freq, _flags) { \
34 .band = IEEE80211_BAND_2GHZ, \
35 .hw_value = (_channel), \
36 .center_freq = (_freq), \
37 .flags = (_flags), \
38 .max_antenna_gain = 0, \
39 .max_power = 30, \
40}
41
42#define CHAN5G(_channel, _flags) { \
43 .band = IEEE80211_BAND_5GHZ, \
44 .hw_value = (_channel), \
45 .center_freq = 5000 + (5 * (_channel)), \
46 .flags = (_flags), \
47 .max_antenna_gain = 0, \
48 .max_power = 30, \
49}
50
51static struct ieee80211_rate ath6kl_rates[] = {
52 RATETAB_ENT(10, 0x1, 0),
53 RATETAB_ENT(20, 0x2, 0),
54 RATETAB_ENT(55, 0x4, 0),
55 RATETAB_ENT(110, 0x8, 0),
56 RATETAB_ENT(60, 0x10, 0),
57 RATETAB_ENT(90, 0x20, 0),
58 RATETAB_ENT(120, 0x40, 0),
59 RATETAB_ENT(180, 0x80, 0),
60 RATETAB_ENT(240, 0x100, 0),
61 RATETAB_ENT(360, 0x200, 0),
62 RATETAB_ENT(480, 0x400, 0),
63 RATETAB_ENT(540, 0x800, 0),
64};
65
66#define ath6kl_a_rates (ath6kl_rates + 4)
67#define ath6kl_a_rates_size 8
68#define ath6kl_g_rates (ath6kl_rates + 0)
69#define ath6kl_g_rates_size 12
70
71static struct ieee80211_channel ath6kl_2ghz_channels[] = {
72 CHAN2G(1, 2412, 0),
73 CHAN2G(2, 2417, 0),
74 CHAN2G(3, 2422, 0),
75 CHAN2G(4, 2427, 0),
76 CHAN2G(5, 2432, 0),
77 CHAN2G(6, 2437, 0),
78 CHAN2G(7, 2442, 0),
79 CHAN2G(8, 2447, 0),
80 CHAN2G(9, 2452, 0),
81 CHAN2G(10, 2457, 0),
82 CHAN2G(11, 2462, 0),
83 CHAN2G(12, 2467, 0),
84 CHAN2G(13, 2472, 0),
85 CHAN2G(14, 2484, 0),
86};
87
88static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
89 CHAN5G(34, 0), CHAN5G(36, 0),
90 CHAN5G(38, 0), CHAN5G(40, 0),
91 CHAN5G(42, 0), CHAN5G(44, 0),
92 CHAN5G(46, 0), CHAN5G(48, 0),
93 CHAN5G(52, 0), CHAN5G(56, 0),
94 CHAN5G(60, 0), CHAN5G(64, 0),
95 CHAN5G(100, 0), CHAN5G(104, 0),
96 CHAN5G(108, 0), CHAN5G(112, 0),
97 CHAN5G(116, 0), CHAN5G(120, 0),
98 CHAN5G(124, 0), CHAN5G(128, 0),
99 CHAN5G(132, 0), CHAN5G(136, 0),
100 CHAN5G(140, 0), CHAN5G(149, 0),
101 CHAN5G(153, 0), CHAN5G(157, 0),
102 CHAN5G(161, 0), CHAN5G(165, 0),
103 CHAN5G(184, 0), CHAN5G(188, 0),
104 CHAN5G(192, 0), CHAN5G(196, 0),
105 CHAN5G(200, 0), CHAN5G(204, 0),
106 CHAN5G(208, 0), CHAN5G(212, 0),
107 CHAN5G(216, 0),
108};
109
110static struct ieee80211_supported_band ath6kl_band_2ghz = {
111 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
112 .channels = ath6kl_2ghz_channels,
113 .n_bitrates = ath6kl_g_rates_size,
114 .bitrates = ath6kl_g_rates,
115};
116
117static struct ieee80211_supported_band ath6kl_band_5ghz = {
118 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
119 .channels = ath6kl_5ghz_a_channels,
120 .n_bitrates = ath6kl_a_rates_size,
121 .bitrates = ath6kl_a_rates,
122};
123
Jouni Malinen837cb972011-10-11 17:31:57 +0300124#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
125
Kalle Valo10509f92011-12-13 14:52:07 +0200126/* returns true if scheduled scan was stopped */
127static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
128{
129 struct ath6kl *ar = vif->ar;
130
131 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
132 return false;
133
134 del_timer_sync(&vif->sched_scan_timer);
135
136 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
137 ATH6KL_HOST_MODE_AWAKE);
138
139 ar->state = ATH6KL_STATE_ON;
140
141 return true;
142}
143
144static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
145{
146 struct ath6kl *ar = vif->ar;
147 bool stopped;
148
149 stopped = __ath6kl_cfg80211_sscan_stop(vif);
150
151 if (!stopped)
152 return;
153
154 cfg80211_sched_scan_stopped(ar->wiphy);
155}
156
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530157static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300158 enum nl80211_wpa_versions wpa_version)
159{
160 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
161
162 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530163 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300164 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530165 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300166 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530167 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300168 } else {
169 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
170 return -ENOTSUPP;
171 }
172
173 return 0;
174}
175
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530176static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300177 enum nl80211_auth_type auth_type)
178{
Kalle Valobdcd8172011-07-18 00:22:30 +0300179 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
180
181 switch (auth_type) {
182 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530183 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300184 break;
185 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530186 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300187 break;
188 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530189 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300190 break;
191
192 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530193 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300194 break;
195
196 default:
197 ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type);
198 return -ENOTSUPP;
199 }
200
201 return 0;
202}
203
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530204static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300205{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530206 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
207 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
208 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300209
210 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
211 __func__, cipher, ucast);
212
213 switch (cipher) {
214 case 0:
215 /* our own hack to use value 0 as no crypto used */
216 *ar_cipher = NONE_CRYPT;
217 *ar_cipher_len = 0;
218 break;
219 case WLAN_CIPHER_SUITE_WEP40:
220 *ar_cipher = WEP_CRYPT;
221 *ar_cipher_len = 5;
222 break;
223 case WLAN_CIPHER_SUITE_WEP104:
224 *ar_cipher = WEP_CRYPT;
225 *ar_cipher_len = 13;
226 break;
227 case WLAN_CIPHER_SUITE_TKIP:
228 *ar_cipher = TKIP_CRYPT;
229 *ar_cipher_len = 0;
230 break;
231 case WLAN_CIPHER_SUITE_CCMP:
232 *ar_cipher = AES_CRYPT;
233 *ar_cipher_len = 0;
234 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200235 case WLAN_CIPHER_SUITE_SMS4:
236 *ar_cipher = WAPI_CRYPT;
237 *ar_cipher_len = 0;
238 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300239 default:
240 ath6kl_err("cipher 0x%x not supported\n", cipher);
241 return -ENOTSUPP;
242 }
243
244 return 0;
245}
246
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530247static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300248{
249 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
250
251 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530252 if (vif->auth_mode == WPA_AUTH)
253 vif->auth_mode = WPA_PSK_AUTH;
254 else if (vif->auth_mode == WPA2_AUTH)
255 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300256 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530257 if (vif->auth_mode == WPA_AUTH)
258 vif->auth_mode = WPA_AUTH_CCKM;
259 else if (vif->auth_mode == WPA2_AUTH)
260 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300261 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530262 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300263 }
264}
265
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530266static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300267{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530268 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530269
Kalle Valobdcd8172011-07-18 00:22:30 +0300270 if (!test_bit(WMI_READY, &ar->flag)) {
271 ath6kl_err("wmi is not ready\n");
272 return false;
273 }
274
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530275 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300276 ath6kl_err("wlan disabled\n");
277 return false;
278 }
279
280 return true;
281}
282
Kevin Fang6981ffd2011-10-07 08:51:19 +0800283static bool ath6kl_is_wpa_ie(const u8 *pos)
284{
285 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
286 pos[2] == 0x00 && pos[3] == 0x50 &&
287 pos[4] == 0xf2 && pos[5] == 0x01;
288}
289
290static bool ath6kl_is_rsn_ie(const u8 *pos)
291{
292 return pos[0] == WLAN_EID_RSN;
293}
294
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700295static bool ath6kl_is_wps_ie(const u8 *pos)
296{
297 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
298 pos[1] >= 4 &&
299 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
300 pos[5] == 0x04);
301}
302
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530303static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
304 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800305{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530306 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800307 const u8 *pos;
308 u8 *buf = NULL;
309 size_t len = 0;
310 int ret;
311
312 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700313 * Clear previously set flag
314 */
315
316 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
317
318 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800319 * Filter out RSN/WPA IE(s)
320 */
321
322 if (ies && ies_len) {
323 buf = kmalloc(ies_len, GFP_KERNEL);
324 if (buf == NULL)
325 return -ENOMEM;
326 pos = ies;
327
328 while (pos + 1 < ies + ies_len) {
329 if (pos + 2 + pos[1] > ies + ies_len)
330 break;
331 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
332 memcpy(buf + len, pos, 2 + pos[1]);
333 len += 2 + pos[1];
334 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700335
336 if (ath6kl_is_wps_ie(pos))
337 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
338
Kevin Fang6981ffd2011-10-07 08:51:19 +0800339 pos += 2 + pos[1];
340 }
341 }
342
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530343 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
344 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800345 kfree(buf);
346 return ret;
347}
348
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530349static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
350{
351 switch (type) {
352 case NL80211_IFTYPE_STATION:
353 *nw_type = INFRA_NETWORK;
354 break;
355 case NL80211_IFTYPE_ADHOC:
356 *nw_type = ADHOC_NETWORK;
357 break;
358 case NL80211_IFTYPE_AP:
359 *nw_type = AP_NETWORK;
360 break;
361 case NL80211_IFTYPE_P2P_CLIENT:
362 *nw_type = INFRA_NETWORK;
363 break;
364 case NL80211_IFTYPE_P2P_GO:
365 *nw_type = AP_NETWORK;
366 break;
367 default:
368 ath6kl_err("invalid interface type %u\n", type);
369 return -ENOTSUPP;
370 }
371
372 return 0;
373}
374
375static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
376 u8 *if_idx, u8 *nw_type)
377{
378 int i;
379
380 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
381 return false;
382
383 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
384 ar->num_vif))
385 return false;
386
387 if (type == NL80211_IFTYPE_STATION ||
388 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200389 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530390 if ((ar->avail_idx_map >> i) & BIT(0)) {
391 *if_idx = i;
392 return true;
393 }
394 }
395 }
396
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530397 if (type == NL80211_IFTYPE_P2P_CLIENT ||
398 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200399 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530400 if ((ar->avail_idx_map >> i) & BIT(0)) {
401 *if_idx = i;
402 return true;
403 }
404 }
405 }
406
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530407 return false;
408}
409
Kalle Valobdcd8172011-07-18 00:22:30 +0300410static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
411 struct cfg80211_connect_params *sme)
412{
413 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530414 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300415 int status;
416
Kalle Valo10509f92011-12-13 14:52:07 +0200417 ath6kl_cfg80211_sscan_disable(vif);
418
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530419 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300420
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530421 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300422 return -EIO;
423
424 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
425 ath6kl_err("destroy in progress\n");
426 return -EBUSY;
427 }
428
429 if (test_bit(SKIP_SCAN, &ar->flag) &&
430 ((sme->channel && sme->channel->center_freq == 0) ||
431 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
432 ath6kl_err("SkipScan: channel or bssid invalid\n");
433 return -EINVAL;
434 }
435
436 if (down_interruptible(&ar->sem)) {
437 ath6kl_err("busy, couldn't get access\n");
438 return -ERESTARTSYS;
439 }
440
441 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
442 ath6kl_err("busy, destroy in progress\n");
443 up(&ar->sem);
444 return -EBUSY;
445 }
446
447 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
448 /*
449 * sleep until the command queue drains
450 */
451 wait_event_interruptible_timeout(ar->event_wq,
452 ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
453 WMI_TIMEOUT);
454 if (signal_pending(current)) {
455 ath6kl_err("cmd queue drain timeout\n");
456 up(&ar->sem);
457 return -EINTR;
458 }
459 }
460
Kevin Fang6981ffd2011-10-07 08:51:19 +0800461 if (sme->ie && (sme->ie_len > 0)) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530462 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
Dan Carpenter743b4512011-11-18 17:09:32 +0300463 if (status) {
464 up(&ar->sem);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800465 return status;
Dan Carpenter743b4512011-11-18 17:09:32 +0300466 }
Raja Mani542c5192011-11-15 14:14:56 +0530467 } else
468 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800469
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530470 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530471 vif->ssid_len == sme->ssid_len &&
472 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530473 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530474 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
475 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530476 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300477
478 up(&ar->sem);
479 if (status) {
480 ath6kl_err("wmi_reconnect_cmd failed\n");
481 return -EIO;
482 }
483 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530484 } else if (vif->ssid_len == sme->ssid_len &&
485 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530486 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300487 }
488
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530489 memset(vif->ssid, 0, sizeof(vif->ssid));
490 vif->ssid_len = sme->ssid_len;
491 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300492
493 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530494 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300495
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530496 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300497 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530498 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300499
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530500 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300501
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530502 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300503 if (status) {
504 up(&ar->sem);
505 return status;
506 }
507
508 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530509 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300510 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530511 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300512
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530513 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300514
515 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530516 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300517
518 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530519 (vif->auth_mode == NONE_AUTH) &&
520 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300521 struct ath6kl_key *key = NULL;
522
523 if (sme->key_idx < WMI_MIN_KEY_INDEX ||
524 sme->key_idx > WMI_MAX_KEY_INDEX) {
525 ath6kl_err("key index %d out of bounds\n",
526 sme->key_idx);
527 up(&ar->sem);
528 return -ENOENT;
529 }
530
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530531 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300532 key->key_len = sme->key_len;
533 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530534 key->cipher = vif->prwise_crypto;
535 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300536
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530537 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530538 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300539 GROUP_USAGE | TX_USAGE,
540 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200541 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300542 key->key, KEY_OP_INIT_VAL, NULL,
543 NO_SYNC_WMIFLAG);
544 }
545
546 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530547 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530548 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
549 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300550 ath6kl_err("couldn't set bss filtering\n");
551 up(&ar->sem);
552 return -EIO;
553 }
554 }
555
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530556 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300557
558 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
559 "%s: connect called with authmode %d dot11 auth %d"
560 " PW crypto %d PW crypto len %d GRP crypto %d"
561 " GRP crypto len %d channel hint %u\n",
562 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530563 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
564 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530565 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300566
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530567 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530568 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530569 vif->dot11_auth_mode, vif->auth_mode,
570 vif->prwise_crypto,
571 vif->prwise_crypto_len,
572 vif->grp_crypto, vif->grp_crypto_len,
573 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530574 vif->req_bssid, vif->ch_hint,
Kalle Valobdcd8172011-07-18 00:22:30 +0300575 ar->connect_ctrl_flags);
576
577 up(&ar->sem);
578
579 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530580 memset(vif->ssid, 0, sizeof(vif->ssid));
581 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300582 ath6kl_err("invalid request\n");
583 return -ENOENT;
584 } else if (status) {
585 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
586 return -EIO;
587 }
588
589 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530590 ((vif->auth_mode == WPA_PSK_AUTH)
591 || (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530592 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300593 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
594 }
595
596 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530597 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300598
599 return 0;
600}
601
Raja Mani4eab6f42011-11-09 17:02:23 +0530602static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
603 enum network_type nw_type,
604 const u8 *bssid,
Jouni Malinen01cac472011-09-19 19:14:59 +0300605 struct ieee80211_channel *chan,
606 const u8 *beacon_ie, size_t beacon_ie_len)
607{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530608 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300609 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530610 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300611 u8 *ie;
612
Raja Mani4eab6f42011-11-09 17:02:23 +0530613 if (nw_type & ADHOC_NETWORK) {
614 cap_mask = WLAN_CAPABILITY_IBSS;
615 cap_val = WLAN_CAPABILITY_IBSS;
616 } else {
617 cap_mask = WLAN_CAPABILITY_ESS;
618 cap_val = WLAN_CAPABILITY_ESS;
619 }
620
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530621 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530622 vif->ssid, vif->ssid_len,
623 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300624 if (bss == NULL) {
625 /*
626 * Since cfg80211 may not yet know about the BSS,
627 * generate a partial entry until the first BSS info
628 * event becomes available.
629 *
630 * Prepend SSID element since it is not included in the Beacon
631 * IEs from the target.
632 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530633 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300634 if (ie == NULL)
635 return -ENOMEM;
636 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530637 ie[1] = vif->ssid_len;
638 memcpy(ie + 2, vif->ssid, vif->ssid_len);
639 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530640 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530641 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530642 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300643 0, GFP_KERNEL);
644 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530645 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
646 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300647 kfree(ie);
648 } else
649 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
650 "entry\n");
651
652 if (bss == NULL)
653 return -ENOMEM;
654
655 cfg80211_put_bss(bss);
656
657 return 0;
658}
659
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530660void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300661 u8 *bssid, u16 listen_intvl,
662 u16 beacon_intvl,
663 enum network_type nw_type,
664 u8 beacon_ie_len, u8 assoc_req_len,
665 u8 assoc_resp_len, u8 *assoc_info)
666{
Jouni Malinen01cac472011-09-19 19:14:59 +0300667 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530668 struct ath6kl *ar = vif->ar;
Kalle Valobdcd8172011-07-18 00:22:30 +0300669
670 /* capinfo + listen interval */
671 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
672
673 /* capinfo + status code + associd */
674 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
675
676 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
677 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
678 assoc_resp_ie_offset;
679
680 assoc_req_len -= assoc_req_ie_offset;
681 assoc_resp_len -= assoc_resp_ie_offset;
682
Jouni Malinen32c10872011-09-19 19:15:07 +0300683 /*
684 * Store Beacon interval here; DTIM period will be available only once
685 * a Beacon frame from the AP is seen.
686 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530687 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530688 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300689
Kalle Valobdcd8172011-07-18 00:22:30 +0300690 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530691 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300692 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
693 "%s: ath6k not in ibss mode\n", __func__);
694 return;
695 }
696 }
697
698 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530699 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
700 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300701 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
702 "%s: ath6k not in station mode\n", __func__);
703 return;
704 }
705 }
706
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530707 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300708
Raja Mani4eab6f42011-11-09 17:02:23 +0530709 if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info,
710 beacon_ie_len) < 0) {
711 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300712 return;
713 }
714
Raja Mani4eab6f42011-11-09 17:02:23 +0530715 if (nw_type & ADHOC_NETWORK) {
716 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
717 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
718 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300719 return;
720 }
721
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530722 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300723 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530724 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530725 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300726 assoc_req_ie, assoc_req_len,
727 assoc_resp_ie, assoc_resp_len,
728 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530729 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300730 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530731 cfg80211_roamed(vif->ndev, chan, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300732 assoc_req_ie, assoc_req_len,
733 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
734 }
735}
736
737static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
738 struct net_device *dev, u16 reason_code)
739{
Kalle Valod6d5c062011-11-25 13:17:37 +0200740 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530741 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300742
743 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
744 reason_code);
745
Kalle Valo10509f92011-12-13 14:52:07 +0200746 ath6kl_cfg80211_sscan_disable(vif);
747
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530748 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300749 return -EIO;
750
751 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
752 ath6kl_err("busy, destroy in progress\n");
753 return -EBUSY;
754 }
755
756 if (down_interruptible(&ar->sem)) {
757 ath6kl_err("busy, couldn't get access\n");
758 return -ERESTARTSYS;
759 }
760
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530761 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530762 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530763 memset(vif->ssid, 0, sizeof(vif->ssid));
764 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300765
766 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530767 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300768
769 up(&ar->sem);
770
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530771 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530772
Kalle Valobdcd8172011-07-18 00:22:30 +0300773 return 0;
774}
775
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530776void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300777 u8 *bssid, u8 assoc_resp_len,
778 u8 *assoc_info, u16 proto_reason)
779{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530780 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530781
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530782 if (vif->scan_req) {
783 cfg80211_scan_done(vif->scan_req, true);
784 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300785 }
786
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530787 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530788 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300789 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
790 "%s: ath6k not in ibss mode\n", __func__);
791 return;
792 }
793 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530794 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300795 return;
796 }
797
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530798 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530799 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
800 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300801 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
802 "%s: ath6k not in station mode\n", __func__);
803 return;
804 }
805 }
806
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530807 /*
808 * Send a disconnect command to target when a disconnect event is
809 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
810 * request from host) to make the firmware stop trying to connect even
811 * after giving disconnect event. There will be one more disconnect
812 * event for this disconnect command with reason code DISCONNECT_CMD
813 * which will be notified to cfg80211.
814 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300815
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530816 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530817 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300818 return;
819 }
820
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530821 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300822
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530823 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530824 cfg80211_connect_result(vif->ndev,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530825 bssid, NULL, 0,
826 NULL, 0,
827 WLAN_STATUS_UNSPECIFIED_FAILURE,
828 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530829 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530830 cfg80211_disconnected(vif->ndev, reason,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530831 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300832 }
833
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530834 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300835}
836
Kalle Valobdcd8172011-07-18 00:22:30 +0300837static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
838 struct cfg80211_scan_request *request)
839{
Kalle Valod6d5c062011-11-25 13:17:37 +0200840 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530841 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300842 s8 n_channels = 0;
843 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300844 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530845 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300846
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530847 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300848 return -EIO;
849
Kalle Valo10509f92011-12-13 14:52:07 +0200850 ath6kl_cfg80211_sscan_disable(vif);
851
Kalle Valobdcd8172011-07-18 00:22:30 +0300852 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530853 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300854 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530855 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530856 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300857 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
858 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300859 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300860 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300861 }
862 }
863
864 if (request->n_ssids && request->ssids[0].ssid_len) {
865 u8 i;
866
867 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
868 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
869
870 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530871 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
872 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300873 request->ssids[i].ssid_len,
874 request->ssids[i].ssid);
875 }
876
Kalle Valo10509f92011-12-13 14:52:07 +0200877 /*
878 * FIXME: we should clear the IE in fw if it's not set so just
879 * remove the check altogether
880 */
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300881 if (request->ie) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530882 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
883 WMI_FRAME_PROBE_REQ,
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300884 request->ie, request->ie_len);
885 if (ret) {
886 ath6kl_err("failed to set Probe Request appie for "
887 "scan");
888 return ret;
889 }
890 }
891
Jouni Malinen11869be2011-09-02 20:07:06 +0300892 /*
893 * Scan only the requested channels if the request specifies a set of
894 * channels. If the list is longer than the target supports, do not
895 * configure the list and instead, scan all available channels.
896 */
897 if (request->n_channels > 0 &&
898 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300899 u8 i;
900
Jouni Malinen11869be2011-09-02 20:07:06 +0300901 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300902
903 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
904 if (channels == NULL) {
905 ath6kl_warn("failed to set scan channels, "
906 "scan all channels");
907 n_channels = 0;
908 }
909
910 for (i = 0; i < n_channels; i++)
911 channels[i] = request->channels[i]->center_freq;
912 }
913
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530914 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530915 force_fg_scan = 1;
916
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530917 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN,
918 force_fg_scan, false, 0, 0, n_channels,
919 channels);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300920 if (ret)
Kalle Valobdcd8172011-07-18 00:22:30 +0300921 ath6kl_err("wmi_startscan_cmd failed\n");
Jouni Malinen11869be2011-09-02 20:07:06 +0300922 else
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530923 vif->scan_req = request;
Kalle Valobdcd8172011-07-18 00:22:30 +0300924
Edward Lu1276c9e2011-08-30 21:58:00 +0300925 kfree(channels);
926
Kalle Valobdcd8172011-07-18 00:22:30 +0300927 return ret;
928}
929
Kalle Valo1c17d312011-11-01 08:43:56 +0200930void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300931{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530932 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300933 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300934
Kalle Valo1c17d312011-11-01 08:43:56 +0200935 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
936 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300937
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530938 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300939 return;
Kalle Valobdcd8172011-07-18 00:22:30 +0300940
Kalle Valo1c17d312011-11-01 08:43:56 +0200941 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300942 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300943
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530944 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
945 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530946 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
947 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300948 0, NULL);
949 }
950 }
951
952out:
Kalle Valocb938212011-10-27 18:47:46 +0300953 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530954 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300955}
956
957static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
958 u8 key_index, bool pairwise,
959 const u8 *mac_addr,
960 struct key_params *params)
961{
Kalle Valod6d5c062011-11-25 13:17:37 +0200962 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530963 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300964 struct ath6kl_key *key = NULL;
965 u8 key_usage;
966 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +0300967
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530968 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300969 return -EIO;
970
Jouni Malinen837cb972011-10-11 17:31:57 +0300971 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
972 if (params->key_len != WMI_KRK_LEN)
973 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530974 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
975 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +0300976 }
977
Kalle Valobdcd8172011-07-18 00:22:30 +0300978 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
979 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
980 "%s: key index %d out of bounds\n", __func__,
981 key_index);
982 return -ENOENT;
983 }
984
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530985 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +0300986 memset(key, 0, sizeof(struct ath6kl_key));
987
988 if (pairwise)
989 key_usage = PAIRWISE_USAGE;
990 else
991 key_usage = GROUP_USAGE;
992
993 if (params) {
Dai Shuibing5e070212011-11-03 11:39:37 +0200994 int seq_len = params->seq_len;
995 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
996 seq_len > ATH6KL_KEY_SEQ_LEN) {
997 /* Only first half of the WPI PN is configured */
998 seq_len = ATH6KL_KEY_SEQ_LEN;
999 }
Kalle Valobdcd8172011-07-18 00:22:30 +03001000 if (params->key_len > WLAN_MAX_KEY_LEN ||
Dai Shuibing5e070212011-11-03 11:39:37 +02001001 seq_len > sizeof(key->seq))
Kalle Valobdcd8172011-07-18 00:22:30 +03001002 return -EINVAL;
1003
1004 key->key_len = params->key_len;
1005 memcpy(key->key, params->key, key->key_len);
Dai Shuibing5e070212011-11-03 11:39:37 +02001006 key->seq_len = seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001007 memcpy(key->seq, params->seq, key->seq_len);
1008 key->cipher = params->cipher;
1009 }
1010
1011 switch (key->cipher) {
1012 case WLAN_CIPHER_SUITE_WEP40:
1013 case WLAN_CIPHER_SUITE_WEP104:
1014 key_type = WEP_CRYPT;
1015 break;
1016
1017 case WLAN_CIPHER_SUITE_TKIP:
1018 key_type = TKIP_CRYPT;
1019 break;
1020
1021 case WLAN_CIPHER_SUITE_CCMP:
1022 key_type = AES_CRYPT;
1023 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001024 case WLAN_CIPHER_SUITE_SMS4:
1025 key_type = WAPI_CRYPT;
1026 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001027
1028 default:
1029 return -ENOTSUPP;
1030 }
1031
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301032 if (((vif->auth_mode == WPA_PSK_AUTH)
1033 || (vif->auth_mode == WPA2_PSK_AUTH))
Kalle Valobdcd8172011-07-18 00:22:30 +03001034 && (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301035 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001036
1037 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1038 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1039 __func__, key_index, key->key_len, key_type,
1040 key_usage, key->seq_len);
1041
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301042 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001043 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
1044 key_type == WAPI_CRYPT) && params) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001045 ar->ap_mode_bkey.valid = true;
1046 ar->ap_mode_bkey.key_index = key_index;
1047 ar->ap_mode_bkey.key_type = key_type;
1048 ar->ap_mode_bkey.key_len = key->key_len;
1049 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301050 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001051 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1052 "key configuration until AP mode has been "
1053 "started\n");
1054 /*
1055 * The key will be set in ath6kl_connect_ap_mode() once
1056 * the connected event is received from the target.
1057 */
1058 return 0;
1059 }
1060 }
1061
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301062 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301063 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001064 /*
1065 * Store the key locally so that it can be re-configured after
1066 * the AP mode has properly started
1067 * (ath6kl_install_statioc_wep_keys).
1068 */
1069 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1070 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301071 vif->wep_key_list[key_index].key_len = key->key_len;
1072 memcpy(vif->wep_key_list[key_index].key, key->key,
1073 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001074 return 0;
1075 }
1076
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301077 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001078 key_type, key_usage, key->key_len,
1079 key->seq, key->seq_len, key->key,
1080 KEY_OP_INIT_VAL,
1081 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001082}
1083
1084static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1085 u8 key_index, bool pairwise,
1086 const u8 *mac_addr)
1087{
Kalle Valod6d5c062011-11-25 13:17:37 +02001088 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301089 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001090
1091 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1092
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301093 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001094 return -EIO;
1095
1096 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1097 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1098 "%s: key index %d out of bounds\n", __func__,
1099 key_index);
1100 return -ENOENT;
1101 }
1102
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301103 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001104 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1105 "%s: index %d is empty\n", __func__, key_index);
1106 return 0;
1107 }
1108
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301109 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001110
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301111 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001112}
1113
1114static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1115 u8 key_index, bool pairwise,
1116 const u8 *mac_addr, void *cookie,
1117 void (*callback) (void *cookie,
1118 struct key_params *))
1119{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301120 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001121 struct ath6kl_key *key = NULL;
1122 struct key_params params;
1123
1124 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1125
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301126 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001127 return -EIO;
1128
1129 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1130 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1131 "%s: key index %d out of bounds\n", __func__,
1132 key_index);
1133 return -ENOENT;
1134 }
1135
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301136 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001137 memset(&params, 0, sizeof(params));
1138 params.cipher = key->cipher;
1139 params.key_len = key->key_len;
1140 params.seq_len = key->seq_len;
1141 params.seq = key->seq;
1142 params.key = key->key;
1143
1144 callback(cookie, &params);
1145
1146 return key->key_len ? 0 : -ENOENT;
1147}
1148
1149static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1150 struct net_device *ndev,
1151 u8 key_index, bool unicast,
1152 bool multicast)
1153{
Kalle Valod6d5c062011-11-25 13:17:37 +02001154 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301155 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001156 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001157 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001158 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001159
1160 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1161
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301162 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001163 return -EIO;
1164
1165 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1166 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1167 "%s: key index %d out of bounds\n",
1168 __func__, key_index);
1169 return -ENOENT;
1170 }
1171
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301172 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001173 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1174 __func__, key_index);
1175 return -EINVAL;
1176 }
1177
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301178 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301179 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001180 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301181 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001182 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001183 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301184 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001185 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301186 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001187
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301188 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001189 return 0; /* Delay until AP mode has been started */
1190
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001191 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1192 vif->def_txkey_index,
1193 key_type, key_usage,
1194 key->key_len, key->seq, key->seq_len,
1195 key->key,
1196 KEY_OP_INIT_VAL, NULL,
1197 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001198}
1199
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301200void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001201 bool ismcast)
1202{
1203 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1204 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1205
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301206 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001207 (ismcast ? NL80211_KEYTYPE_GROUP :
1208 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1209 GFP_KERNEL);
1210}
1211
1212static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1213{
1214 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301215 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001216 int ret;
1217
1218 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1219 changed);
1220
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301221 vif = ath6kl_vif_first(ar);
1222 if (!vif)
1223 return -EIO;
1224
1225 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001226 return -EIO;
1227
1228 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1229 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1230 if (ret != 0) {
1231 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1232 return -EIO;
1233 }
1234 }
1235
1236 return 0;
1237}
1238
1239/*
1240 * The type nl80211_tx_power_setting replaces the following
1241 * data type from 2.6.36 onwards
1242*/
1243static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1244 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001245 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001246{
1247 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301248 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001249 u8 ath6kl_dbm;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001250 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001251
1252 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1253 type, dbm);
1254
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301255 vif = ath6kl_vif_first(ar);
1256 if (!vif)
1257 return -EIO;
1258
1259 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001260 return -EIO;
1261
1262 switch (type) {
1263 case NL80211_TX_POWER_AUTOMATIC:
1264 return 0;
1265 case NL80211_TX_POWER_LIMITED:
1266 ar->tx_pwr = ath6kl_dbm = dbm;
1267 break;
1268 default:
1269 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1270 __func__, type);
1271 return -EOPNOTSUPP;
1272 }
1273
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301274 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001275
1276 return 0;
1277}
1278
1279static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1280{
1281 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301282 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001283
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301284 vif = ath6kl_vif_first(ar);
1285 if (!vif)
1286 return -EIO;
1287
1288 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001289 return -EIO;
1290
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301291 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001292 ar->tx_pwr = 0;
1293
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301294 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001295 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1296 return -EIO;
1297 }
1298
1299 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1300 5 * HZ);
1301
1302 if (signal_pending(current)) {
1303 ath6kl_err("target did not respond\n");
1304 return -EINTR;
1305 }
1306 }
1307
1308 *dbm = ar->tx_pwr;
1309 return 0;
1310}
1311
1312static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1313 struct net_device *dev,
1314 bool pmgmt, int timeout)
1315{
1316 struct ath6kl *ar = ath6kl_priv(dev);
1317 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301318 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001319
1320 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1321 __func__, pmgmt, timeout);
1322
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301323 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001324 return -EIO;
1325
1326 if (pmgmt) {
1327 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1328 mode.pwr_mode = REC_POWER;
1329 } else {
1330 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1331 mode.pwr_mode = MAX_PERF_POWER;
1332 }
1333
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301334 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
1335 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001336 ath6kl_err("wmi_powermode_cmd failed\n");
1337 return -EIO;
1338 }
1339
1340 return 0;
1341}
1342
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301343static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1344 char *name,
1345 enum nl80211_iftype type,
1346 u32 *flags,
1347 struct vif_params *params)
1348{
1349 struct ath6kl *ar = wiphy_priv(wiphy);
1350 struct net_device *ndev;
1351 u8 if_idx, nw_type;
1352
Kalle Valo71f96ee2011-11-14 19:31:30 +02001353 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301354 ath6kl_err("Reached maximum number of supported vif\n");
1355 return ERR_PTR(-EINVAL);
1356 }
1357
1358 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1359 ath6kl_err("Not a supported interface type\n");
1360 return ERR_PTR(-EINVAL);
1361 }
1362
1363 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1364 if (!ndev)
1365 return ERR_PTR(-ENOMEM);
1366
1367 ar->num_vif++;
1368
1369 return ndev;
1370}
1371
1372static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1373 struct net_device *ndev)
1374{
1375 struct ath6kl *ar = wiphy_priv(wiphy);
1376 struct ath6kl_vif *vif = netdev_priv(ndev);
1377
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301378 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301379 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301380 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301381
1382 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1383
1384 ath6kl_deinit_if_data(vif);
1385
1386 return 0;
1387}
1388
Kalle Valobdcd8172011-07-18 00:22:30 +03001389static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1390 struct net_device *ndev,
1391 enum nl80211_iftype type, u32 *flags,
1392 struct vif_params *params)
1393{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301394 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001395
1396 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1397
Kalle Valobdcd8172011-07-18 00:22:30 +03001398 switch (type) {
1399 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301400 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001401 break;
1402 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301403 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001404 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001405 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301406 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001407 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001408 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301409 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001410 break;
1411 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301412 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001413 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001414 default:
1415 ath6kl_err("invalid interface type %u\n", type);
1416 return -EOPNOTSUPP;
1417 }
1418
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301419 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001420
1421 return 0;
1422}
1423
1424static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1425 struct net_device *dev,
1426 struct cfg80211_ibss_params *ibss_param)
1427{
1428 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301429 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001430 int status;
1431
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301432 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001433 return -EIO;
1434
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301435 vif->ssid_len = ibss_param->ssid_len;
1436 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001437
1438 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301439 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001440
1441 if (ibss_param->channel_fixed) {
1442 /*
1443 * TODO: channel_fixed: The channel should be fixed, do not
1444 * search for IBSSs to join on other channels. Target
1445 * firmware does not support this feature, needs to be
1446 * updated.
1447 */
1448 return -EOPNOTSUPP;
1449 }
1450
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301451 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001452 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301453 memcpy(vif->req_bssid, ibss_param->bssid,
1454 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001455
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301456 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001457
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301458 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001459 if (status)
1460 return status;
1461
1462 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301463 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1464 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001465 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301466 ath6kl_set_cipher(vif, 0, true);
1467 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001468 }
1469
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301470 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001471
1472 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1473 "%s: connect called with authmode %d dot11 auth %d"
1474 " PW crypto %d PW crypto len %d GRP crypto %d"
1475 " GRP crypto len %d channel hint %u\n",
1476 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301477 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1478 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301479 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001480
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301481 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301482 vif->dot11_auth_mode, vif->auth_mode,
1483 vif->prwise_crypto,
1484 vif->prwise_crypto_len,
1485 vif->grp_crypto, vif->grp_crypto_len,
1486 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301487 vif->req_bssid, vif->ch_hint,
Kalle Valobdcd8172011-07-18 00:22:30 +03001488 ar->connect_ctrl_flags);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301489 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001490
1491 return 0;
1492}
1493
1494static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1495 struct net_device *dev)
1496{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301497 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001498
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301499 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001500 return -EIO;
1501
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301502 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301503 memset(vif->ssid, 0, sizeof(vif->ssid));
1504 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001505
1506 return 0;
1507}
1508
1509static const u32 cipher_suites[] = {
1510 WLAN_CIPHER_SUITE_WEP40,
1511 WLAN_CIPHER_SUITE_WEP104,
1512 WLAN_CIPHER_SUITE_TKIP,
1513 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001514 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001515 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001516};
1517
1518static bool is_rate_legacy(s32 rate)
1519{
1520 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1521 6000, 9000, 12000, 18000, 24000,
1522 36000, 48000, 54000
1523 };
1524 u8 i;
1525
1526 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1527 if (rate == legacy[i])
1528 return true;
1529
1530 return false;
1531}
1532
1533static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1534{
1535 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1536 52000, 58500, 65000, 72200
1537 };
1538 u8 i;
1539
1540 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1541 if (rate == ht20[i]) {
1542 if (i == ARRAY_SIZE(ht20) - 1)
1543 /* last rate uses sgi */
1544 *sgi = true;
1545 else
1546 *sgi = false;
1547
1548 *mcs = i;
1549 return true;
1550 }
1551 }
1552 return false;
1553}
1554
1555static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1556{
1557 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1558 81000, 108000, 121500, 135000,
1559 150000
1560 };
1561 u8 i;
1562
1563 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1564 if (rate == ht40[i]) {
1565 if (i == ARRAY_SIZE(ht40) - 1)
1566 /* last rate uses sgi */
1567 *sgi = true;
1568 else
1569 *sgi = false;
1570
1571 *mcs = i;
1572 return true;
1573 }
1574 }
1575
1576 return false;
1577}
1578
1579static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1580 u8 *mac, struct station_info *sinfo)
1581{
1582 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301583 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001584 long left;
1585 bool sgi;
1586 s32 rate;
1587 int ret;
1588 u8 mcs;
1589
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301590 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001591 return -ENOENT;
1592
1593 if (down_interruptible(&ar->sem))
1594 return -EBUSY;
1595
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301596 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001597
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301598 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001599
1600 if (ret != 0) {
1601 up(&ar->sem);
1602 return -EIO;
1603 }
1604
1605 left = wait_event_interruptible_timeout(ar->event_wq,
1606 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301607 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001608 WMI_TIMEOUT);
1609
1610 up(&ar->sem);
1611
1612 if (left == 0)
1613 return -ETIMEDOUT;
1614 else if (left < 0)
1615 return left;
1616
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301617 if (vif->target_stats.rx_byte) {
1618 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001619 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301620 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001621 sinfo->filled |= STATION_INFO_RX_PACKETS;
1622 }
1623
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301624 if (vif->target_stats.tx_byte) {
1625 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001626 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301627 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001628 sinfo->filled |= STATION_INFO_TX_PACKETS;
1629 }
1630
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301631 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001632 sinfo->filled |= STATION_INFO_SIGNAL;
1633
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301634 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001635
1636 if (is_rate_legacy(rate)) {
1637 sinfo->txrate.legacy = rate / 100;
1638 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1639 if (sgi) {
1640 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1641 sinfo->txrate.mcs = mcs - 1;
1642 } else {
1643 sinfo->txrate.mcs = mcs;
1644 }
1645
1646 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1647 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1648 if (sgi) {
1649 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1650 sinfo->txrate.mcs = mcs - 1;
1651 } else {
1652 sinfo->txrate.mcs = mcs;
1653 }
1654
1655 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1656 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1657 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001658 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1659 "invalid rate from stats: %d\n", rate);
1660 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001661 return 0;
1662 }
1663
1664 sinfo->filled |= STATION_INFO_TX_BITRATE;
1665
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301666 if (test_bit(CONNECTED, &vif->flags) &&
1667 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301668 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001669 sinfo->filled |= STATION_INFO_BSS_PARAM;
1670 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301671 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1672 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001673 }
1674
Kalle Valobdcd8172011-07-18 00:22:30 +03001675 return 0;
1676}
1677
1678static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1679 struct cfg80211_pmksa *pmksa)
1680{
1681 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301682 struct ath6kl_vif *vif = netdev_priv(netdev);
1683
1684 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001685 pmksa->pmkid, true);
1686}
1687
1688static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1689 struct cfg80211_pmksa *pmksa)
1690{
1691 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301692 struct ath6kl_vif *vif = netdev_priv(netdev);
1693
1694 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001695 pmksa->pmkid, false);
1696}
1697
1698static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1699{
1700 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301701 struct ath6kl_vif *vif = netdev_priv(netdev);
1702
1703 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301704 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1705 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001706 return 0;
1707}
1708
Raja Mani6cb3c712011-11-07 22:52:45 +02001709static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1710{
1711 struct ath6kl_vif *vif;
1712 int ret, pos, left;
1713 u32 filter = 0;
1714 u16 i;
1715 u8 mask[WOW_MASK_SIZE];
1716
1717 vif = ath6kl_vif_first(ar);
1718 if (!vif)
1719 return -EIO;
1720
1721 if (!ath6kl_cfg80211_ready(vif))
1722 return -EIO;
1723
1724 if (!test_bit(CONNECTED, &vif->flags))
1725 return -EINVAL;
1726
1727 /* Clear existing WOW patterns */
1728 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
1729 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
1730 WOW_LIST_ID, i);
1731 /* Configure new WOW patterns */
1732 for (i = 0; i < wow->n_patterns; i++) {
1733
1734 /*
1735 * Convert given nl80211 specific mask value to equivalent
1736 * driver specific mask value and send it to the chip along
1737 * with patterns. For example, If the mask value defined in
1738 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1739 * then equivalent driver specific mask value is
1740 * "0xFF 0x00 0xFF 0x00".
1741 */
1742 memset(&mask, 0, sizeof(mask));
1743 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1744 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1745 mask[pos] = 0xFF;
1746 }
1747 /*
1748 * Note: Pattern's offset is not passed as part of wowlan
1749 * parameter from CFG layer. So it's always passed as ZERO
1750 * to the firmware. It means, given WOW patterns are always
1751 * matched from the first byte of received pkt in the firmware.
1752 */
1753 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1754 vif->fw_vif_idx, WOW_LIST_ID,
1755 wow->patterns[i].pattern_len,
1756 0 /* pattern offset */,
1757 wow->patterns[i].pattern, mask);
1758 if (ret)
1759 return ret;
1760 }
1761
1762 if (wow->disconnect)
1763 filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1764
1765 if (wow->magic_pkt)
1766 filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1767
1768 if (wow->gtk_rekey_failure)
1769 filter |= WOW_FILTER_OPTION_GTK_ERROR;
1770
1771 if (wow->eap_identity_req)
1772 filter |= WOW_FILTER_OPTION_EAP_REQ;
1773
1774 if (wow->four_way_handshake)
1775 filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1776
1777 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
1778 ATH6KL_WOW_MODE_ENABLE,
1779 filter,
1780 WOW_HOST_REQ_DELAY);
1781 if (ret)
1782 return ret;
1783
1784 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1785 ATH6KL_HOST_MODE_ASLEEP);
1786 if (ret)
1787 return ret;
1788
1789 if (ar->tx_pending[ar->ctrl_ep]) {
1790 left = wait_event_interruptible_timeout(ar->event_wq,
1791 ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
1792 if (left == 0) {
1793 ath6kl_warn("clear wmi ctrl data timeout\n");
1794 ret = -ETIMEDOUT;
1795 } else if (left < 0) {
1796 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
1797 ret = left;
1798 }
1799 }
1800
1801 return ret;
1802}
1803
1804static int ath6kl_wow_resume(struct ath6kl *ar)
1805{
1806 struct ath6kl_vif *vif;
1807 int ret;
1808
1809 vif = ath6kl_vif_first(ar);
1810 if (!vif)
1811 return -EIO;
1812
1813 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1814 ATH6KL_HOST_MODE_AWAKE);
1815 return ret;
1816}
1817
Kalle Valo52d81a62011-11-01 08:44:21 +02001818int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02001819 enum ath6kl_cfg_suspend_mode mode,
1820 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02001821{
1822 int ret;
1823
Kalle Valo52d81a62011-11-01 08:44:21 +02001824 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02001825 case ATH6KL_CFG_SUSPEND_WOW:
1826
1827 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
1828
1829 /* Flush all non control pkts in TX path */
1830 ath6kl_tx_data_cleanup(ar);
1831
1832 ret = ath6kl_wow_suspend(ar, wow);
1833 if (ret) {
1834 ath6kl_err("wow suspend failed: %d\n", ret);
1835 return ret;
1836 }
1837 ar->state = ATH6KL_STATE_WOW;
1838 break;
1839
Kalle Valo52d81a62011-11-01 08:44:21 +02001840 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02001841
Kalle Valo7125f012011-12-13 14:51:37 +02001842 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02001843
Kalle Valo52d81a62011-11-01 08:44:21 +02001844 /* save the current power mode before enabling power save */
1845 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
1846
1847 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
1848 if (ret) {
1849 ath6kl_warn("wmi powermode command failed during suspend: %d\n",
1850 ret);
1851 }
1852
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001853 ar->state = ATH6KL_STATE_DEEPSLEEP;
1854
Kalle Valo52d81a62011-11-01 08:44:21 +02001855 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001856
1857 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02001858
Kalle Valo7125f012011-12-13 14:51:37 +02001859 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02001860
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001861 if (ar->state == ATH6KL_STATE_OFF) {
1862 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
1863 "suspend hw off, no action for cutpower\n");
1864 break;
1865 }
1866
1867 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
1868
1869 ret = ath6kl_init_hw_stop(ar);
1870 if (ret) {
1871 ath6kl_warn("failed to stop hw during suspend: %d\n",
1872 ret);
1873 }
1874
1875 ar->state = ATH6KL_STATE_CUTPOWER;
1876
1877 break;
1878
Kalle Valo10509f92011-12-13 14:52:07 +02001879 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
1880 /*
1881 * Nothing needed for schedule scan, firmware is already in
1882 * wow mode and sleeping most of the time.
1883 */
1884 break;
1885
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001886 default:
1887 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02001888 }
1889
1890 return 0;
1891}
1892
1893int ath6kl_cfg80211_resume(struct ath6kl *ar)
1894{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001895 int ret;
1896
1897 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02001898 case ATH6KL_STATE_WOW:
1899 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
1900
1901 ret = ath6kl_wow_resume(ar);
1902 if (ret) {
1903 ath6kl_warn("wow mode resume failed: %d\n", ret);
1904 return ret;
1905 }
1906
1907 ar->state = ATH6KL_STATE_ON;
1908 break;
1909
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001910 case ATH6KL_STATE_DEEPSLEEP:
1911 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
1912 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
1913 ar->wmi->saved_pwr_mode);
1914 if (ret) {
1915 ath6kl_warn("wmi powermode command failed during resume: %d\n",
1916 ret);
1917 }
1918 }
1919
1920 ar->state = ATH6KL_STATE_ON;
1921
1922 break;
1923
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001924 case ATH6KL_STATE_CUTPOWER:
1925 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
1926
1927 ret = ath6kl_init_hw_start(ar);
1928 if (ret) {
1929 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
1930 return ret;
1931 }
Raja Manid7c44e02011-11-07 22:52:46 +02001932 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001933
Kalle Valo10509f92011-12-13 14:52:07 +02001934 case ATH6KL_STATE_SCHED_SCAN:
1935 break;
1936
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001937 default:
1938 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02001939 }
1940
1941 return 0;
1942}
1943
Kalle Valoabcb3442011-07-22 08:26:20 +03001944#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02001945
1946/* hif layer decides what suspend mode to use */
1947static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03001948 struct cfg80211_wowlan *wow)
1949{
1950 struct ath6kl *ar = wiphy_priv(wiphy);
1951
Raja Mani0f60e9f2011-11-07 22:52:45 +02001952 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03001953}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03001954
Kalle Valo52d81a62011-11-01 08:44:21 +02001955static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03001956{
1957 struct ath6kl *ar = wiphy_priv(wiphy);
1958
1959 return ath6kl_hif_resume(ar);
1960}
Raja Mania918fb32011-11-07 22:52:46 +02001961
1962/*
1963 * FIXME: WOW suspend mode is selected if the host sdio controller supports
1964 * both sdio irq wake up and keep power. The target pulls sdio data line to
1965 * wake up the host when WOW pattern matches. This causes sdio irq handler
1966 * is being called in the host side which internally hits ath6kl's RX path.
1967 *
1968 * Since sdio interrupt is not disabled, RX path executes even before
1969 * the host executes the actual resume operation from PM module.
1970 *
1971 * In the current scenario, WOW resume should happen before start processing
1972 * any data from the target. So It's required to perform WOW resume in RX path.
1973 * Ideally we should perform WOW resume only in the actual platform
1974 * resume path. This area needs bit rework to avoid WOW resume in RX path.
1975 *
1976 * ath6kl_check_wow_status() is called from ath6kl_rx().
1977 */
1978void ath6kl_check_wow_status(struct ath6kl *ar)
1979{
1980 if (ar->state == ATH6KL_STATE_WOW)
1981 ath6kl_cfg80211_resume(ar);
1982}
1983
1984#else
1985
1986void ath6kl_check_wow_status(struct ath6kl *ar)
1987{
1988}
Kalle Valoabcb3442011-07-22 08:26:20 +03001989#endif
1990
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03001991static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
1992 struct ieee80211_channel *chan,
1993 enum nl80211_channel_type channel_type)
1994{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301995 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03001996
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301997 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03001998 return -EIO;
1999
2000 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2001 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302002 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002003
2004 return 0;
2005}
2006
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002007static bool ath6kl_is_p2p_ie(const u8 *pos)
2008{
2009 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2010 pos[2] == 0x50 && pos[3] == 0x6f &&
2011 pos[4] == 0x9a && pos[5] == 0x09;
2012}
2013
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302014static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2015 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002016{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302017 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002018 const u8 *pos;
2019 u8 *buf = NULL;
2020 size_t len = 0;
2021 int ret;
2022
2023 /*
2024 * Filter out P2P IE(s) since they will be included depending on
2025 * the Probe Request frame in ath6kl_send_go_probe_resp().
2026 */
2027
2028 if (ies && ies_len) {
2029 buf = kmalloc(ies_len, GFP_KERNEL);
2030 if (buf == NULL)
2031 return -ENOMEM;
2032 pos = ies;
2033 while (pos + 1 < ies + ies_len) {
2034 if (pos + 2 + pos[1] > ies + ies_len)
2035 break;
2036 if (!ath6kl_is_p2p_ie(pos)) {
2037 memcpy(buf + len, pos, 2 + pos[1]);
2038 len += 2 + pos[1];
2039 }
2040 pos += 2 + pos[1];
2041 }
2042 }
2043
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302044 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2045 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002046 kfree(buf);
2047 return ret;
2048}
2049
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002050static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
2051 struct beacon_parameters *info, bool add)
2052{
2053 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302054 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002055 struct ieee80211_mgmt *mgmt;
2056 u8 *ies;
2057 int ies_len;
2058 struct wmi_connect_cmd p;
2059 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302060 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002061
2062 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
2063
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302064 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002065 return -EIO;
2066
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302067 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002068 return -EOPNOTSUPP;
2069
2070 if (info->beacon_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302071 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2072 WMI_FRAME_BEACON,
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002073 info->beacon_ies,
2074 info->beacon_ies_len);
2075 if (res)
2076 return res;
2077 }
2078 if (info->proberesp_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302079 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002080 info->proberesp_ies_len);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002081 if (res)
2082 return res;
2083 }
2084 if (info->assocresp_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302085 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2086 WMI_FRAME_ASSOC_RESP,
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002087 info->assocresp_ies,
2088 info->assocresp_ies_len);
2089 if (res)
2090 return res;
2091 }
2092
2093 if (!add)
2094 return 0;
2095
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002096 ar->ap_mode_bkey.valid = false;
2097
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002098 /* TODO:
2099 * info->interval
2100 * info->dtim_period
2101 */
2102
2103 if (info->head == NULL)
2104 return -EINVAL;
2105 mgmt = (struct ieee80211_mgmt *) info->head;
2106 ies = mgmt->u.beacon.variable;
2107 if (ies > info->head + info->head_len)
2108 return -EINVAL;
2109 ies_len = info->head + info->head_len - ies;
2110
2111 if (info->ssid == NULL)
2112 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302113 memcpy(vif->ssid, info->ssid, info->ssid_len);
2114 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002115 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
2116 return -EOPNOTSUPP; /* TODO */
2117
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302118 ret = ath6kl_set_auth_type(vif, info->auth_type);
2119 if (ret)
2120 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002121
2122 memset(&p, 0, sizeof(p));
2123
2124 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2125 switch (info->crypto.akm_suites[i]) {
2126 case WLAN_AKM_SUITE_8021X:
2127 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2128 p.auth_mode |= WPA_AUTH;
2129 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2130 p.auth_mode |= WPA2_AUTH;
2131 break;
2132 case WLAN_AKM_SUITE_PSK:
2133 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2134 p.auth_mode |= WPA_PSK_AUTH;
2135 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2136 p.auth_mode |= WPA2_PSK_AUTH;
2137 break;
2138 }
2139 }
2140 if (p.auth_mode == 0)
2141 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302142 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002143
2144 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2145 switch (info->crypto.ciphers_pairwise[i]) {
2146 case WLAN_CIPHER_SUITE_WEP40:
2147 case WLAN_CIPHER_SUITE_WEP104:
2148 p.prwise_crypto_type |= WEP_CRYPT;
2149 break;
2150 case WLAN_CIPHER_SUITE_TKIP:
2151 p.prwise_crypto_type |= TKIP_CRYPT;
2152 break;
2153 case WLAN_CIPHER_SUITE_CCMP:
2154 p.prwise_crypto_type |= AES_CRYPT;
2155 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002156 case WLAN_CIPHER_SUITE_SMS4:
2157 p.prwise_crypto_type |= WAPI_CRYPT;
2158 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002159 }
2160 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002161 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002162 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302163 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002164 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302165 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002166
2167 switch (info->crypto.cipher_group) {
2168 case WLAN_CIPHER_SUITE_WEP40:
2169 case WLAN_CIPHER_SUITE_WEP104:
2170 p.grp_crypto_type = WEP_CRYPT;
2171 break;
2172 case WLAN_CIPHER_SUITE_TKIP:
2173 p.grp_crypto_type = TKIP_CRYPT;
2174 break;
2175 case WLAN_CIPHER_SUITE_CCMP:
2176 p.grp_crypto_type = AES_CRYPT;
2177 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002178 case WLAN_CIPHER_SUITE_SMS4:
2179 p.grp_crypto_type = WAPI_CRYPT;
2180 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002181 default:
2182 p.grp_crypto_type = NONE_CRYPT;
2183 break;
2184 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302185 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002186
2187 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302188 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002189
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302190 p.ssid_len = vif->ssid_len;
2191 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2192 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302193 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002194
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302195 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002196 if (res < 0)
2197 return res;
2198
2199 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002200}
2201
2202static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
2203 struct beacon_parameters *info)
2204{
2205 return ath6kl_ap_beacon(wiphy, dev, info, true);
2206}
2207
2208static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
2209 struct beacon_parameters *info)
2210{
2211 return ath6kl_ap_beacon(wiphy, dev, info, false);
2212}
2213
2214static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
2215{
2216 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302217 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002218
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302219 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002220 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302221 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002222 return -ENOTCONN;
2223
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302224 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302225 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002226
2227 return 0;
2228}
2229
Jouni Malinen23875132011-08-30 21:57:53 +03002230static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2231 u8 *mac, struct station_parameters *params)
2232{
2233 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302234 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002235
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302236 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002237 return -EOPNOTSUPP;
2238
2239 /* Use this only for authorizing/unauthorizing a station */
2240 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2241 return -EOPNOTSUPP;
2242
2243 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302244 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2245 WMI_AP_MLME_AUTHORIZE, mac, 0);
2246 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2247 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002248}
2249
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002250static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2251 struct net_device *dev,
2252 struct ieee80211_channel *chan,
2253 enum nl80211_channel_type channel_type,
2254 unsigned int duration,
2255 u64 *cookie)
2256{
2257 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302258 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002259 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002260
2261 /* TODO: if already pending or ongoing remain-on-channel,
2262 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002263 id = ++vif->last_roc_id;
2264 if (id == 0) {
2265 /* Do not use 0 as the cookie value */
2266 id = ++vif->last_roc_id;
2267 }
2268 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002269
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302270 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2271 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002272}
2273
2274static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2275 struct net_device *dev,
2276 u64 cookie)
2277{
2278 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302279 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002280
Jouni Malinen10522612011-10-27 16:00:13 +03002281 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002282 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002283 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002284
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302285 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002286}
2287
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302288static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2289 const u8 *buf, size_t len,
2290 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002291{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302292 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002293 const u8 *pos;
2294 u8 *p2p;
2295 int p2p_len;
2296 int ret;
2297 const struct ieee80211_mgmt *mgmt;
2298
2299 mgmt = (const struct ieee80211_mgmt *) buf;
2300
2301 /* Include P2P IE(s) from the frame generated in user space. */
2302
2303 p2p = kmalloc(len, GFP_KERNEL);
2304 if (p2p == NULL)
2305 return -ENOMEM;
2306 p2p_len = 0;
2307
2308 pos = mgmt->u.probe_resp.variable;
2309 while (pos + 1 < buf + len) {
2310 if (pos + 2 + pos[1] > buf + len)
2311 break;
2312 if (ath6kl_is_p2p_ie(pos)) {
2313 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2314 p2p_len += 2 + pos[1];
2315 }
2316 pos += 2 + pos[1];
2317 }
2318
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302319 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2320 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002321 kfree(p2p);
2322 return ret;
2323}
2324
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002325static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2326 struct ieee80211_channel *chan, bool offchan,
2327 enum nl80211_channel_type channel_type,
2328 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002329 const u8 *buf, size_t len, bool no_cck,
2330 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002331{
2332 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302333 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002334 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002335 const struct ieee80211_mgmt *mgmt;
2336
2337 mgmt = (const struct ieee80211_mgmt *) buf;
2338 if (buf + len >= mgmt->u.probe_resp.variable &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302339 vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002340 ieee80211_is_probe_resp(mgmt->frame_control)) {
2341 /*
2342 * Send Probe Response frame in AP mode using a separate WMI
2343 * command to allow the target to fill in the generic IEs.
2344 */
2345 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302346 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002347 chan->center_freq);
2348 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002349
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302350 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002351 if (id == 0) {
2352 /*
2353 * 0 is a reserved value in the WMI command and shall not be
2354 * used for the command.
2355 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302356 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002357 }
2358
2359 *cookie = id;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302360 return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
2361 chan->center_freq, wait,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002362 buf, len);
2363}
2364
Jouni Malinenae32c302011-08-30 21:58:01 +03002365static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2366 struct net_device *dev,
2367 u16 frame_type, bool reg)
2368{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302369 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002370
2371 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2372 __func__, frame_type, reg);
2373 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2374 /*
2375 * Note: This notification callback is not allowed to sleep, so
2376 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2377 * hardcode target to report Probe Request frames all the time.
2378 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302379 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002380 }
2381}
2382
Kalle Valo10509f92011-12-13 14:52:07 +02002383static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2384 struct net_device *dev,
2385 struct cfg80211_sched_scan_request *request)
2386{
2387 struct ath6kl *ar = ath6kl_priv(dev);
2388 struct ath6kl_vif *vif = netdev_priv(dev);
2389 u16 interval;
2390 int ret;
2391 u8 i;
2392
2393 if (ar->state != ATH6KL_STATE_ON)
2394 return -EIO;
2395
2396 if (vif->sme_state != SME_DISCONNECTED)
2397 return -EBUSY;
2398
2399 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2400 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2401 i, DISABLE_SSID_FLAG,
2402 0, NULL);
2403 }
2404
2405 /* fw uses seconds, also make sure that it's >0 */
2406 interval = max_t(u16, 1, request->interval / 1000);
2407
2408 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2409 interval, interval,
2410 10, 0, 0, 0, 3, 0, 0, 0);
2411
2412 if (request->n_ssids && request->ssids[0].ssid_len) {
2413 for (i = 0; i < request->n_ssids; i++) {
2414 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2415 i, SPECIFIC_SSID_FLAG,
2416 request->ssids[i].ssid_len,
2417 request->ssids[i].ssid);
2418 }
2419 }
2420
2421 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2422 ATH6KL_WOW_MODE_ENABLE,
2423 WOW_FILTER_SSID,
2424 WOW_HOST_REQ_DELAY);
2425 if (ret) {
2426 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2427 return ret;
2428 }
2429
2430 /* this also clears IE in fw if it's not set */
2431 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2432 WMI_FRAME_PROBE_REQ,
2433 request->ie, request->ie_len);
2434 if (ret) {
2435 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
2436 ret);
2437 return ret;
2438 }
2439
2440 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2441 ATH6KL_HOST_MODE_ASLEEP);
2442 if (ret) {
2443 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
2444 ret);
2445 return ret;
2446 }
2447
2448 ar->state = ATH6KL_STATE_SCHED_SCAN;
2449
2450 return ret;
2451}
2452
2453static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
2454 struct net_device *dev)
2455{
2456 struct ath6kl_vif *vif = netdev_priv(dev);
2457 bool stopped;
2458
2459 stopped = __ath6kl_cfg80211_sscan_stop(vif);
2460
2461 if (!stopped)
2462 return -EIO;
2463
2464 return 0;
2465}
2466
Jouni Malinenf80574a2011-08-30 21:58:04 +03002467static const struct ieee80211_txrx_stypes
2468ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2469 [NL80211_IFTYPE_STATION] = {
2470 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2471 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2472 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2473 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2474 },
2475 [NL80211_IFTYPE_P2P_CLIENT] = {
2476 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2477 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2478 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2479 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2480 },
2481 [NL80211_IFTYPE_P2P_GO] = {
2482 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2483 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2484 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2485 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2486 },
2487};
2488
Kalle Valobdcd8172011-07-18 00:22:30 +03002489static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302490 .add_virtual_intf = ath6kl_cfg80211_add_iface,
2491 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03002492 .change_virtual_intf = ath6kl_cfg80211_change_iface,
2493 .scan = ath6kl_cfg80211_scan,
2494 .connect = ath6kl_cfg80211_connect,
2495 .disconnect = ath6kl_cfg80211_disconnect,
2496 .add_key = ath6kl_cfg80211_add_key,
2497 .get_key = ath6kl_cfg80211_get_key,
2498 .del_key = ath6kl_cfg80211_del_key,
2499 .set_default_key = ath6kl_cfg80211_set_default_key,
2500 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
2501 .set_tx_power = ath6kl_cfg80211_set_txpower,
2502 .get_tx_power = ath6kl_cfg80211_get_txpower,
2503 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
2504 .join_ibss = ath6kl_cfg80211_join_ibss,
2505 .leave_ibss = ath6kl_cfg80211_leave_ibss,
2506 .get_station = ath6kl_get_station,
2507 .set_pmksa = ath6kl_set_pmksa,
2508 .del_pmksa = ath6kl_del_pmksa,
2509 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03002510 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03002511#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002512 .suspend = __ath6kl_cfg80211_suspend,
2513 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03002514#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002515 .set_channel = ath6kl_set_channel,
2516 .add_beacon = ath6kl_add_beacon,
2517 .set_beacon = ath6kl_set_beacon,
2518 .del_beacon = ath6kl_del_beacon,
Jouni Malinen23875132011-08-30 21:57:53 +03002519 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002520 .remain_on_channel = ath6kl_remain_on_channel,
2521 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002522 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03002523 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02002524 .sched_scan_start = ath6kl_cfg80211_sscan_start,
2525 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03002526};
2527
Kalle Valo7125f012011-12-13 14:51:37 +02002528void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02002529{
Kalle Valo10509f92011-12-13 14:52:07 +02002530 ath6kl_cfg80211_sscan_disable(vif);
2531
Kalle Valoec4b7f62011-11-01 08:44:04 +02002532 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02002533 case SME_DISCONNECTED:
2534 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02002535 case SME_CONNECTING:
2536 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
2537 NULL, 0,
2538 WLAN_STATUS_UNSPECIFIED_FAILURE,
2539 GFP_KERNEL);
2540 break;
2541 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02002542 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
2543 break;
2544 }
2545
2546 if (test_bit(CONNECTED, &vif->flags) ||
2547 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02002548 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002549
2550 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02002551 clear_bit(CONNECTED, &vif->flags);
2552 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002553
2554 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02002555 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
2556 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
2557 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02002558
2559 ath6kl_cfg80211_scan_complete_event(vif, true);
2560}
2561
Kalle Valo7125f012011-12-13 14:51:37 +02002562void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
2563{
2564 struct ath6kl_vif *vif;
2565
2566 vif = ath6kl_vif_first(ar);
2567 if (!vif) {
2568 /* save the current power mode before enabling power save */
2569 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2570
2571 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
2572 ath6kl_warn("ath6kl_deep_sleep_enable: "
2573 "wmi_powermode_cmd failed\n");
2574 return;
2575 }
2576
2577 /*
2578 * FIXME: we should take ar->list_lock to protect changes in the
2579 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
2580 * sleeps.
2581 */
2582 list_for_each_entry(vif, &ar->vif_list, list)
2583 ath6kl_cfg80211_stop(vif);
2584}
2585
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302586struct ath6kl *ath6kl_core_alloc(struct device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +03002587{
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002588 struct ath6kl *ar;
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302589 struct wiphy *wiphy;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302590 u8 ctr;
Kalle Valobdcd8172011-07-18 00:22:30 +03002591
2592 /* create a new wiphy for use with cfg80211 */
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302593 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302594
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302595 if (!wiphy) {
Kalle Valobdcd8172011-07-18 00:22:30 +03002596 ath6kl_err("couldn't allocate wiphy device\n");
Kalle Valobdcd8172011-07-18 00:22:30 +03002597 return NULL;
2598 }
2599
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302600 ar = wiphy_priv(wiphy);
Vasanthakumar Thiagarajan774439a2011-11-18 10:05:26 +05302601 ar->p2p = !!ath6kl_p2p;
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302602 ar->wiphy = wiphy;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302603 ar->dev = dev;
2604
Kalle Valo71f96ee2011-11-14 19:31:30 +02002605 ar->vif_max = 1;
2606
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +05302607 ar->max_norm_iface = 1;
2608
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302609 spin_lock_init(&ar->lock);
2610 spin_lock_init(&ar->mcastpsq_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302611 spin_lock_init(&ar->list_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302612
2613 init_waitqueue_head(&ar->event_wq);
2614 sema_init(&ar->sem, 1);
2615
2616 INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302617 INIT_LIST_HEAD(&ar->vif_list);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302618
2619 clear_bit(WMI_ENABLED, &ar->flag);
2620 clear_bit(SKIP_SCAN, &ar->flag);
2621 clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
2622
2623 ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL;
2624 ar->listen_intvl_b = 0;
2625 ar->tx_pwr = 0;
2626
2627 ar->intra_bss = 1;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302628 ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
2629
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002630 ar->state = ATH6KL_STATE_OFF;
2631
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302632 memset((u8 *)ar->sta_list, 0,
2633 AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
2634
2635 /* Init the PS queues */
2636 for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
2637 spin_lock_init(&ar->sta_list[ctr].psq_lock);
2638 skb_queue_head_init(&ar->sta_list[ctr].psq);
2639 }
2640
2641 skb_queue_head_init(&ar->mcastpsq);
2642
2643 memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3);
2644
2645 return ar;
2646}
2647
2648int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
2649{
2650 struct wiphy *wiphy = ar->wiphy;
2651 int ret;
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002652
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302653 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
Jouni Malinenf80574a2011-08-30 21:58:04 +03002654
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302655 wiphy->max_remain_on_channel_duration = 5000;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002656
Kalle Valobdcd8172011-07-18 00:22:30 +03002657 /* set device pointer for wiphy */
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302658 set_wiphy_dev(wiphy, ar->dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03002659
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302660 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302661 BIT(NL80211_IFTYPE_ADHOC) |
2662 BIT(NL80211_IFTYPE_AP);
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002663 if (ar->p2p) {
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302664 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302665 BIT(NL80211_IFTYPE_P2P_CLIENT);
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002666 }
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302667
Kalle Valobdcd8172011-07-18 00:22:30 +03002668 /* max num of ssids that can be probed during scanning */
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302669 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
2670 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
2671 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
2672 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
2673 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
Kalle Valobdcd8172011-07-18 00:22:30 +03002674
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302675 wiphy->cipher_suites = cipher_suites;
2676 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
Kalle Valobdcd8172011-07-18 00:22:30 +03002677
Raja Manieae9e062011-11-07 22:52:46 +02002678 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
2679 WIPHY_WOWLAN_DISCONNECT |
2680 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
2681 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
2682 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
2683 WIPHY_WOWLAN_4WAY_HANDSHAKE;
2684 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
2685 wiphy->wowlan.pattern_min_len = 1;
2686 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
2687
Kalle Valo10509f92011-12-13 14:52:07 +02002688 wiphy->max_sched_scan_ssids = 10;
2689
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302690 ret = wiphy_register(wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03002691 if (ret < 0) {
2692 ath6kl_err("couldn't register wiphy device\n");
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302693 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +03002694 }
2695
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302696 return 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03002697}
2698
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302699static int ath6kl_init_if_data(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03002700{
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302701 vif->aggr_cntxt = aggr_init(vif->ndev);
2702 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302703 ath6kl_err("failed to initialize aggr\n");
2704 return -ENOMEM;
2705 }
Kalle Valobdcd8172011-07-18 00:22:30 +03002706
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302707 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302708 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02002709 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
2710 (unsigned long) vif);
2711
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302712 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05302713 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302714
2715 return 0;
2716}
2717
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302718void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302719{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302720 struct ath6kl *ar = vif->ar;
2721
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302722 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302723
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302724 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
2725
2726 if (vif->nw_type == ADHOC_NETWORK)
2727 ar->ibss_if_active = false;
2728
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302729 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302730
2731 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302732}
2733
2734struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302735 enum nl80211_iftype type, u8 fw_vif_idx,
2736 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302737{
2738 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302739 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302740
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302741 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302742 if (!ndev)
2743 return NULL;
2744
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302745 vif = netdev_priv(ndev);
2746 ndev->ieee80211_ptr = &vif->wdev;
2747 vif->wdev.wiphy = ar->wiphy;
2748 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302749 vif->ndev = ndev;
2750 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
2751 vif->wdev.netdev = ndev;
2752 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302753 vif->fw_vif_idx = fw_vif_idx;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302754 vif->nw_type = vif->next_mode = nw_type;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302755
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302756 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
2757 if (fw_vif_idx != 0)
2758 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
2759 0x2;
2760
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302761 init_netdev(ndev);
2762
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05302763 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302764
2765 /* TODO: Pass interface specific pointer instead of ar */
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302766 if (ath6kl_init_if_data(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302767 goto err;
2768
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302769 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302770 goto err;
2771
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302772 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05302773 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302774 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302775 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302776 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302777
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302778 if (type == NL80211_IFTYPE_ADHOC)
2779 ar->ibss_if_active = true;
2780
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05302781 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302782 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05302783 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302784
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302785 return ndev;
2786
2787err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302788 aggr_module_destroy(vif->aggr_cntxt);
2789 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302790 return NULL;
2791}
2792
2793void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar)
2794{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302795 wiphy_unregister(ar->wiphy);
2796 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03002797}