blob: 2a166cc7cc3cece2b45f5e3e5989a70f4f5faeab [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040017#include <linux/moduleparam.h>
Raja Manic08631c2011-12-16 14:24:24 +053018#include <linux/inetdevice.h>
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040019
Kalle Valobdcd8172011-07-18 00:22:30 +030020#include "core.h"
21#include "cfg80211.h"
22#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030023#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030024#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030025
Jouni Malinen6bbc7c32011-09-05 17:38:47 +030026static unsigned int ath6kl_p2p;
27
28module_param(ath6kl_p2p, uint, 0644);
29
Kalle Valobdcd8172011-07-18 00:22:30 +030030#define RATETAB_ENT(_rate, _rateid, _flags) { \
31 .bitrate = (_rate), \
32 .flags = (_flags), \
33 .hw_value = (_rateid), \
34}
35
36#define CHAN2G(_channel, _freq, _flags) { \
37 .band = IEEE80211_BAND_2GHZ, \
38 .hw_value = (_channel), \
39 .center_freq = (_freq), \
40 .flags = (_flags), \
41 .max_antenna_gain = 0, \
42 .max_power = 30, \
43}
44
45#define CHAN5G(_channel, _flags) { \
46 .band = IEEE80211_BAND_5GHZ, \
47 .hw_value = (_channel), \
48 .center_freq = 5000 + (5 * (_channel)), \
49 .flags = (_flags), \
50 .max_antenna_gain = 0, \
51 .max_power = 30, \
52}
53
54static struct ieee80211_rate ath6kl_rates[] = {
55 RATETAB_ENT(10, 0x1, 0),
56 RATETAB_ENT(20, 0x2, 0),
57 RATETAB_ENT(55, 0x4, 0),
58 RATETAB_ENT(110, 0x8, 0),
59 RATETAB_ENT(60, 0x10, 0),
60 RATETAB_ENT(90, 0x20, 0),
61 RATETAB_ENT(120, 0x40, 0),
62 RATETAB_ENT(180, 0x80, 0),
63 RATETAB_ENT(240, 0x100, 0),
64 RATETAB_ENT(360, 0x200, 0),
65 RATETAB_ENT(480, 0x400, 0),
66 RATETAB_ENT(540, 0x800, 0),
67};
68
69#define ath6kl_a_rates (ath6kl_rates + 4)
70#define ath6kl_a_rates_size 8
71#define ath6kl_g_rates (ath6kl_rates + 0)
72#define ath6kl_g_rates_size 12
73
74static struct ieee80211_channel ath6kl_2ghz_channels[] = {
75 CHAN2G(1, 2412, 0),
76 CHAN2G(2, 2417, 0),
77 CHAN2G(3, 2422, 0),
78 CHAN2G(4, 2427, 0),
79 CHAN2G(5, 2432, 0),
80 CHAN2G(6, 2437, 0),
81 CHAN2G(7, 2442, 0),
82 CHAN2G(8, 2447, 0),
83 CHAN2G(9, 2452, 0),
84 CHAN2G(10, 2457, 0),
85 CHAN2G(11, 2462, 0),
86 CHAN2G(12, 2467, 0),
87 CHAN2G(13, 2472, 0),
88 CHAN2G(14, 2484, 0),
89};
90
91static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
92 CHAN5G(34, 0), CHAN5G(36, 0),
93 CHAN5G(38, 0), CHAN5G(40, 0),
94 CHAN5G(42, 0), CHAN5G(44, 0),
95 CHAN5G(46, 0), CHAN5G(48, 0),
96 CHAN5G(52, 0), CHAN5G(56, 0),
97 CHAN5G(60, 0), CHAN5G(64, 0),
98 CHAN5G(100, 0), CHAN5G(104, 0),
99 CHAN5G(108, 0), CHAN5G(112, 0),
100 CHAN5G(116, 0), CHAN5G(120, 0),
101 CHAN5G(124, 0), CHAN5G(128, 0),
102 CHAN5G(132, 0), CHAN5G(136, 0),
103 CHAN5G(140, 0), CHAN5G(149, 0),
104 CHAN5G(153, 0), CHAN5G(157, 0),
105 CHAN5G(161, 0), CHAN5G(165, 0),
106 CHAN5G(184, 0), CHAN5G(188, 0),
107 CHAN5G(192, 0), CHAN5G(196, 0),
108 CHAN5G(200, 0), CHAN5G(204, 0),
109 CHAN5G(208, 0), CHAN5G(212, 0),
110 CHAN5G(216, 0),
111};
112
113static struct ieee80211_supported_band ath6kl_band_2ghz = {
114 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
115 .channels = ath6kl_2ghz_channels,
116 .n_bitrates = ath6kl_g_rates_size,
117 .bitrates = ath6kl_g_rates,
118};
119
120static struct ieee80211_supported_band ath6kl_band_5ghz = {
121 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
122 .channels = ath6kl_5ghz_a_channels,
123 .n_bitrates = ath6kl_a_rates_size,
124 .bitrates = ath6kl_a_rates,
125};
126
Jouni Malinen837cb972011-10-11 17:31:57 +0300127#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
128
Kalle Valo10509f92011-12-13 14:52:07 +0200129/* returns true if scheduled scan was stopped */
130static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
131{
132 struct ath6kl *ar = vif->ar;
133
134 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
135 return false;
136
137 del_timer_sync(&vif->sched_scan_timer);
138
139 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
140 ATH6KL_HOST_MODE_AWAKE);
141
142 ar->state = ATH6KL_STATE_ON;
143
144 return true;
145}
146
147static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
148{
149 struct ath6kl *ar = vif->ar;
150 bool stopped;
151
152 stopped = __ath6kl_cfg80211_sscan_stop(vif);
153
154 if (!stopped)
155 return;
156
157 cfg80211_sched_scan_stopped(ar->wiphy);
158}
159
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530160static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300161 enum nl80211_wpa_versions wpa_version)
162{
163 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
164
165 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530166 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300167 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530168 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300169 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530170 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300171 } else {
172 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
173 return -ENOTSUPP;
174 }
175
176 return 0;
177}
178
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530179static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300180 enum nl80211_auth_type auth_type)
181{
Kalle Valobdcd8172011-07-18 00:22:30 +0300182 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
183
184 switch (auth_type) {
185 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530186 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300187 break;
188 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530189 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300190 break;
191 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530192 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300193 break;
194
195 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530196 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300197 break;
198
199 default:
200 ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type);
201 return -ENOTSUPP;
202 }
203
204 return 0;
205}
206
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530207static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300208{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530209 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
210 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
211 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300212
213 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
214 __func__, cipher, ucast);
215
216 switch (cipher) {
217 case 0:
218 /* our own hack to use value 0 as no crypto used */
219 *ar_cipher = NONE_CRYPT;
220 *ar_cipher_len = 0;
221 break;
222 case WLAN_CIPHER_SUITE_WEP40:
223 *ar_cipher = WEP_CRYPT;
224 *ar_cipher_len = 5;
225 break;
226 case WLAN_CIPHER_SUITE_WEP104:
227 *ar_cipher = WEP_CRYPT;
228 *ar_cipher_len = 13;
229 break;
230 case WLAN_CIPHER_SUITE_TKIP:
231 *ar_cipher = TKIP_CRYPT;
232 *ar_cipher_len = 0;
233 break;
234 case WLAN_CIPHER_SUITE_CCMP:
235 *ar_cipher = AES_CRYPT;
236 *ar_cipher_len = 0;
237 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200238 case WLAN_CIPHER_SUITE_SMS4:
239 *ar_cipher = WAPI_CRYPT;
240 *ar_cipher_len = 0;
241 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300242 default:
243 ath6kl_err("cipher 0x%x not supported\n", cipher);
244 return -ENOTSUPP;
245 }
246
247 return 0;
248}
249
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530250static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300251{
252 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
253
254 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530255 if (vif->auth_mode == WPA_AUTH)
256 vif->auth_mode = WPA_PSK_AUTH;
257 else if (vif->auth_mode == WPA2_AUTH)
258 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300259 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530260 if (vif->auth_mode == WPA_AUTH)
261 vif->auth_mode = WPA_AUTH_CCKM;
262 else if (vif->auth_mode == WPA2_AUTH)
263 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300264 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530265 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300266 }
267}
268
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530269static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300270{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530271 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530272
Kalle Valobdcd8172011-07-18 00:22:30 +0300273 if (!test_bit(WMI_READY, &ar->flag)) {
274 ath6kl_err("wmi is not ready\n");
275 return false;
276 }
277
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530278 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300279 ath6kl_err("wlan disabled\n");
280 return false;
281 }
282
283 return true;
284}
285
Kevin Fang6981ffd2011-10-07 08:51:19 +0800286static bool ath6kl_is_wpa_ie(const u8 *pos)
287{
288 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
289 pos[2] == 0x00 && pos[3] == 0x50 &&
290 pos[4] == 0xf2 && pos[5] == 0x01;
291}
292
293static bool ath6kl_is_rsn_ie(const u8 *pos)
294{
295 return pos[0] == WLAN_EID_RSN;
296}
297
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700298static bool ath6kl_is_wps_ie(const u8 *pos)
299{
300 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
301 pos[1] >= 4 &&
302 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
303 pos[5] == 0x04);
304}
305
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530306static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
307 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800308{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530309 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800310 const u8 *pos;
311 u8 *buf = NULL;
312 size_t len = 0;
313 int ret;
314
315 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700316 * Clear previously set flag
317 */
318
319 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
320
321 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800322 * Filter out RSN/WPA IE(s)
323 */
324
325 if (ies && ies_len) {
326 buf = kmalloc(ies_len, GFP_KERNEL);
327 if (buf == NULL)
328 return -ENOMEM;
329 pos = ies;
330
331 while (pos + 1 < ies + ies_len) {
332 if (pos + 2 + pos[1] > ies + ies_len)
333 break;
334 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
335 memcpy(buf + len, pos, 2 + pos[1]);
336 len += 2 + pos[1];
337 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700338
339 if (ath6kl_is_wps_ie(pos))
340 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
341
Kevin Fang6981ffd2011-10-07 08:51:19 +0800342 pos += 2 + pos[1];
343 }
344 }
345
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530346 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
347 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800348 kfree(buf);
349 return ret;
350}
351
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530352static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
353{
354 switch (type) {
355 case NL80211_IFTYPE_STATION:
356 *nw_type = INFRA_NETWORK;
357 break;
358 case NL80211_IFTYPE_ADHOC:
359 *nw_type = ADHOC_NETWORK;
360 break;
361 case NL80211_IFTYPE_AP:
362 *nw_type = AP_NETWORK;
363 break;
364 case NL80211_IFTYPE_P2P_CLIENT:
365 *nw_type = INFRA_NETWORK;
366 break;
367 case NL80211_IFTYPE_P2P_GO:
368 *nw_type = AP_NETWORK;
369 break;
370 default:
371 ath6kl_err("invalid interface type %u\n", type);
372 return -ENOTSUPP;
373 }
374
375 return 0;
376}
377
378static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
379 u8 *if_idx, u8 *nw_type)
380{
381 int i;
382
383 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
384 return false;
385
386 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
387 ar->num_vif))
388 return false;
389
390 if (type == NL80211_IFTYPE_STATION ||
391 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200392 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530393 if ((ar->avail_idx_map >> i) & BIT(0)) {
394 *if_idx = i;
395 return true;
396 }
397 }
398 }
399
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530400 if (type == NL80211_IFTYPE_P2P_CLIENT ||
401 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200402 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530403 if ((ar->avail_idx_map >> i) & BIT(0)) {
404 *if_idx = i;
405 return true;
406 }
407 }
408 }
409
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530410 return false;
411}
412
Kalle Valobdcd8172011-07-18 00:22:30 +0300413static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
414 struct cfg80211_connect_params *sme)
415{
416 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530417 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300418 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800419 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Kalle Valobdcd8172011-07-18 00:22:30 +0300420
Kalle Valo10509f92011-12-13 14:52:07 +0200421 ath6kl_cfg80211_sscan_disable(vif);
422
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530423 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300424
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530425 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300426 return -EIO;
427
428 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
429 ath6kl_err("destroy in progress\n");
430 return -EBUSY;
431 }
432
433 if (test_bit(SKIP_SCAN, &ar->flag) &&
434 ((sme->channel && sme->channel->center_freq == 0) ||
435 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
436 ath6kl_err("SkipScan: channel or bssid invalid\n");
437 return -EINVAL;
438 }
439
440 if (down_interruptible(&ar->sem)) {
441 ath6kl_err("busy, couldn't get access\n");
442 return -ERESTARTSYS;
443 }
444
445 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
446 ath6kl_err("busy, destroy in progress\n");
447 up(&ar->sem);
448 return -EBUSY;
449 }
450
451 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
452 /*
453 * sleep until the command queue drains
454 */
455 wait_event_interruptible_timeout(ar->event_wq,
456 ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
457 WMI_TIMEOUT);
458 if (signal_pending(current)) {
459 ath6kl_err("cmd queue drain timeout\n");
460 up(&ar->sem);
461 return -EINTR;
462 }
463 }
464
Jouni Malinen6e786cb2011-12-15 14:16:00 +0200465 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
466 if (status) {
467 up(&ar->sem);
468 return status;
469 }
470
471 if (sme->ie == NULL || sme->ie_len == 0)
Raja Mani542c5192011-11-15 14:14:56 +0530472 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800473
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530474 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530475 vif->ssid_len == sme->ssid_len &&
476 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530477 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530478 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
479 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530480 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300481
482 up(&ar->sem);
483 if (status) {
484 ath6kl_err("wmi_reconnect_cmd failed\n");
485 return -EIO;
486 }
487 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530488 } else if (vif->ssid_len == sme->ssid_len &&
489 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530490 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300491 }
492
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530493 memset(vif->ssid, 0, sizeof(vif->ssid));
494 vif->ssid_len = sme->ssid_len;
495 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300496
497 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530498 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300499
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530500 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300501 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530502 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300503
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530504 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300505
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530506 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300507 if (status) {
508 up(&ar->sem);
509 return status;
510 }
511
512 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530513 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300514 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530515 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300516
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530517 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300518
519 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530520 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300521
522 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530523 (vif->auth_mode == NONE_AUTH) &&
524 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300525 struct ath6kl_key *key = NULL;
526
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530527 if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300528 ath6kl_err("key index %d out of bounds\n",
529 sme->key_idx);
530 up(&ar->sem);
531 return -ENOENT;
532 }
533
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530534 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300535 key->key_len = sme->key_len;
536 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530537 key->cipher = vif->prwise_crypto;
538 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300539
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530540 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530541 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300542 GROUP_USAGE | TX_USAGE,
543 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200544 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300545 key->key, KEY_OP_INIT_VAL, NULL,
546 NO_SYNC_WMIFLAG);
547 }
548
549 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530550 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530551 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
552 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300553 ath6kl_err("couldn't set bss filtering\n");
554 up(&ar->sem);
555 return -EIO;
556 }
557 }
558
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530559 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300560
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800561 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
562 nw_subtype = SUBTYPE_P2PCLIENT;
563
Kalle Valobdcd8172011-07-18 00:22:30 +0300564 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
565 "%s: connect called with authmode %d dot11 auth %d"
566 " PW crypto %d PW crypto len %d GRP crypto %d"
567 " GRP crypto len %d channel hint %u\n",
568 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530569 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
570 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530571 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300572
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530573 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530574 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530575 vif->dot11_auth_mode, vif->auth_mode,
576 vif->prwise_crypto,
577 vif->prwise_crypto_len,
578 vif->grp_crypto, vif->grp_crypto_len,
579 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530580 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800581 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300582
583 up(&ar->sem);
584
585 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530586 memset(vif->ssid, 0, sizeof(vif->ssid));
587 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300588 ath6kl_err("invalid request\n");
589 return -ENOENT;
590 } else if (status) {
591 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
592 return -EIO;
593 }
594
595 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530596 ((vif->auth_mode == WPA_PSK_AUTH)
597 || (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530598 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300599 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
600 }
601
602 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530603 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300604
605 return 0;
606}
607
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530608static struct cfg80211_bss *
609ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
610 enum network_type nw_type,
611 const u8 *bssid,
612 struct ieee80211_channel *chan,
613 const u8 *beacon_ie,
614 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300615{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530616 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300617 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530618 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300619 u8 *ie;
620
Raja Mani4eab6f42011-11-09 17:02:23 +0530621 if (nw_type & ADHOC_NETWORK) {
622 cap_mask = WLAN_CAPABILITY_IBSS;
623 cap_val = WLAN_CAPABILITY_IBSS;
624 } else {
625 cap_mask = WLAN_CAPABILITY_ESS;
626 cap_val = WLAN_CAPABILITY_ESS;
627 }
628
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530629 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530630 vif->ssid, vif->ssid_len,
631 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300632 if (bss == NULL) {
633 /*
634 * Since cfg80211 may not yet know about the BSS,
635 * generate a partial entry until the first BSS info
636 * event becomes available.
637 *
638 * Prepend SSID element since it is not included in the Beacon
639 * IEs from the target.
640 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530641 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300642 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530643 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300644 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530645 ie[1] = vif->ssid_len;
646 memcpy(ie + 2, vif->ssid, vif->ssid_len);
647 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530648 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530649 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530650 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300651 0, GFP_KERNEL);
652 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530653 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
654 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300655 kfree(ie);
656 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530657 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300658
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530659 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300660}
661
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530662void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300663 u8 *bssid, u16 listen_intvl,
664 u16 beacon_intvl,
665 enum network_type nw_type,
666 u8 beacon_ie_len, u8 assoc_req_len,
667 u8 assoc_resp_len, u8 *assoc_info)
668{
Jouni Malinen01cac472011-09-19 19:14:59 +0300669 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530670 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530671 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300672
673 /* capinfo + listen interval */
674 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
675
676 /* capinfo + status code + associd */
677 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
678
679 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
680 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
681 assoc_resp_ie_offset;
682
683 assoc_req_len -= assoc_req_ie_offset;
684 assoc_resp_len -= assoc_resp_ie_offset;
685
Jouni Malinen32c10872011-09-19 19:15:07 +0300686 /*
687 * Store Beacon interval here; DTIM period will be available only once
688 * a Beacon frame from the AP is seen.
689 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530690 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530691 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300692
Kalle Valobdcd8172011-07-18 00:22:30 +0300693 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530694 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300695 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
696 "%s: ath6k not in ibss mode\n", __func__);
697 return;
698 }
699 }
700
701 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530702 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
703 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300704 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
705 "%s: ath6k not in station mode\n", __func__);
706 return;
707 }
708 }
709
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530710 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300711
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530712 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
713 assoc_info, beacon_ie_len);
714 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530715 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300716 return;
717 }
718
Raja Mani4eab6f42011-11-09 17:02:23 +0530719 if (nw_type & ADHOC_NETWORK) {
720 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
721 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
722 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530723 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300724 return;
725 }
726
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530727 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300728 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530729 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530730 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300731 assoc_req_ie, assoc_req_len,
732 assoc_resp_ie, assoc_resp_len,
733 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530734 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530735 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300736 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530737 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
738 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300739 }
740}
741
742static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
743 struct net_device *dev, u16 reason_code)
744{
Kalle Valod6d5c062011-11-25 13:17:37 +0200745 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530746 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300747
748 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
749 reason_code);
750
Kalle Valo10509f92011-12-13 14:52:07 +0200751 ath6kl_cfg80211_sscan_disable(vif);
752
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530753 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300754 return -EIO;
755
756 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
757 ath6kl_err("busy, destroy in progress\n");
758 return -EBUSY;
759 }
760
761 if (down_interruptible(&ar->sem)) {
762 ath6kl_err("busy, couldn't get access\n");
763 return -ERESTARTSYS;
764 }
765
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530766 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530767 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530768 memset(vif->ssid, 0, sizeof(vif->ssid));
769 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300770
771 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530772 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300773
774 up(&ar->sem);
775
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530776 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530777
Kalle Valobdcd8172011-07-18 00:22:30 +0300778 return 0;
779}
780
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530781void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300782 u8 *bssid, u8 assoc_resp_len,
783 u8 *assoc_info, u16 proto_reason)
784{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530785 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530786
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530787 if (vif->scan_req) {
788 cfg80211_scan_done(vif->scan_req, true);
789 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300790 }
791
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530792 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530793 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300794 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
795 "%s: ath6k not in ibss mode\n", __func__);
796 return;
797 }
798 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530799 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300800 return;
801 }
802
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530803 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530804 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
805 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300806 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
807 "%s: ath6k not in station mode\n", __func__);
808 return;
809 }
810 }
811
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530812 /*
813 * Send a disconnect command to target when a disconnect event is
814 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
815 * request from host) to make the firmware stop trying to connect even
816 * after giving disconnect event. There will be one more disconnect
817 * event for this disconnect command with reason code DISCONNECT_CMD
818 * which will be notified to cfg80211.
819 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300820
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530821 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530822 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300823 return;
824 }
825
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530826 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300827
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530828 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530829 cfg80211_connect_result(vif->ndev,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530830 bssid, NULL, 0,
831 NULL, 0,
832 WLAN_STATUS_UNSPECIFIED_FAILURE,
833 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530834 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530835 cfg80211_disconnected(vif->ndev, reason,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530836 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300837 }
838
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530839 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300840}
841
Kalle Valobdcd8172011-07-18 00:22:30 +0300842static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
843 struct cfg80211_scan_request *request)
844{
Kalle Valod6d5c062011-11-25 13:17:37 +0200845 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530846 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300847 s8 n_channels = 0;
848 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300849 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530850 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300851
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530852 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300853 return -EIO;
854
Kalle Valo10509f92011-12-13 14:52:07 +0200855 ath6kl_cfg80211_sscan_disable(vif);
856
Kalle Valobdcd8172011-07-18 00:22:30 +0300857 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530858 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300859 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530860 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530861 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300862 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
863 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300864 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300865 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300866 }
867 }
868
869 if (request->n_ssids && request->ssids[0].ssid_len) {
870 u8 i;
871
872 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
873 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
874
875 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530876 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
877 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300878 request->ssids[i].ssid_len,
879 request->ssids[i].ssid);
880 }
881
Kalle Valo10509f92011-12-13 14:52:07 +0200882 /*
883 * FIXME: we should clear the IE in fw if it's not set so just
884 * remove the check altogether
885 */
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300886 if (request->ie) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530887 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
888 WMI_FRAME_PROBE_REQ,
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300889 request->ie, request->ie_len);
890 if (ret) {
891 ath6kl_err("failed to set Probe Request appie for "
892 "scan");
893 return ret;
894 }
895 }
896
Jouni Malinen11869be2011-09-02 20:07:06 +0300897 /*
898 * Scan only the requested channels if the request specifies a set of
899 * channels. If the list is longer than the target supports, do not
900 * configure the list and instead, scan all available channels.
901 */
902 if (request->n_channels > 0 &&
903 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300904 u8 i;
905
Jouni Malinen11869be2011-09-02 20:07:06 +0300906 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300907
908 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
909 if (channels == NULL) {
910 ath6kl_warn("failed to set scan channels, "
911 "scan all channels");
912 n_channels = 0;
913 }
914
915 for (i = 0; i < n_channels; i++)
916 channels[i] = request->channels[i]->center_freq;
917 }
918
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530919 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530920 force_fg_scan = 1;
921
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800922 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
923 ar->fw_capabilities)) {
924 /*
925 * If capable of doing P2P mgmt operations using
926 * station interface, send additional information like
927 * supported rates to advertise and xmit rates for
928 * probe requests
929 */
930 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
931 WMI_LONG_SCAN, force_fg_scan,
932 false, 0, 0, n_channels,
933 channels, request->no_cck,
934 request->rates);
935 } else {
936 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
937 WMI_LONG_SCAN, force_fg_scan,
938 false, 0, 0, n_channels,
939 channels);
940 }
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300941 if (ret)
Kalle Valobdcd8172011-07-18 00:22:30 +0300942 ath6kl_err("wmi_startscan_cmd failed\n");
Jouni Malinen11869be2011-09-02 20:07:06 +0300943 else
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530944 vif->scan_req = request;
Kalle Valobdcd8172011-07-18 00:22:30 +0300945
Edward Lu1276c9e2011-08-30 21:58:00 +0300946 kfree(channels);
947
Kalle Valobdcd8172011-07-18 00:22:30 +0300948 return ret;
949}
950
Kalle Valo1c17d312011-11-01 08:43:56 +0200951void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300952{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530953 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300954 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300955
Kalle Valo1c17d312011-11-01 08:43:56 +0200956 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
957 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300958
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530959 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300960 return;
Kalle Valobdcd8172011-07-18 00:22:30 +0300961
Kalle Valo1c17d312011-11-01 08:43:56 +0200962 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300963 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300964
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530965 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
966 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530967 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
968 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300969 0, NULL);
970 }
971 }
972
973out:
Kalle Valocb938212011-10-27 18:47:46 +0300974 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530975 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300976}
977
978static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
979 u8 key_index, bool pairwise,
980 const u8 *mac_addr,
981 struct key_params *params)
982{
Kalle Valod6d5c062011-11-25 13:17:37 +0200983 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530984 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300985 struct ath6kl_key *key = NULL;
986 u8 key_usage;
987 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +0300988
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530989 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300990 return -EIO;
991
Jouni Malinen837cb972011-10-11 17:31:57 +0300992 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
993 if (params->key_len != WMI_KRK_LEN)
994 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530995 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
996 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +0300997 }
998
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530999 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001000 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1001 "%s: key index %d out of bounds\n", __func__,
1002 key_index);
1003 return -ENOENT;
1004 }
1005
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301006 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001007 memset(key, 0, sizeof(struct ath6kl_key));
1008
1009 if (pairwise)
1010 key_usage = PAIRWISE_USAGE;
1011 else
1012 key_usage = GROUP_USAGE;
1013
1014 if (params) {
Dai Shuibing5e070212011-11-03 11:39:37 +02001015 int seq_len = params->seq_len;
1016 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1017 seq_len > ATH6KL_KEY_SEQ_LEN) {
1018 /* Only first half of the WPI PN is configured */
1019 seq_len = ATH6KL_KEY_SEQ_LEN;
1020 }
Kalle Valobdcd8172011-07-18 00:22:30 +03001021 if (params->key_len > WLAN_MAX_KEY_LEN ||
Dai Shuibing5e070212011-11-03 11:39:37 +02001022 seq_len > sizeof(key->seq))
Kalle Valobdcd8172011-07-18 00:22:30 +03001023 return -EINVAL;
1024
1025 key->key_len = params->key_len;
1026 memcpy(key->key, params->key, key->key_len);
Dai Shuibing5e070212011-11-03 11:39:37 +02001027 key->seq_len = seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001028 memcpy(key->seq, params->seq, key->seq_len);
1029 key->cipher = params->cipher;
1030 }
1031
1032 switch (key->cipher) {
1033 case WLAN_CIPHER_SUITE_WEP40:
1034 case WLAN_CIPHER_SUITE_WEP104:
1035 key_type = WEP_CRYPT;
1036 break;
1037
1038 case WLAN_CIPHER_SUITE_TKIP:
1039 key_type = TKIP_CRYPT;
1040 break;
1041
1042 case WLAN_CIPHER_SUITE_CCMP:
1043 key_type = AES_CRYPT;
1044 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001045 case WLAN_CIPHER_SUITE_SMS4:
1046 key_type = WAPI_CRYPT;
1047 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001048
1049 default:
1050 return -ENOTSUPP;
1051 }
1052
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301053 if (((vif->auth_mode == WPA_PSK_AUTH)
1054 || (vif->auth_mode == WPA2_PSK_AUTH))
Kalle Valobdcd8172011-07-18 00:22:30 +03001055 && (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301056 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001057
1058 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1059 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1060 __func__, key_index, key->key_len, key_type,
1061 key_usage, key->seq_len);
1062
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301063 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001064 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
1065 key_type == WAPI_CRYPT) && params) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001066 ar->ap_mode_bkey.valid = true;
1067 ar->ap_mode_bkey.key_index = key_index;
1068 ar->ap_mode_bkey.key_type = key_type;
1069 ar->ap_mode_bkey.key_len = key->key_len;
1070 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301071 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001072 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1073 "key configuration until AP mode has been "
1074 "started\n");
1075 /*
1076 * The key will be set in ath6kl_connect_ap_mode() once
1077 * the connected event is received from the target.
1078 */
1079 return 0;
1080 }
1081 }
1082
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301083 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301084 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001085 /*
1086 * Store the key locally so that it can be re-configured after
1087 * the AP mode has properly started
1088 * (ath6kl_install_statioc_wep_keys).
1089 */
1090 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1091 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301092 vif->wep_key_list[key_index].key_len = key->key_len;
1093 memcpy(vif->wep_key_list[key_index].key, key->key,
1094 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001095 return 0;
1096 }
1097
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301098 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001099 key_type, key_usage, key->key_len,
1100 key->seq, key->seq_len, key->key,
1101 KEY_OP_INIT_VAL,
1102 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001103}
1104
1105static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1106 u8 key_index, bool pairwise,
1107 const u8 *mac_addr)
1108{
Kalle Valod6d5c062011-11-25 13:17:37 +02001109 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301110 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001111
1112 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1113
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301114 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001115 return -EIO;
1116
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301117 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001118 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1119 "%s: key index %d out of bounds\n", __func__,
1120 key_index);
1121 return -ENOENT;
1122 }
1123
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301124 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001125 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1126 "%s: index %d is empty\n", __func__, key_index);
1127 return 0;
1128 }
1129
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301130 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001131
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301132 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001133}
1134
1135static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1136 u8 key_index, bool pairwise,
1137 const u8 *mac_addr, void *cookie,
1138 void (*callback) (void *cookie,
1139 struct key_params *))
1140{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301141 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001142 struct ath6kl_key *key = NULL;
1143 struct key_params params;
1144
1145 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1146
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301147 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001148 return -EIO;
1149
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301150 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001151 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1152 "%s: key index %d out of bounds\n", __func__,
1153 key_index);
1154 return -ENOENT;
1155 }
1156
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301157 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001158 memset(&params, 0, sizeof(params));
1159 params.cipher = key->cipher;
1160 params.key_len = key->key_len;
1161 params.seq_len = key->seq_len;
1162 params.seq = key->seq;
1163 params.key = key->key;
1164
1165 callback(cookie, &params);
1166
1167 return key->key_len ? 0 : -ENOENT;
1168}
1169
1170static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1171 struct net_device *ndev,
1172 u8 key_index, bool unicast,
1173 bool multicast)
1174{
Kalle Valod6d5c062011-11-25 13:17:37 +02001175 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301176 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001177 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001178 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001179 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001180
1181 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1182
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301183 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001184 return -EIO;
1185
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301186 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001187 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1188 "%s: key index %d out of bounds\n",
1189 __func__, key_index);
1190 return -ENOENT;
1191 }
1192
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301193 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001194 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1195 __func__, key_index);
1196 return -EINVAL;
1197 }
1198
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301199 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301200 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001201 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301202 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001203 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001204 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301205 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001206 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301207 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001208
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301209 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001210 return 0; /* Delay until AP mode has been started */
1211
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001212 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1213 vif->def_txkey_index,
1214 key_type, key_usage,
1215 key->key_len, key->seq, key->seq_len,
1216 key->key,
1217 KEY_OP_INIT_VAL, NULL,
1218 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001219}
1220
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301221void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001222 bool ismcast)
1223{
1224 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1225 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1226
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301227 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001228 (ismcast ? NL80211_KEYTYPE_GROUP :
1229 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1230 GFP_KERNEL);
1231}
1232
1233static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1234{
1235 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301236 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001237 int ret;
1238
1239 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1240 changed);
1241
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301242 vif = ath6kl_vif_first(ar);
1243 if (!vif)
1244 return -EIO;
1245
1246 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001247 return -EIO;
1248
1249 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1250 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1251 if (ret != 0) {
1252 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1253 return -EIO;
1254 }
1255 }
1256
1257 return 0;
1258}
1259
1260/*
1261 * The type nl80211_tx_power_setting replaces the following
1262 * data type from 2.6.36 onwards
1263*/
1264static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1265 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001266 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001267{
1268 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301269 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001270 u8 ath6kl_dbm;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001271 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001272
1273 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1274 type, dbm);
1275
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301276 vif = ath6kl_vif_first(ar);
1277 if (!vif)
1278 return -EIO;
1279
1280 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001281 return -EIO;
1282
1283 switch (type) {
1284 case NL80211_TX_POWER_AUTOMATIC:
1285 return 0;
1286 case NL80211_TX_POWER_LIMITED:
1287 ar->tx_pwr = ath6kl_dbm = dbm;
1288 break;
1289 default:
1290 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1291 __func__, type);
1292 return -EOPNOTSUPP;
1293 }
1294
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301295 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001296
1297 return 0;
1298}
1299
1300static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1301{
1302 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301303 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001304
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301305 vif = ath6kl_vif_first(ar);
1306 if (!vif)
1307 return -EIO;
1308
1309 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001310 return -EIO;
1311
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301312 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001313 ar->tx_pwr = 0;
1314
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301315 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001316 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1317 return -EIO;
1318 }
1319
1320 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1321 5 * HZ);
1322
1323 if (signal_pending(current)) {
1324 ath6kl_err("target did not respond\n");
1325 return -EINTR;
1326 }
1327 }
1328
1329 *dbm = ar->tx_pwr;
1330 return 0;
1331}
1332
1333static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1334 struct net_device *dev,
1335 bool pmgmt, int timeout)
1336{
1337 struct ath6kl *ar = ath6kl_priv(dev);
1338 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301339 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001340
1341 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1342 __func__, pmgmt, timeout);
1343
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301344 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001345 return -EIO;
1346
1347 if (pmgmt) {
1348 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1349 mode.pwr_mode = REC_POWER;
1350 } else {
1351 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1352 mode.pwr_mode = MAX_PERF_POWER;
1353 }
1354
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301355 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
1356 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001357 ath6kl_err("wmi_powermode_cmd failed\n");
1358 return -EIO;
1359 }
1360
1361 return 0;
1362}
1363
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301364static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1365 char *name,
1366 enum nl80211_iftype type,
1367 u32 *flags,
1368 struct vif_params *params)
1369{
1370 struct ath6kl *ar = wiphy_priv(wiphy);
1371 struct net_device *ndev;
1372 u8 if_idx, nw_type;
1373
Kalle Valo71f96ee2011-11-14 19:31:30 +02001374 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301375 ath6kl_err("Reached maximum number of supported vif\n");
1376 return ERR_PTR(-EINVAL);
1377 }
1378
1379 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1380 ath6kl_err("Not a supported interface type\n");
1381 return ERR_PTR(-EINVAL);
1382 }
1383
1384 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1385 if (!ndev)
1386 return ERR_PTR(-ENOMEM);
1387
1388 ar->num_vif++;
1389
1390 return ndev;
1391}
1392
1393static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1394 struct net_device *ndev)
1395{
1396 struct ath6kl *ar = wiphy_priv(wiphy);
1397 struct ath6kl_vif *vif = netdev_priv(ndev);
1398
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301399 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301400 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301401 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301402
1403 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1404
1405 ath6kl_deinit_if_data(vif);
1406
1407 return 0;
1408}
1409
Kalle Valobdcd8172011-07-18 00:22:30 +03001410static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1411 struct net_device *ndev,
1412 enum nl80211_iftype type, u32 *flags,
1413 struct vif_params *params)
1414{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301415 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001416
1417 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1418
Kalle Valobdcd8172011-07-18 00:22:30 +03001419 switch (type) {
1420 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301421 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001422 break;
1423 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301424 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001425 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001426 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301427 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001428 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001429 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301430 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001431 break;
1432 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301433 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001434 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001435 default:
1436 ath6kl_err("invalid interface type %u\n", type);
1437 return -EOPNOTSUPP;
1438 }
1439
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301440 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001441
1442 return 0;
1443}
1444
1445static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1446 struct net_device *dev,
1447 struct cfg80211_ibss_params *ibss_param)
1448{
1449 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301450 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001451 int status;
1452
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301453 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001454 return -EIO;
1455
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301456 vif->ssid_len = ibss_param->ssid_len;
1457 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001458
1459 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301460 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001461
1462 if (ibss_param->channel_fixed) {
1463 /*
1464 * TODO: channel_fixed: The channel should be fixed, do not
1465 * search for IBSSs to join on other channels. Target
1466 * firmware does not support this feature, needs to be
1467 * updated.
1468 */
1469 return -EOPNOTSUPP;
1470 }
1471
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301472 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001473 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301474 memcpy(vif->req_bssid, ibss_param->bssid,
1475 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001476
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301477 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001478
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301479 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001480 if (status)
1481 return status;
1482
1483 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301484 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1485 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001486 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301487 ath6kl_set_cipher(vif, 0, true);
1488 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001489 }
1490
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301491 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001492
1493 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1494 "%s: connect called with authmode %d dot11 auth %d"
1495 " PW crypto %d PW crypto len %d GRP crypto %d"
1496 " GRP crypto len %d channel hint %u\n",
1497 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301498 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1499 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301500 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001501
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301502 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301503 vif->dot11_auth_mode, vif->auth_mode,
1504 vif->prwise_crypto,
1505 vif->prwise_crypto_len,
1506 vif->grp_crypto, vif->grp_crypto_len,
1507 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301508 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001509 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301510 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001511
1512 return 0;
1513}
1514
1515static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1516 struct net_device *dev)
1517{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301518 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001519
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301520 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001521 return -EIO;
1522
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301523 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301524 memset(vif->ssid, 0, sizeof(vif->ssid));
1525 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001526
1527 return 0;
1528}
1529
1530static const u32 cipher_suites[] = {
1531 WLAN_CIPHER_SUITE_WEP40,
1532 WLAN_CIPHER_SUITE_WEP104,
1533 WLAN_CIPHER_SUITE_TKIP,
1534 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001535 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001536 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001537};
1538
1539static bool is_rate_legacy(s32 rate)
1540{
1541 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1542 6000, 9000, 12000, 18000, 24000,
1543 36000, 48000, 54000
1544 };
1545 u8 i;
1546
1547 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1548 if (rate == legacy[i])
1549 return true;
1550
1551 return false;
1552}
1553
1554static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1555{
1556 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1557 52000, 58500, 65000, 72200
1558 };
1559 u8 i;
1560
1561 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1562 if (rate == ht20[i]) {
1563 if (i == ARRAY_SIZE(ht20) - 1)
1564 /* last rate uses sgi */
1565 *sgi = true;
1566 else
1567 *sgi = false;
1568
1569 *mcs = i;
1570 return true;
1571 }
1572 }
1573 return false;
1574}
1575
1576static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1577{
1578 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1579 81000, 108000, 121500, 135000,
1580 150000
1581 };
1582 u8 i;
1583
1584 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1585 if (rate == ht40[i]) {
1586 if (i == ARRAY_SIZE(ht40) - 1)
1587 /* last rate uses sgi */
1588 *sgi = true;
1589 else
1590 *sgi = false;
1591
1592 *mcs = i;
1593 return true;
1594 }
1595 }
1596
1597 return false;
1598}
1599
1600static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1601 u8 *mac, struct station_info *sinfo)
1602{
1603 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301604 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001605 long left;
1606 bool sgi;
1607 s32 rate;
1608 int ret;
1609 u8 mcs;
1610
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301611 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001612 return -ENOENT;
1613
1614 if (down_interruptible(&ar->sem))
1615 return -EBUSY;
1616
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301617 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001618
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301619 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001620
1621 if (ret != 0) {
1622 up(&ar->sem);
1623 return -EIO;
1624 }
1625
1626 left = wait_event_interruptible_timeout(ar->event_wq,
1627 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301628 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001629 WMI_TIMEOUT);
1630
1631 up(&ar->sem);
1632
1633 if (left == 0)
1634 return -ETIMEDOUT;
1635 else if (left < 0)
1636 return left;
1637
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301638 if (vif->target_stats.rx_byte) {
1639 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001640 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301641 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001642 sinfo->filled |= STATION_INFO_RX_PACKETS;
1643 }
1644
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301645 if (vif->target_stats.tx_byte) {
1646 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001647 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301648 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001649 sinfo->filled |= STATION_INFO_TX_PACKETS;
1650 }
1651
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301652 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001653 sinfo->filled |= STATION_INFO_SIGNAL;
1654
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301655 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001656
1657 if (is_rate_legacy(rate)) {
1658 sinfo->txrate.legacy = rate / 100;
1659 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1660 if (sgi) {
1661 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1662 sinfo->txrate.mcs = mcs - 1;
1663 } else {
1664 sinfo->txrate.mcs = mcs;
1665 }
1666
1667 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1668 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1669 if (sgi) {
1670 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1671 sinfo->txrate.mcs = mcs - 1;
1672 } else {
1673 sinfo->txrate.mcs = mcs;
1674 }
1675
1676 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1677 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1678 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001679 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1680 "invalid rate from stats: %d\n", rate);
1681 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001682 return 0;
1683 }
1684
1685 sinfo->filled |= STATION_INFO_TX_BITRATE;
1686
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301687 if (test_bit(CONNECTED, &vif->flags) &&
1688 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301689 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001690 sinfo->filled |= STATION_INFO_BSS_PARAM;
1691 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301692 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1693 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001694 }
1695
Kalle Valobdcd8172011-07-18 00:22:30 +03001696 return 0;
1697}
1698
1699static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1700 struct cfg80211_pmksa *pmksa)
1701{
1702 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301703 struct ath6kl_vif *vif = netdev_priv(netdev);
1704
1705 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001706 pmksa->pmkid, true);
1707}
1708
1709static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1710 struct cfg80211_pmksa *pmksa)
1711{
1712 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301713 struct ath6kl_vif *vif = netdev_priv(netdev);
1714
1715 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001716 pmksa->pmkid, false);
1717}
1718
1719static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1720{
1721 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301722 struct ath6kl_vif *vif = netdev_priv(netdev);
1723
1724 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301725 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1726 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001727 return 0;
1728}
1729
Raja Mani6cb3c712011-11-07 22:52:45 +02001730static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1731{
Raja Manic08631c2011-12-16 14:24:24 +05301732 struct in_device *in_dev;
1733 struct in_ifaddr *ifa;
Raja Mani6cb3c712011-11-07 22:52:45 +02001734 struct ath6kl_vif *vif;
1735 int ret, pos, left;
1736 u32 filter = 0;
1737 u16 i;
Raja Manic08631c2011-12-16 14:24:24 +05301738 u8 mask[WOW_MASK_SIZE], index = 0;
1739 __be32 ips[MAX_IP_ADDRS];
Raja Mani6cb3c712011-11-07 22:52:45 +02001740
1741 vif = ath6kl_vif_first(ar);
1742 if (!vif)
1743 return -EIO;
1744
1745 if (!ath6kl_cfg80211_ready(vif))
1746 return -EIO;
1747
1748 if (!test_bit(CONNECTED, &vif->flags))
1749 return -EINVAL;
1750
1751 /* Clear existing WOW patterns */
1752 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
1753 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
1754 WOW_LIST_ID, i);
1755 /* Configure new WOW patterns */
1756 for (i = 0; i < wow->n_patterns; i++) {
1757
1758 /*
1759 * Convert given nl80211 specific mask value to equivalent
1760 * driver specific mask value and send it to the chip along
1761 * with patterns. For example, If the mask value defined in
1762 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1763 * then equivalent driver specific mask value is
1764 * "0xFF 0x00 0xFF 0x00".
1765 */
1766 memset(&mask, 0, sizeof(mask));
1767 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1768 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1769 mask[pos] = 0xFF;
1770 }
1771 /*
1772 * Note: Pattern's offset is not passed as part of wowlan
1773 * parameter from CFG layer. So it's always passed as ZERO
1774 * to the firmware. It means, given WOW patterns are always
1775 * matched from the first byte of received pkt in the firmware.
1776 */
1777 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1778 vif->fw_vif_idx, WOW_LIST_ID,
1779 wow->patterns[i].pattern_len,
1780 0 /* pattern offset */,
1781 wow->patterns[i].pattern, mask);
1782 if (ret)
1783 return ret;
1784 }
1785
Raja Manic08631c2011-12-16 14:24:24 +05301786 /* Setup own IP addr for ARP agent. */
1787 in_dev = __in_dev_get_rtnl(vif->ndev);
1788 if (!in_dev)
1789 goto skip_arp;
1790
1791 ifa = in_dev->ifa_list;
1792 memset(&ips, 0, sizeof(ips));
1793
1794 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
1795 while (index < MAX_IP_ADDRS && ifa) {
1796 ips[index] = ifa->ifa_local;
1797 ifa = ifa->ifa_next;
1798 index++;
1799 }
1800
1801 if (ifa) {
1802 ath6kl_err("total IP addr count is exceeding fw limit\n");
1803 return -EINVAL;
1804 }
1805
1806 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
1807 if (ret) {
1808 ath6kl_err("fail to setup ip for arp agent\n");
1809 return ret;
1810 }
1811
1812skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02001813 if (wow->disconnect)
1814 filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1815
1816 if (wow->magic_pkt)
1817 filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1818
1819 if (wow->gtk_rekey_failure)
1820 filter |= WOW_FILTER_OPTION_GTK_ERROR;
1821
1822 if (wow->eap_identity_req)
1823 filter |= WOW_FILTER_OPTION_EAP_REQ;
1824
1825 if (wow->four_way_handshake)
1826 filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1827
1828 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
1829 ATH6KL_WOW_MODE_ENABLE,
1830 filter,
1831 WOW_HOST_REQ_DELAY);
1832 if (ret)
1833 return ret;
1834
1835 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1836 ATH6KL_HOST_MODE_ASLEEP);
1837 if (ret)
1838 return ret;
1839
1840 if (ar->tx_pending[ar->ctrl_ep]) {
1841 left = wait_event_interruptible_timeout(ar->event_wq,
1842 ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
1843 if (left == 0) {
1844 ath6kl_warn("clear wmi ctrl data timeout\n");
1845 ret = -ETIMEDOUT;
1846 } else if (left < 0) {
1847 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
1848 ret = left;
1849 }
1850 }
1851
1852 return ret;
1853}
1854
1855static int ath6kl_wow_resume(struct ath6kl *ar)
1856{
1857 struct ath6kl_vif *vif;
1858 int ret;
1859
1860 vif = ath6kl_vif_first(ar);
1861 if (!vif)
1862 return -EIO;
1863
1864 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1865 ATH6KL_HOST_MODE_AWAKE);
1866 return ret;
1867}
1868
Kalle Valo52d81a62011-11-01 08:44:21 +02001869int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02001870 enum ath6kl_cfg_suspend_mode mode,
1871 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02001872{
1873 int ret;
1874
Kalle Valo52d81a62011-11-01 08:44:21 +02001875 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02001876 case ATH6KL_CFG_SUSPEND_WOW:
1877
1878 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
1879
1880 /* Flush all non control pkts in TX path */
1881 ath6kl_tx_data_cleanup(ar);
1882
1883 ret = ath6kl_wow_suspend(ar, wow);
1884 if (ret) {
1885 ath6kl_err("wow suspend failed: %d\n", ret);
1886 return ret;
1887 }
1888 ar->state = ATH6KL_STATE_WOW;
1889 break;
1890
Kalle Valo52d81a62011-11-01 08:44:21 +02001891 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02001892
Kalle Valo7125f012011-12-13 14:51:37 +02001893 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02001894
Kalle Valo52d81a62011-11-01 08:44:21 +02001895 /* save the current power mode before enabling power save */
1896 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
1897
1898 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
1899 if (ret) {
1900 ath6kl_warn("wmi powermode command failed during suspend: %d\n",
1901 ret);
1902 }
1903
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001904 ar->state = ATH6KL_STATE_DEEPSLEEP;
1905
Kalle Valo52d81a62011-11-01 08:44:21 +02001906 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001907
1908 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02001909
Kalle Valo7125f012011-12-13 14:51:37 +02001910 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02001911
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001912 if (ar->state == ATH6KL_STATE_OFF) {
1913 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
1914 "suspend hw off, no action for cutpower\n");
1915 break;
1916 }
1917
1918 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
1919
1920 ret = ath6kl_init_hw_stop(ar);
1921 if (ret) {
1922 ath6kl_warn("failed to stop hw during suspend: %d\n",
1923 ret);
1924 }
1925
1926 ar->state = ATH6KL_STATE_CUTPOWER;
1927
1928 break;
1929
Kalle Valo10509f92011-12-13 14:52:07 +02001930 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
1931 /*
1932 * Nothing needed for schedule scan, firmware is already in
1933 * wow mode and sleeping most of the time.
1934 */
1935 break;
1936
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001937 default:
1938 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02001939 }
1940
1941 return 0;
1942}
1943
1944int ath6kl_cfg80211_resume(struct ath6kl *ar)
1945{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001946 int ret;
1947
1948 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02001949 case ATH6KL_STATE_WOW:
1950 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
1951
1952 ret = ath6kl_wow_resume(ar);
1953 if (ret) {
1954 ath6kl_warn("wow mode resume failed: %d\n", ret);
1955 return ret;
1956 }
1957
1958 ar->state = ATH6KL_STATE_ON;
1959 break;
1960
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001961 case ATH6KL_STATE_DEEPSLEEP:
1962 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
1963 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
1964 ar->wmi->saved_pwr_mode);
1965 if (ret) {
1966 ath6kl_warn("wmi powermode command failed during resume: %d\n",
1967 ret);
1968 }
1969 }
1970
1971 ar->state = ATH6KL_STATE_ON;
1972
1973 break;
1974
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001975 case ATH6KL_STATE_CUTPOWER:
1976 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
1977
1978 ret = ath6kl_init_hw_start(ar);
1979 if (ret) {
1980 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
1981 return ret;
1982 }
Raja Manid7c44e02011-11-07 22:52:46 +02001983 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001984
Kalle Valo10509f92011-12-13 14:52:07 +02001985 case ATH6KL_STATE_SCHED_SCAN:
1986 break;
1987
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001988 default:
1989 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02001990 }
1991
1992 return 0;
1993}
1994
Kalle Valoabcb3442011-07-22 08:26:20 +03001995#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02001996
1997/* hif layer decides what suspend mode to use */
1998static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03001999 struct cfg80211_wowlan *wow)
2000{
2001 struct ath6kl *ar = wiphy_priv(wiphy);
2002
Raja Mani0f60e9f2011-11-07 22:52:45 +02002003 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002004}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002005
Kalle Valo52d81a62011-11-01 08:44:21 +02002006static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002007{
2008 struct ath6kl *ar = wiphy_priv(wiphy);
2009
2010 return ath6kl_hif_resume(ar);
2011}
Raja Mania918fb32011-11-07 22:52:46 +02002012
2013/*
2014 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2015 * both sdio irq wake up and keep power. The target pulls sdio data line to
2016 * wake up the host when WOW pattern matches. This causes sdio irq handler
2017 * is being called in the host side which internally hits ath6kl's RX path.
2018 *
2019 * Since sdio interrupt is not disabled, RX path executes even before
2020 * the host executes the actual resume operation from PM module.
2021 *
2022 * In the current scenario, WOW resume should happen before start processing
2023 * any data from the target. So It's required to perform WOW resume in RX path.
2024 * Ideally we should perform WOW resume only in the actual platform
2025 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2026 *
2027 * ath6kl_check_wow_status() is called from ath6kl_rx().
2028 */
2029void ath6kl_check_wow_status(struct ath6kl *ar)
2030{
2031 if (ar->state == ATH6KL_STATE_WOW)
2032 ath6kl_cfg80211_resume(ar);
2033}
2034
2035#else
2036
2037void ath6kl_check_wow_status(struct ath6kl *ar)
2038{
2039}
Kalle Valoabcb3442011-07-22 08:26:20 +03002040#endif
2041
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002042static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2043 struct ieee80211_channel *chan,
2044 enum nl80211_channel_type channel_type)
2045{
Sujith Manoharane68f6752011-12-22 12:15:27 +05302046 struct ath6kl_vif *vif;
2047
2048 /*
2049 * 'dev' could be NULL if a channel change is required for the hardware
2050 * device itself, instead of a particular VIF.
2051 *
2052 * FIXME: To be handled properly when monitor mode is supported.
2053 */
2054 if (!dev)
2055 return -EBUSY;
2056
2057 vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002058
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302059 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002060 return -EIO;
2061
2062 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2063 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302064 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002065
2066 return 0;
2067}
2068
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002069static bool ath6kl_is_p2p_ie(const u8 *pos)
2070{
2071 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2072 pos[2] == 0x50 && pos[3] == 0x6f &&
2073 pos[4] == 0x9a && pos[5] == 0x09;
2074}
2075
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302076static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2077 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002078{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302079 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002080 const u8 *pos;
2081 u8 *buf = NULL;
2082 size_t len = 0;
2083 int ret;
2084
2085 /*
2086 * Filter out P2P IE(s) since they will be included depending on
2087 * the Probe Request frame in ath6kl_send_go_probe_resp().
2088 */
2089
2090 if (ies && ies_len) {
2091 buf = kmalloc(ies_len, GFP_KERNEL);
2092 if (buf == NULL)
2093 return -ENOMEM;
2094 pos = ies;
2095 while (pos + 1 < ies + ies_len) {
2096 if (pos + 2 + pos[1] > ies + ies_len)
2097 break;
2098 if (!ath6kl_is_p2p_ie(pos)) {
2099 memcpy(buf + len, pos, 2 + pos[1]);
2100 len += 2 + pos[1];
2101 }
2102 pos += 2 + pos[1];
2103 }
2104 }
2105
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302106 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2107 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002108 kfree(buf);
2109 return ret;
2110}
2111
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002112static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
2113 struct beacon_parameters *info, bool add)
2114{
2115 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302116 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002117 struct ieee80211_mgmt *mgmt;
2118 u8 *ies;
2119 int ies_len;
2120 struct wmi_connect_cmd p;
2121 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302122 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002123
2124 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
2125
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302126 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002127 return -EIO;
2128
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302129 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002130 return -EOPNOTSUPP;
2131
2132 if (info->beacon_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302133 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2134 WMI_FRAME_BEACON,
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002135 info->beacon_ies,
2136 info->beacon_ies_len);
2137 if (res)
2138 return res;
2139 }
2140 if (info->proberesp_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302141 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002142 info->proberesp_ies_len);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002143 if (res)
2144 return res;
2145 }
2146 if (info->assocresp_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302147 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2148 WMI_FRAME_ASSOC_RESP,
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002149 info->assocresp_ies,
2150 info->assocresp_ies_len);
2151 if (res)
2152 return res;
2153 }
2154
2155 if (!add)
2156 return 0;
2157
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002158 ar->ap_mode_bkey.valid = false;
2159
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002160 /* TODO:
2161 * info->interval
2162 * info->dtim_period
2163 */
2164
2165 if (info->head == NULL)
2166 return -EINVAL;
2167 mgmt = (struct ieee80211_mgmt *) info->head;
2168 ies = mgmt->u.beacon.variable;
2169 if (ies > info->head + info->head_len)
2170 return -EINVAL;
2171 ies_len = info->head + info->head_len - ies;
2172
2173 if (info->ssid == NULL)
2174 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302175 memcpy(vif->ssid, info->ssid, info->ssid_len);
2176 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002177 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
2178 return -EOPNOTSUPP; /* TODO */
2179
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302180 ret = ath6kl_set_auth_type(vif, info->auth_type);
2181 if (ret)
2182 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002183
2184 memset(&p, 0, sizeof(p));
2185
2186 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2187 switch (info->crypto.akm_suites[i]) {
2188 case WLAN_AKM_SUITE_8021X:
2189 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2190 p.auth_mode |= WPA_AUTH;
2191 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2192 p.auth_mode |= WPA2_AUTH;
2193 break;
2194 case WLAN_AKM_SUITE_PSK:
2195 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2196 p.auth_mode |= WPA_PSK_AUTH;
2197 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2198 p.auth_mode |= WPA2_PSK_AUTH;
2199 break;
2200 }
2201 }
2202 if (p.auth_mode == 0)
2203 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302204 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002205
2206 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2207 switch (info->crypto.ciphers_pairwise[i]) {
2208 case WLAN_CIPHER_SUITE_WEP40:
2209 case WLAN_CIPHER_SUITE_WEP104:
2210 p.prwise_crypto_type |= WEP_CRYPT;
2211 break;
2212 case WLAN_CIPHER_SUITE_TKIP:
2213 p.prwise_crypto_type |= TKIP_CRYPT;
2214 break;
2215 case WLAN_CIPHER_SUITE_CCMP:
2216 p.prwise_crypto_type |= AES_CRYPT;
2217 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002218 case WLAN_CIPHER_SUITE_SMS4:
2219 p.prwise_crypto_type |= WAPI_CRYPT;
2220 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002221 }
2222 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002223 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002224 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302225 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002226 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302227 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002228
2229 switch (info->crypto.cipher_group) {
2230 case WLAN_CIPHER_SUITE_WEP40:
2231 case WLAN_CIPHER_SUITE_WEP104:
2232 p.grp_crypto_type = WEP_CRYPT;
2233 break;
2234 case WLAN_CIPHER_SUITE_TKIP:
2235 p.grp_crypto_type = TKIP_CRYPT;
2236 break;
2237 case WLAN_CIPHER_SUITE_CCMP:
2238 p.grp_crypto_type = AES_CRYPT;
2239 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002240 case WLAN_CIPHER_SUITE_SMS4:
2241 p.grp_crypto_type = WAPI_CRYPT;
2242 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002243 default:
2244 p.grp_crypto_type = NONE_CRYPT;
2245 break;
2246 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302247 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002248
2249 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302250 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002251
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302252 p.ssid_len = vif->ssid_len;
2253 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2254 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302255 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002256
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002257 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2258 p.nw_subtype = SUBTYPE_P2PGO;
2259 } else {
2260 /*
2261 * Due to firmware limitation, it is not possible to
2262 * do P2P mgmt operations in AP mode
2263 */
2264 p.nw_subtype = SUBTYPE_NONE;
2265 }
2266
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302267 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002268 if (res < 0)
2269 return res;
2270
2271 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002272}
2273
2274static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
2275 struct beacon_parameters *info)
2276{
2277 return ath6kl_ap_beacon(wiphy, dev, info, true);
2278}
2279
2280static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
2281 struct beacon_parameters *info)
2282{
2283 return ath6kl_ap_beacon(wiphy, dev, info, false);
2284}
2285
2286static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
2287{
2288 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302289 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002290
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302291 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002292 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302293 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002294 return -ENOTCONN;
2295
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302296 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302297 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002298
2299 return 0;
2300}
2301
Jouni Malinen33e53082011-12-27 11:02:56 +02002302static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2303
2304static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2305 u8 *mac)
2306{
2307 struct ath6kl *ar = ath6kl_priv(dev);
2308 struct ath6kl_vif *vif = netdev_priv(dev);
2309 const u8 *addr = mac ? mac : bcast_addr;
2310
2311 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2312 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2313}
2314
Jouni Malinen23875132011-08-30 21:57:53 +03002315static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2316 u8 *mac, struct station_parameters *params)
2317{
2318 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302319 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002320
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302321 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002322 return -EOPNOTSUPP;
2323
2324 /* Use this only for authorizing/unauthorizing a station */
2325 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2326 return -EOPNOTSUPP;
2327
2328 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302329 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2330 WMI_AP_MLME_AUTHORIZE, mac, 0);
2331 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2332 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002333}
2334
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002335static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2336 struct net_device *dev,
2337 struct ieee80211_channel *chan,
2338 enum nl80211_channel_type channel_type,
2339 unsigned int duration,
2340 u64 *cookie)
2341{
2342 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302343 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002344 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002345
2346 /* TODO: if already pending or ongoing remain-on-channel,
2347 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002348 id = ++vif->last_roc_id;
2349 if (id == 0) {
2350 /* Do not use 0 as the cookie value */
2351 id = ++vif->last_roc_id;
2352 }
2353 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002354
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302355 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2356 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002357}
2358
2359static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2360 struct net_device *dev,
2361 u64 cookie)
2362{
2363 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302364 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002365
Jouni Malinen10522612011-10-27 16:00:13 +03002366 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002367 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002368 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002369
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302370 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002371}
2372
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302373static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2374 const u8 *buf, size_t len,
2375 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002376{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302377 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002378 const u8 *pos;
2379 u8 *p2p;
2380 int p2p_len;
2381 int ret;
2382 const struct ieee80211_mgmt *mgmt;
2383
2384 mgmt = (const struct ieee80211_mgmt *) buf;
2385
2386 /* Include P2P IE(s) from the frame generated in user space. */
2387
2388 p2p = kmalloc(len, GFP_KERNEL);
2389 if (p2p == NULL)
2390 return -ENOMEM;
2391 p2p_len = 0;
2392
2393 pos = mgmt->u.probe_resp.variable;
2394 while (pos + 1 < buf + len) {
2395 if (pos + 2 + pos[1] > buf + len)
2396 break;
2397 if (ath6kl_is_p2p_ie(pos)) {
2398 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2399 p2p_len += 2 + pos[1];
2400 }
2401 pos += 2 + pos[1];
2402 }
2403
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302404 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2405 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002406 kfree(p2p);
2407 return ret;
2408}
2409
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002410static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2411 struct ieee80211_channel *chan, bool offchan,
2412 enum nl80211_channel_type channel_type,
2413 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002414 const u8 *buf, size_t len, bool no_cck,
2415 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002416{
2417 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302418 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002419 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002420 const struct ieee80211_mgmt *mgmt;
2421
2422 mgmt = (const struct ieee80211_mgmt *) buf;
2423 if (buf + len >= mgmt->u.probe_resp.variable &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302424 vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002425 ieee80211_is_probe_resp(mgmt->frame_control)) {
2426 /*
2427 * Send Probe Response frame in AP mode using a separate WMI
2428 * command to allow the target to fill in the generic IEs.
2429 */
2430 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302431 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002432 chan->center_freq);
2433 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002434
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302435 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002436 if (id == 0) {
2437 /*
2438 * 0 is a reserved value in the WMI command and shall not be
2439 * used for the command.
2440 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302441 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002442 }
2443
2444 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002445
2446 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
2447 ar->fw_capabilities)) {
2448 /*
2449 * If capable of doing P2P mgmt operations using
2450 * station interface, send additional information like
2451 * supported rates to advertise and xmit rates for
2452 * probe requests
2453 */
2454 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2455 chan->center_freq, wait,
2456 buf, len, no_cck);
2457 } else {
2458 return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
2459 chan->center_freq, wait,
2460 buf, len);
2461 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002462}
2463
Jouni Malinenae32c302011-08-30 21:58:01 +03002464static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2465 struct net_device *dev,
2466 u16 frame_type, bool reg)
2467{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302468 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002469
2470 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2471 __func__, frame_type, reg);
2472 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2473 /*
2474 * Note: This notification callback is not allowed to sleep, so
2475 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2476 * hardcode target to report Probe Request frames all the time.
2477 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302478 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002479 }
2480}
2481
Kalle Valo10509f92011-12-13 14:52:07 +02002482static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2483 struct net_device *dev,
2484 struct cfg80211_sched_scan_request *request)
2485{
2486 struct ath6kl *ar = ath6kl_priv(dev);
2487 struct ath6kl_vif *vif = netdev_priv(dev);
2488 u16 interval;
2489 int ret;
2490 u8 i;
2491
2492 if (ar->state != ATH6KL_STATE_ON)
2493 return -EIO;
2494
2495 if (vif->sme_state != SME_DISCONNECTED)
2496 return -EBUSY;
2497
2498 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2499 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2500 i, DISABLE_SSID_FLAG,
2501 0, NULL);
2502 }
2503
2504 /* fw uses seconds, also make sure that it's >0 */
2505 interval = max_t(u16, 1, request->interval / 1000);
2506
2507 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2508 interval, interval,
2509 10, 0, 0, 0, 3, 0, 0, 0);
2510
2511 if (request->n_ssids && request->ssids[0].ssid_len) {
2512 for (i = 0; i < request->n_ssids; i++) {
2513 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2514 i, SPECIFIC_SSID_FLAG,
2515 request->ssids[i].ssid_len,
2516 request->ssids[i].ssid);
2517 }
2518 }
2519
2520 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2521 ATH6KL_WOW_MODE_ENABLE,
2522 WOW_FILTER_SSID,
2523 WOW_HOST_REQ_DELAY);
2524 if (ret) {
2525 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2526 return ret;
2527 }
2528
2529 /* this also clears IE in fw if it's not set */
2530 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2531 WMI_FRAME_PROBE_REQ,
2532 request->ie, request->ie_len);
2533 if (ret) {
2534 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
2535 ret);
2536 return ret;
2537 }
2538
2539 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2540 ATH6KL_HOST_MODE_ASLEEP);
2541 if (ret) {
2542 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
2543 ret);
2544 return ret;
2545 }
2546
2547 ar->state = ATH6KL_STATE_SCHED_SCAN;
2548
2549 return ret;
2550}
2551
2552static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
2553 struct net_device *dev)
2554{
2555 struct ath6kl_vif *vif = netdev_priv(dev);
2556 bool stopped;
2557
2558 stopped = __ath6kl_cfg80211_sscan_stop(vif);
2559
2560 if (!stopped)
2561 return -EIO;
2562
2563 return 0;
2564}
2565
Jouni Malinenf80574a2011-08-30 21:58:04 +03002566static const struct ieee80211_txrx_stypes
2567ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2568 [NL80211_IFTYPE_STATION] = {
2569 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2570 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2571 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2572 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2573 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02002574 [NL80211_IFTYPE_AP] = {
2575 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2576 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2577 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2578 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2579 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03002580 [NL80211_IFTYPE_P2P_CLIENT] = {
2581 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2582 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2583 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2584 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2585 },
2586 [NL80211_IFTYPE_P2P_GO] = {
2587 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2588 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2589 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2590 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2591 },
2592};
2593
Kalle Valobdcd8172011-07-18 00:22:30 +03002594static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302595 .add_virtual_intf = ath6kl_cfg80211_add_iface,
2596 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03002597 .change_virtual_intf = ath6kl_cfg80211_change_iface,
2598 .scan = ath6kl_cfg80211_scan,
2599 .connect = ath6kl_cfg80211_connect,
2600 .disconnect = ath6kl_cfg80211_disconnect,
2601 .add_key = ath6kl_cfg80211_add_key,
2602 .get_key = ath6kl_cfg80211_get_key,
2603 .del_key = ath6kl_cfg80211_del_key,
2604 .set_default_key = ath6kl_cfg80211_set_default_key,
2605 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
2606 .set_tx_power = ath6kl_cfg80211_set_txpower,
2607 .get_tx_power = ath6kl_cfg80211_get_txpower,
2608 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
2609 .join_ibss = ath6kl_cfg80211_join_ibss,
2610 .leave_ibss = ath6kl_cfg80211_leave_ibss,
2611 .get_station = ath6kl_get_station,
2612 .set_pmksa = ath6kl_set_pmksa,
2613 .del_pmksa = ath6kl_del_pmksa,
2614 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03002615 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03002616#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002617 .suspend = __ath6kl_cfg80211_suspend,
2618 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03002619#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002620 .set_channel = ath6kl_set_channel,
2621 .add_beacon = ath6kl_add_beacon,
2622 .set_beacon = ath6kl_set_beacon,
2623 .del_beacon = ath6kl_del_beacon,
Jouni Malinen33e53082011-12-27 11:02:56 +02002624 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03002625 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002626 .remain_on_channel = ath6kl_remain_on_channel,
2627 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002628 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03002629 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02002630 .sched_scan_start = ath6kl_cfg80211_sscan_start,
2631 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03002632};
2633
Kalle Valo7125f012011-12-13 14:51:37 +02002634void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02002635{
Kalle Valo10509f92011-12-13 14:52:07 +02002636 ath6kl_cfg80211_sscan_disable(vif);
2637
Kalle Valoec4b7f62011-11-01 08:44:04 +02002638 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02002639 case SME_DISCONNECTED:
2640 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02002641 case SME_CONNECTING:
2642 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
2643 NULL, 0,
2644 WLAN_STATUS_UNSPECIFIED_FAILURE,
2645 GFP_KERNEL);
2646 break;
2647 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02002648 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
2649 break;
2650 }
2651
2652 if (test_bit(CONNECTED, &vif->flags) ||
2653 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02002654 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002655
2656 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02002657 clear_bit(CONNECTED, &vif->flags);
2658 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002659
2660 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02002661 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
2662 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
2663 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02002664
2665 ath6kl_cfg80211_scan_complete_event(vif, true);
2666}
2667
Kalle Valo7125f012011-12-13 14:51:37 +02002668void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
2669{
2670 struct ath6kl_vif *vif;
2671
2672 vif = ath6kl_vif_first(ar);
2673 if (!vif) {
2674 /* save the current power mode before enabling power save */
2675 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2676
2677 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
2678 ath6kl_warn("ath6kl_deep_sleep_enable: "
2679 "wmi_powermode_cmd failed\n");
2680 return;
2681 }
2682
2683 /*
2684 * FIXME: we should take ar->list_lock to protect changes in the
2685 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
2686 * sleeps.
2687 */
2688 list_for_each_entry(vif, &ar->vif_list, list)
2689 ath6kl_cfg80211_stop(vif);
2690}
2691
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302692struct ath6kl *ath6kl_core_alloc(struct device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +03002693{
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002694 struct ath6kl *ar;
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302695 struct wiphy *wiphy;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302696 u8 ctr;
Kalle Valobdcd8172011-07-18 00:22:30 +03002697
2698 /* create a new wiphy for use with cfg80211 */
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302699 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302700
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302701 if (!wiphy) {
Kalle Valobdcd8172011-07-18 00:22:30 +03002702 ath6kl_err("couldn't allocate wiphy device\n");
Kalle Valobdcd8172011-07-18 00:22:30 +03002703 return NULL;
2704 }
2705
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302706 ar = wiphy_priv(wiphy);
Vasanthakumar Thiagarajan774439a2011-11-18 10:05:26 +05302707 ar->p2p = !!ath6kl_p2p;
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302708 ar->wiphy = wiphy;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302709 ar->dev = dev;
2710
Kalle Valo71f96ee2011-11-14 19:31:30 +02002711 ar->vif_max = 1;
2712
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +05302713 ar->max_norm_iface = 1;
2714
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302715 spin_lock_init(&ar->lock);
2716 spin_lock_init(&ar->mcastpsq_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302717 spin_lock_init(&ar->list_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302718
2719 init_waitqueue_head(&ar->event_wq);
2720 sema_init(&ar->sem, 1);
2721
2722 INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302723 INIT_LIST_HEAD(&ar->vif_list);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302724
2725 clear_bit(WMI_ENABLED, &ar->flag);
2726 clear_bit(SKIP_SCAN, &ar->flag);
2727 clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
2728
2729 ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL;
2730 ar->listen_intvl_b = 0;
2731 ar->tx_pwr = 0;
2732
2733 ar->intra_bss = 1;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302734 ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
2735
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002736 ar->state = ATH6KL_STATE_OFF;
2737
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302738 memset((u8 *)ar->sta_list, 0,
2739 AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
2740
2741 /* Init the PS queues */
2742 for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
2743 spin_lock_init(&ar->sta_list[ctr].psq_lock);
2744 skb_queue_head_init(&ar->sta_list[ctr].psq);
2745 }
2746
2747 skb_queue_head_init(&ar->mcastpsq);
2748
2749 memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3);
2750
2751 return ar;
2752}
2753
2754int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
2755{
2756 struct wiphy *wiphy = ar->wiphy;
2757 int ret;
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002758
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302759 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
Jouni Malinenf80574a2011-08-30 21:58:04 +03002760
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302761 wiphy->max_remain_on_channel_duration = 5000;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002762
Kalle Valobdcd8172011-07-18 00:22:30 +03002763 /* set device pointer for wiphy */
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302764 set_wiphy_dev(wiphy, ar->dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03002765
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302766 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302767 BIT(NL80211_IFTYPE_ADHOC) |
2768 BIT(NL80211_IFTYPE_AP);
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002769 if (ar->p2p) {
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302770 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302771 BIT(NL80211_IFTYPE_P2P_CLIENT);
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002772 }
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302773
Kalle Valobdcd8172011-07-18 00:22:30 +03002774 /* max num of ssids that can be probed during scanning */
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302775 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
2776 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
2777 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
2778 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
2779 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
Kalle Valobdcd8172011-07-18 00:22:30 +03002780
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302781 wiphy->cipher_suites = cipher_suites;
2782 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
Kalle Valobdcd8172011-07-18 00:22:30 +03002783
Raja Manieae9e062011-11-07 22:52:46 +02002784 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
2785 WIPHY_WOWLAN_DISCONNECT |
2786 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
2787 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
2788 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
2789 WIPHY_WOWLAN_4WAY_HANDSHAKE;
2790 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
2791 wiphy->wowlan.pattern_min_len = 1;
2792 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
2793
Kalle Valo10509f92011-12-13 14:52:07 +02002794 wiphy->max_sched_scan_ssids = 10;
2795
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302796 ret = wiphy_register(wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03002797 if (ret < 0) {
2798 ath6kl_err("couldn't register wiphy device\n");
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302799 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +03002800 }
2801
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302802 return 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03002803}
2804
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302805static int ath6kl_init_if_data(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03002806{
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302807 vif->aggr_cntxt = aggr_init(vif->ndev);
2808 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302809 ath6kl_err("failed to initialize aggr\n");
2810 return -ENOMEM;
2811 }
Kalle Valobdcd8172011-07-18 00:22:30 +03002812
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302813 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302814 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02002815 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
2816 (unsigned long) vif);
2817
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302818 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05302819 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302820
2821 return 0;
2822}
2823
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302824void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302825{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302826 struct ath6kl *ar = vif->ar;
2827
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302828 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302829
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302830 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
2831
2832 if (vif->nw_type == ADHOC_NETWORK)
2833 ar->ibss_if_active = false;
2834
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302835 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302836
2837 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302838}
2839
2840struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302841 enum nl80211_iftype type, u8 fw_vif_idx,
2842 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302843{
2844 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302845 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302846
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302847 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302848 if (!ndev)
2849 return NULL;
2850
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302851 vif = netdev_priv(ndev);
2852 ndev->ieee80211_ptr = &vif->wdev;
2853 vif->wdev.wiphy = ar->wiphy;
2854 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302855 vif->ndev = ndev;
2856 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
2857 vif->wdev.netdev = ndev;
2858 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302859 vif->fw_vif_idx = fw_vif_idx;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302860 vif->nw_type = vif->next_mode = nw_type;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302861
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302862 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
2863 if (fw_vif_idx != 0)
2864 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
2865 0x2;
2866
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302867 init_netdev(ndev);
2868
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05302869 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302870
2871 /* TODO: Pass interface specific pointer instead of ar */
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302872 if (ath6kl_init_if_data(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302873 goto err;
2874
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302875 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302876 goto err;
2877
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302878 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05302879 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302880 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302881 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302882 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302883
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302884 if (type == NL80211_IFTYPE_ADHOC)
2885 ar->ibss_if_active = true;
2886
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05302887 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302888 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05302889 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302890
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302891 return ndev;
2892
2893err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302894 aggr_module_destroy(vif->aggr_cntxt);
2895 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302896 return NULL;
2897}
2898
2899void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar)
2900{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302901 wiphy_unregister(ar->wiphy);
2902 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03002903}