blob: 6c59a217b1a133f390d1dd2f444494110660b9f7 [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>
18
Kalle Valobdcd8172011-07-18 00:22:30 +030019#include "core.h"
20#include "cfg80211.h"
21#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030022#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030023#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030024
Jouni Malinen6bbc7c32011-09-05 17:38:47 +030025static unsigned int ath6kl_p2p;
26
27module_param(ath6kl_p2p, uint, 0644);
28
Kalle Valobdcd8172011-07-18 00:22:30 +030029#define RATETAB_ENT(_rate, _rateid, _flags) { \
30 .bitrate = (_rate), \
31 .flags = (_flags), \
32 .hw_value = (_rateid), \
33}
34
35#define CHAN2G(_channel, _freq, _flags) { \
36 .band = IEEE80211_BAND_2GHZ, \
37 .hw_value = (_channel), \
38 .center_freq = (_freq), \
39 .flags = (_flags), \
40 .max_antenna_gain = 0, \
41 .max_power = 30, \
42}
43
44#define CHAN5G(_channel, _flags) { \
45 .band = IEEE80211_BAND_5GHZ, \
46 .hw_value = (_channel), \
47 .center_freq = 5000 + (5 * (_channel)), \
48 .flags = (_flags), \
49 .max_antenna_gain = 0, \
50 .max_power = 30, \
51}
52
53static struct ieee80211_rate ath6kl_rates[] = {
54 RATETAB_ENT(10, 0x1, 0),
55 RATETAB_ENT(20, 0x2, 0),
56 RATETAB_ENT(55, 0x4, 0),
57 RATETAB_ENT(110, 0x8, 0),
58 RATETAB_ENT(60, 0x10, 0),
59 RATETAB_ENT(90, 0x20, 0),
60 RATETAB_ENT(120, 0x40, 0),
61 RATETAB_ENT(180, 0x80, 0),
62 RATETAB_ENT(240, 0x100, 0),
63 RATETAB_ENT(360, 0x200, 0),
64 RATETAB_ENT(480, 0x400, 0),
65 RATETAB_ENT(540, 0x800, 0),
66};
67
68#define ath6kl_a_rates (ath6kl_rates + 4)
69#define ath6kl_a_rates_size 8
70#define ath6kl_g_rates (ath6kl_rates + 0)
71#define ath6kl_g_rates_size 12
72
73static struct ieee80211_channel ath6kl_2ghz_channels[] = {
74 CHAN2G(1, 2412, 0),
75 CHAN2G(2, 2417, 0),
76 CHAN2G(3, 2422, 0),
77 CHAN2G(4, 2427, 0),
78 CHAN2G(5, 2432, 0),
79 CHAN2G(6, 2437, 0),
80 CHAN2G(7, 2442, 0),
81 CHAN2G(8, 2447, 0),
82 CHAN2G(9, 2452, 0),
83 CHAN2G(10, 2457, 0),
84 CHAN2G(11, 2462, 0),
85 CHAN2G(12, 2467, 0),
86 CHAN2G(13, 2472, 0),
87 CHAN2G(14, 2484, 0),
88};
89
90static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
91 CHAN5G(34, 0), CHAN5G(36, 0),
92 CHAN5G(38, 0), CHAN5G(40, 0),
93 CHAN5G(42, 0), CHAN5G(44, 0),
94 CHAN5G(46, 0), CHAN5G(48, 0),
95 CHAN5G(52, 0), CHAN5G(56, 0),
96 CHAN5G(60, 0), CHAN5G(64, 0),
97 CHAN5G(100, 0), CHAN5G(104, 0),
98 CHAN5G(108, 0), CHAN5G(112, 0),
99 CHAN5G(116, 0), CHAN5G(120, 0),
100 CHAN5G(124, 0), CHAN5G(128, 0),
101 CHAN5G(132, 0), CHAN5G(136, 0),
102 CHAN5G(140, 0), CHAN5G(149, 0),
103 CHAN5G(153, 0), CHAN5G(157, 0),
104 CHAN5G(161, 0), CHAN5G(165, 0),
105 CHAN5G(184, 0), CHAN5G(188, 0),
106 CHAN5G(192, 0), CHAN5G(196, 0),
107 CHAN5G(200, 0), CHAN5G(204, 0),
108 CHAN5G(208, 0), CHAN5G(212, 0),
109 CHAN5G(216, 0),
110};
111
112static struct ieee80211_supported_band ath6kl_band_2ghz = {
113 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
114 .channels = ath6kl_2ghz_channels,
115 .n_bitrates = ath6kl_g_rates_size,
116 .bitrates = ath6kl_g_rates,
117};
118
119static struct ieee80211_supported_band ath6kl_band_5ghz = {
120 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
121 .channels = ath6kl_5ghz_a_channels,
122 .n_bitrates = ath6kl_a_rates_size,
123 .bitrates = ath6kl_a_rates,
124};
125
Jouni Malinen837cb972011-10-11 17:31:57 +0300126#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
127
Kalle Valo10509f92011-12-13 14:52:07 +0200128/* returns true if scheduled scan was stopped */
129static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
130{
131 struct ath6kl *ar = vif->ar;
132
133 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
134 return false;
135
136 del_timer_sync(&vif->sched_scan_timer);
137
138 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
139 ATH6KL_HOST_MODE_AWAKE);
140
141 ar->state = ATH6KL_STATE_ON;
142
143 return true;
144}
145
146static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
147{
148 struct ath6kl *ar = vif->ar;
149 bool stopped;
150
151 stopped = __ath6kl_cfg80211_sscan_stop(vif);
152
153 if (!stopped)
154 return;
155
156 cfg80211_sched_scan_stopped(ar->wiphy);
157}
158
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530159static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300160 enum nl80211_wpa_versions wpa_version)
161{
162 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
163
164 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530165 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300166 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530167 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300168 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530169 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300170 } else {
171 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
172 return -ENOTSUPP;
173 }
174
175 return 0;
176}
177
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530178static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300179 enum nl80211_auth_type auth_type)
180{
Kalle Valobdcd8172011-07-18 00:22:30 +0300181 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
182
183 switch (auth_type) {
184 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530185 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300186 break;
187 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530188 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300189 break;
190 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530191 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300192 break;
193
194 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530195 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300196 break;
197
198 default:
199 ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type);
200 return -ENOTSUPP;
201 }
202
203 return 0;
204}
205
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530206static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300207{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530208 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
209 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
210 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300211
212 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
213 __func__, cipher, ucast);
214
215 switch (cipher) {
216 case 0:
217 /* our own hack to use value 0 as no crypto used */
218 *ar_cipher = NONE_CRYPT;
219 *ar_cipher_len = 0;
220 break;
221 case WLAN_CIPHER_SUITE_WEP40:
222 *ar_cipher = WEP_CRYPT;
223 *ar_cipher_len = 5;
224 break;
225 case WLAN_CIPHER_SUITE_WEP104:
226 *ar_cipher = WEP_CRYPT;
227 *ar_cipher_len = 13;
228 break;
229 case WLAN_CIPHER_SUITE_TKIP:
230 *ar_cipher = TKIP_CRYPT;
231 *ar_cipher_len = 0;
232 break;
233 case WLAN_CIPHER_SUITE_CCMP:
234 *ar_cipher = AES_CRYPT;
235 *ar_cipher_len = 0;
236 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200237 case WLAN_CIPHER_SUITE_SMS4:
238 *ar_cipher = WAPI_CRYPT;
239 *ar_cipher_len = 0;
240 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300241 default:
242 ath6kl_err("cipher 0x%x not supported\n", cipher);
243 return -ENOTSUPP;
244 }
245
246 return 0;
247}
248
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530249static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300250{
251 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
252
253 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530254 if (vif->auth_mode == WPA_AUTH)
255 vif->auth_mode = WPA_PSK_AUTH;
256 else if (vif->auth_mode == WPA2_AUTH)
257 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300258 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530259 if (vif->auth_mode == WPA_AUTH)
260 vif->auth_mode = WPA_AUTH_CCKM;
261 else if (vif->auth_mode == WPA2_AUTH)
262 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300263 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530264 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300265 }
266}
267
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530268static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300269{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530270 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530271
Kalle Valobdcd8172011-07-18 00:22:30 +0300272 if (!test_bit(WMI_READY, &ar->flag)) {
273 ath6kl_err("wmi is not ready\n");
274 return false;
275 }
276
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530277 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300278 ath6kl_err("wlan disabled\n");
279 return false;
280 }
281
282 return true;
283}
284
Kevin Fang6981ffd2011-10-07 08:51:19 +0800285static bool ath6kl_is_wpa_ie(const u8 *pos)
286{
287 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
288 pos[2] == 0x00 && pos[3] == 0x50 &&
289 pos[4] == 0xf2 && pos[5] == 0x01;
290}
291
292static bool ath6kl_is_rsn_ie(const u8 *pos)
293{
294 return pos[0] == WLAN_EID_RSN;
295}
296
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700297static bool ath6kl_is_wps_ie(const u8 *pos)
298{
299 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
300 pos[1] >= 4 &&
301 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
302 pos[5] == 0x04);
303}
304
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530305static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
306 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800307{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530308 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800309 const u8 *pos;
310 u8 *buf = NULL;
311 size_t len = 0;
312 int ret;
313
314 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700315 * Clear previously set flag
316 */
317
318 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
319
320 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800321 * Filter out RSN/WPA IE(s)
322 */
323
324 if (ies && ies_len) {
325 buf = kmalloc(ies_len, GFP_KERNEL);
326 if (buf == NULL)
327 return -ENOMEM;
328 pos = ies;
329
330 while (pos + 1 < ies + ies_len) {
331 if (pos + 2 + pos[1] > ies + ies_len)
332 break;
333 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
334 memcpy(buf + len, pos, 2 + pos[1]);
335 len += 2 + pos[1];
336 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700337
338 if (ath6kl_is_wps_ie(pos))
339 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
340
Kevin Fang6981ffd2011-10-07 08:51:19 +0800341 pos += 2 + pos[1];
342 }
343 }
344
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530345 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
346 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800347 kfree(buf);
348 return ret;
349}
350
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530351static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
352{
353 switch (type) {
354 case NL80211_IFTYPE_STATION:
355 *nw_type = INFRA_NETWORK;
356 break;
357 case NL80211_IFTYPE_ADHOC:
358 *nw_type = ADHOC_NETWORK;
359 break;
360 case NL80211_IFTYPE_AP:
361 *nw_type = AP_NETWORK;
362 break;
363 case NL80211_IFTYPE_P2P_CLIENT:
364 *nw_type = INFRA_NETWORK;
365 break;
366 case NL80211_IFTYPE_P2P_GO:
367 *nw_type = AP_NETWORK;
368 break;
369 default:
370 ath6kl_err("invalid interface type %u\n", type);
371 return -ENOTSUPP;
372 }
373
374 return 0;
375}
376
377static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
378 u8 *if_idx, u8 *nw_type)
379{
380 int i;
381
382 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
383 return false;
384
385 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
386 ar->num_vif))
387 return false;
388
389 if (type == NL80211_IFTYPE_STATION ||
390 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200391 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530392 if ((ar->avail_idx_map >> i) & BIT(0)) {
393 *if_idx = i;
394 return true;
395 }
396 }
397 }
398
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530399 if (type == NL80211_IFTYPE_P2P_CLIENT ||
400 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200401 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530402 if ((ar->avail_idx_map >> i) & BIT(0)) {
403 *if_idx = i;
404 return true;
405 }
406 }
407 }
408
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530409 return false;
410}
411
Kalle Valobdcd8172011-07-18 00:22:30 +0300412static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
413 struct cfg80211_connect_params *sme)
414{
415 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530416 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300417 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800418 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Kalle Valobdcd8172011-07-18 00:22:30 +0300419
Kalle Valo10509f92011-12-13 14:52:07 +0200420 ath6kl_cfg80211_sscan_disable(vif);
421
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530422 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300423
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530424 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300425 return -EIO;
426
427 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
428 ath6kl_err("destroy in progress\n");
429 return -EBUSY;
430 }
431
432 if (test_bit(SKIP_SCAN, &ar->flag) &&
433 ((sme->channel && sme->channel->center_freq == 0) ||
434 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
435 ath6kl_err("SkipScan: channel or bssid invalid\n");
436 return -EINVAL;
437 }
438
439 if (down_interruptible(&ar->sem)) {
440 ath6kl_err("busy, couldn't get access\n");
441 return -ERESTARTSYS;
442 }
443
444 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
445 ath6kl_err("busy, destroy in progress\n");
446 up(&ar->sem);
447 return -EBUSY;
448 }
449
450 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
451 /*
452 * sleep until the command queue drains
453 */
454 wait_event_interruptible_timeout(ar->event_wq,
455 ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
456 WMI_TIMEOUT);
457 if (signal_pending(current)) {
458 ath6kl_err("cmd queue drain timeout\n");
459 up(&ar->sem);
460 return -EINTR;
461 }
462 }
463
Kevin Fang6981ffd2011-10-07 08:51:19 +0800464 if (sme->ie && (sme->ie_len > 0)) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530465 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
Dan Carpenter743b4512011-11-18 17:09:32 +0300466 if (status) {
467 up(&ar->sem);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800468 return status;
Dan Carpenter743b4512011-11-18 17:09:32 +0300469 }
Raja Mani542c5192011-11-15 14:14:56 +0530470 } else
471 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800472
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530473 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530474 vif->ssid_len == sme->ssid_len &&
475 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530476 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530477 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
478 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530479 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300480
481 up(&ar->sem);
482 if (status) {
483 ath6kl_err("wmi_reconnect_cmd failed\n");
484 return -EIO;
485 }
486 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530487 } else if (vif->ssid_len == sme->ssid_len &&
488 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530489 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300490 }
491
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530492 memset(vif->ssid, 0, sizeof(vif->ssid));
493 vif->ssid_len = sme->ssid_len;
494 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300495
496 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530497 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300498
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530499 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300500 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530501 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300502
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530503 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300504
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530505 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300506 if (status) {
507 up(&ar->sem);
508 return status;
509 }
510
511 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530512 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300513 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530514 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300515
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530516 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300517
518 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530519 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300520
521 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530522 (vif->auth_mode == NONE_AUTH) &&
523 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300524 struct ath6kl_key *key = NULL;
525
526 if (sme->key_idx < WMI_MIN_KEY_INDEX ||
527 sme->key_idx > WMI_MAX_KEY_INDEX) {
528 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
Raja Mani4eab6f42011-11-09 17:02:23 +0530608static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
609 enum network_type nw_type,
610 const u8 *bssid,
Jouni Malinen01cac472011-09-19 19:14:59 +0300611 struct ieee80211_channel *chan,
612 const u8 *beacon_ie, size_t beacon_ie_len)
613{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530614 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300615 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530616 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300617 u8 *ie;
618
Raja Mani4eab6f42011-11-09 17:02:23 +0530619 if (nw_type & ADHOC_NETWORK) {
620 cap_mask = WLAN_CAPABILITY_IBSS;
621 cap_val = WLAN_CAPABILITY_IBSS;
622 } else {
623 cap_mask = WLAN_CAPABILITY_ESS;
624 cap_val = WLAN_CAPABILITY_ESS;
625 }
626
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530627 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530628 vif->ssid, vif->ssid_len,
629 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300630 if (bss == NULL) {
631 /*
632 * Since cfg80211 may not yet know about the BSS,
633 * generate a partial entry until the first BSS info
634 * event becomes available.
635 *
636 * Prepend SSID element since it is not included in the Beacon
637 * IEs from the target.
638 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530639 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300640 if (ie == NULL)
641 return -ENOMEM;
642 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530643 ie[1] = vif->ssid_len;
644 memcpy(ie + 2, vif->ssid, vif->ssid_len);
645 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530646 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530647 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530648 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300649 0, GFP_KERNEL);
650 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530651 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
652 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300653 kfree(ie);
654 } else
655 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
656 "entry\n");
657
658 if (bss == NULL)
659 return -ENOMEM;
660
661 cfg80211_put_bss(bss);
662
663 return 0;
664}
665
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530666void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300667 u8 *bssid, u16 listen_intvl,
668 u16 beacon_intvl,
669 enum network_type nw_type,
670 u8 beacon_ie_len, u8 assoc_req_len,
671 u8 assoc_resp_len, u8 *assoc_info)
672{
Jouni Malinen01cac472011-09-19 19:14:59 +0300673 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530674 struct ath6kl *ar = vif->ar;
Kalle Valobdcd8172011-07-18 00:22:30 +0300675
676 /* capinfo + listen interval */
677 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
678
679 /* capinfo + status code + associd */
680 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
681
682 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
683 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
684 assoc_resp_ie_offset;
685
686 assoc_req_len -= assoc_req_ie_offset;
687 assoc_resp_len -= assoc_resp_ie_offset;
688
Jouni Malinen32c10872011-09-19 19:15:07 +0300689 /*
690 * Store Beacon interval here; DTIM period will be available only once
691 * a Beacon frame from the AP is seen.
692 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530693 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530694 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300695
Kalle Valobdcd8172011-07-18 00:22:30 +0300696 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530697 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300698 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
699 "%s: ath6k not in ibss mode\n", __func__);
700 return;
701 }
702 }
703
704 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530705 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
706 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300707 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
708 "%s: ath6k not in station mode\n", __func__);
709 return;
710 }
711 }
712
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530713 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300714
Raja Mani4eab6f42011-11-09 17:02:23 +0530715 if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info,
716 beacon_ie_len) < 0) {
717 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300718 return;
719 }
720
Raja Mani4eab6f42011-11-09 17:02:23 +0530721 if (nw_type & ADHOC_NETWORK) {
722 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
723 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
724 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300725 return;
726 }
727
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530728 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300729 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530730 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530731 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300732 assoc_req_ie, assoc_req_len,
733 assoc_resp_ie, assoc_resp_len,
734 WLAN_STATUS_SUCCESS, GFP_KERNEL);
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 Thiagarajan240d2792011-10-25 19:34:13 +0530737 cfg80211_roamed(vif->ndev, chan, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300738 assoc_req_ie, assoc_req_len,
739 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
740 }
741}
742
743static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
744 struct net_device *dev, u16 reason_code)
745{
Kalle Valod6d5c062011-11-25 13:17:37 +0200746 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530747 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300748
749 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
750 reason_code);
751
Kalle Valo10509f92011-12-13 14:52:07 +0200752 ath6kl_cfg80211_sscan_disable(vif);
753
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530754 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300755 return -EIO;
756
757 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
758 ath6kl_err("busy, destroy in progress\n");
759 return -EBUSY;
760 }
761
762 if (down_interruptible(&ar->sem)) {
763 ath6kl_err("busy, couldn't get access\n");
764 return -ERESTARTSYS;
765 }
766
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530767 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530768 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530769 memset(vif->ssid, 0, sizeof(vif->ssid));
770 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300771
772 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530773 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300774
775 up(&ar->sem);
776
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530777 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530778
Kalle Valobdcd8172011-07-18 00:22:30 +0300779 return 0;
780}
781
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530782void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300783 u8 *bssid, u8 assoc_resp_len,
784 u8 *assoc_info, u16 proto_reason)
785{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530786 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530787
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530788 if (vif->scan_req) {
789 cfg80211_scan_done(vif->scan_req, true);
790 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300791 }
792
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530793 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530794 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300795 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
796 "%s: ath6k not in ibss mode\n", __func__);
797 return;
798 }
799 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530800 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300801 return;
802 }
803
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530804 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530805 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
806 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300807 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
808 "%s: ath6k not in station mode\n", __func__);
809 return;
810 }
811 }
812
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530813 /*
814 * Send a disconnect command to target when a disconnect event is
815 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
816 * request from host) to make the firmware stop trying to connect even
817 * after giving disconnect event. There will be one more disconnect
818 * event for this disconnect command with reason code DISCONNECT_CMD
819 * which will be notified to cfg80211.
820 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300821
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530822 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530823 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300824 return;
825 }
826
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530827 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300828
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530829 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530830 cfg80211_connect_result(vif->ndev,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530831 bssid, NULL, 0,
832 NULL, 0,
833 WLAN_STATUS_UNSPECIFIED_FAILURE,
834 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530835 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530836 cfg80211_disconnected(vif->ndev, reason,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530837 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300838 }
839
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530840 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300841}
842
Kalle Valobdcd8172011-07-18 00:22:30 +0300843static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
844 struct cfg80211_scan_request *request)
845{
Kalle Valod6d5c062011-11-25 13:17:37 +0200846 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530847 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300848 s8 n_channels = 0;
849 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300850 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530851 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300852
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530853 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300854 return -EIO;
855
Kalle Valo10509f92011-12-13 14:52:07 +0200856 ath6kl_cfg80211_sscan_disable(vif);
857
Kalle Valobdcd8172011-07-18 00:22:30 +0300858 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530859 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300860 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530861 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530862 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300863 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
864 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300865 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300866 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300867 }
868 }
869
870 if (request->n_ssids && request->ssids[0].ssid_len) {
871 u8 i;
872
873 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
874 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
875
876 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530877 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
878 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300879 request->ssids[i].ssid_len,
880 request->ssids[i].ssid);
881 }
882
Kalle Valo10509f92011-12-13 14:52:07 +0200883 /*
884 * FIXME: we should clear the IE in fw if it's not set so just
885 * remove the check altogether
886 */
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300887 if (request->ie) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530888 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
889 WMI_FRAME_PROBE_REQ,
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300890 request->ie, request->ie_len);
891 if (ret) {
892 ath6kl_err("failed to set Probe Request appie for "
893 "scan");
894 return ret;
895 }
896 }
897
Jouni Malinen11869be2011-09-02 20:07:06 +0300898 /*
899 * Scan only the requested channels if the request specifies a set of
900 * channels. If the list is longer than the target supports, do not
901 * configure the list and instead, scan all available channels.
902 */
903 if (request->n_channels > 0 &&
904 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300905 u8 i;
906
Jouni Malinen11869be2011-09-02 20:07:06 +0300907 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300908
909 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
910 if (channels == NULL) {
911 ath6kl_warn("failed to set scan channels, "
912 "scan all channels");
913 n_channels = 0;
914 }
915
916 for (i = 0; i < n_channels; i++)
917 channels[i] = request->channels[i]->center_freq;
918 }
919
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530920 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530921 force_fg_scan = 1;
922
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800923 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
924 ar->fw_capabilities)) {
925 /*
926 * If capable of doing P2P mgmt operations using
927 * station interface, send additional information like
928 * supported rates to advertise and xmit rates for
929 * probe requests
930 */
931 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
932 WMI_LONG_SCAN, force_fg_scan,
933 false, 0, 0, n_channels,
934 channels, request->no_cck,
935 request->rates);
936 } else {
937 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
938 WMI_LONG_SCAN, force_fg_scan,
939 false, 0, 0, n_channels,
940 channels);
941 }
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300942 if (ret)
Kalle Valobdcd8172011-07-18 00:22:30 +0300943 ath6kl_err("wmi_startscan_cmd failed\n");
Jouni Malinen11869be2011-09-02 20:07:06 +0300944 else
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530945 vif->scan_req = request;
Kalle Valobdcd8172011-07-18 00:22:30 +0300946
Edward Lu1276c9e2011-08-30 21:58:00 +0300947 kfree(channels);
948
Kalle Valobdcd8172011-07-18 00:22:30 +0300949 return ret;
950}
951
Kalle Valo1c17d312011-11-01 08:43:56 +0200952void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300953{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530954 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300955 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300956
Kalle Valo1c17d312011-11-01 08:43:56 +0200957 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
958 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300959
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530960 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300961 return;
Kalle Valobdcd8172011-07-18 00:22:30 +0300962
Kalle Valo1c17d312011-11-01 08:43:56 +0200963 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300964 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300965
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530966 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
967 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530968 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
969 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300970 0, NULL);
971 }
972 }
973
974out:
Kalle Valocb938212011-10-27 18:47:46 +0300975 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530976 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300977}
978
979static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
980 u8 key_index, bool pairwise,
981 const u8 *mac_addr,
982 struct key_params *params)
983{
Kalle Valod6d5c062011-11-25 13:17:37 +0200984 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530985 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300986 struct ath6kl_key *key = NULL;
987 u8 key_usage;
988 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +0300989
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530990 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300991 return -EIO;
992
Jouni Malinen837cb972011-10-11 17:31:57 +0300993 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
994 if (params->key_len != WMI_KRK_LEN)
995 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530996 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
997 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +0300998 }
999
Kalle Valobdcd8172011-07-18 00:22:30 +03001000 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1001 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1002 "%s: key index %d out of bounds\n", __func__,
1003 key_index);
1004 return -ENOENT;
1005 }
1006
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301007 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001008 memset(key, 0, sizeof(struct ath6kl_key));
1009
1010 if (pairwise)
1011 key_usage = PAIRWISE_USAGE;
1012 else
1013 key_usage = GROUP_USAGE;
1014
1015 if (params) {
Dai Shuibing5e070212011-11-03 11:39:37 +02001016 int seq_len = params->seq_len;
1017 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1018 seq_len > ATH6KL_KEY_SEQ_LEN) {
1019 /* Only first half of the WPI PN is configured */
1020 seq_len = ATH6KL_KEY_SEQ_LEN;
1021 }
Kalle Valobdcd8172011-07-18 00:22:30 +03001022 if (params->key_len > WLAN_MAX_KEY_LEN ||
Dai Shuibing5e070212011-11-03 11:39:37 +02001023 seq_len > sizeof(key->seq))
Kalle Valobdcd8172011-07-18 00:22:30 +03001024 return -EINVAL;
1025
1026 key->key_len = params->key_len;
1027 memcpy(key->key, params->key, key->key_len);
Dai Shuibing5e070212011-11-03 11:39:37 +02001028 key->seq_len = seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001029 memcpy(key->seq, params->seq, key->seq_len);
1030 key->cipher = params->cipher;
1031 }
1032
1033 switch (key->cipher) {
1034 case WLAN_CIPHER_SUITE_WEP40:
1035 case WLAN_CIPHER_SUITE_WEP104:
1036 key_type = WEP_CRYPT;
1037 break;
1038
1039 case WLAN_CIPHER_SUITE_TKIP:
1040 key_type = TKIP_CRYPT;
1041 break;
1042
1043 case WLAN_CIPHER_SUITE_CCMP:
1044 key_type = AES_CRYPT;
1045 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001046 case WLAN_CIPHER_SUITE_SMS4:
1047 key_type = WAPI_CRYPT;
1048 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001049
1050 default:
1051 return -ENOTSUPP;
1052 }
1053
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301054 if (((vif->auth_mode == WPA_PSK_AUTH)
1055 || (vif->auth_mode == WPA2_PSK_AUTH))
Kalle Valobdcd8172011-07-18 00:22:30 +03001056 && (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301057 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001058
1059 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1060 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1061 __func__, key_index, key->key_len, key_type,
1062 key_usage, key->seq_len);
1063
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301064 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001065 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
1066 key_type == WAPI_CRYPT) && params) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001067 ar->ap_mode_bkey.valid = true;
1068 ar->ap_mode_bkey.key_index = key_index;
1069 ar->ap_mode_bkey.key_type = key_type;
1070 ar->ap_mode_bkey.key_len = key->key_len;
1071 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301072 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001073 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1074 "key configuration until AP mode has been "
1075 "started\n");
1076 /*
1077 * The key will be set in ath6kl_connect_ap_mode() once
1078 * the connected event is received from the target.
1079 */
1080 return 0;
1081 }
1082 }
1083
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301084 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301085 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001086 /*
1087 * Store the key locally so that it can be re-configured after
1088 * the AP mode has properly started
1089 * (ath6kl_install_statioc_wep_keys).
1090 */
1091 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1092 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301093 vif->wep_key_list[key_index].key_len = key->key_len;
1094 memcpy(vif->wep_key_list[key_index].key, key->key,
1095 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001096 return 0;
1097 }
1098
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301099 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001100 key_type, key_usage, key->key_len,
1101 key->seq, key->seq_len, key->key,
1102 KEY_OP_INIT_VAL,
1103 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001104}
1105
1106static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1107 u8 key_index, bool pairwise,
1108 const u8 *mac_addr)
1109{
Kalle Valod6d5c062011-11-25 13:17:37 +02001110 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301111 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001112
1113 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1114
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301115 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001116 return -EIO;
1117
1118 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1119 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1120 "%s: key index %d out of bounds\n", __func__,
1121 key_index);
1122 return -ENOENT;
1123 }
1124
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301125 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001126 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1127 "%s: index %d is empty\n", __func__, key_index);
1128 return 0;
1129 }
1130
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301131 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001132
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301133 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001134}
1135
1136static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1137 u8 key_index, bool pairwise,
1138 const u8 *mac_addr, void *cookie,
1139 void (*callback) (void *cookie,
1140 struct key_params *))
1141{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301142 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001143 struct ath6kl_key *key = NULL;
1144 struct key_params params;
1145
1146 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1147
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301148 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001149 return -EIO;
1150
1151 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1152 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1153 "%s: key index %d out of bounds\n", __func__,
1154 key_index);
1155 return -ENOENT;
1156 }
1157
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301158 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001159 memset(&params, 0, sizeof(params));
1160 params.cipher = key->cipher;
1161 params.key_len = key->key_len;
1162 params.seq_len = key->seq_len;
1163 params.seq = key->seq;
1164 params.key = key->key;
1165
1166 callback(cookie, &params);
1167
1168 return key->key_len ? 0 : -ENOENT;
1169}
1170
1171static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1172 struct net_device *ndev,
1173 u8 key_index, bool unicast,
1174 bool multicast)
1175{
Kalle Valod6d5c062011-11-25 13:17:37 +02001176 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301177 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001178 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001179 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001180 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001181
1182 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1183
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301184 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001185 return -EIO;
1186
1187 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1188 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1189 "%s: key index %d out of bounds\n",
1190 __func__, key_index);
1191 return -ENOENT;
1192 }
1193
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301194 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001195 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1196 __func__, key_index);
1197 return -EINVAL;
1198 }
1199
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301200 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301201 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001202 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301203 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001204 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001205 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301206 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001207 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301208 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001209
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301210 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001211 return 0; /* Delay until AP mode has been started */
1212
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001213 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1214 vif->def_txkey_index,
1215 key_type, key_usage,
1216 key->key_len, key->seq, key->seq_len,
1217 key->key,
1218 KEY_OP_INIT_VAL, NULL,
1219 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001220}
1221
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301222void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001223 bool ismcast)
1224{
1225 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1226 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1227
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301228 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001229 (ismcast ? NL80211_KEYTYPE_GROUP :
1230 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1231 GFP_KERNEL);
1232}
1233
1234static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1235{
1236 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301237 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001238 int ret;
1239
1240 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1241 changed);
1242
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301243 vif = ath6kl_vif_first(ar);
1244 if (!vif)
1245 return -EIO;
1246
1247 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001248 return -EIO;
1249
1250 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1251 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1252 if (ret != 0) {
1253 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1254 return -EIO;
1255 }
1256 }
1257
1258 return 0;
1259}
1260
1261/*
1262 * The type nl80211_tx_power_setting replaces the following
1263 * data type from 2.6.36 onwards
1264*/
1265static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1266 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001267 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001268{
1269 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301270 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001271 u8 ath6kl_dbm;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001272 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001273
1274 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1275 type, dbm);
1276
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301277 vif = ath6kl_vif_first(ar);
1278 if (!vif)
1279 return -EIO;
1280
1281 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001282 return -EIO;
1283
1284 switch (type) {
1285 case NL80211_TX_POWER_AUTOMATIC:
1286 return 0;
1287 case NL80211_TX_POWER_LIMITED:
1288 ar->tx_pwr = ath6kl_dbm = dbm;
1289 break;
1290 default:
1291 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1292 __func__, type);
1293 return -EOPNOTSUPP;
1294 }
1295
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301296 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001297
1298 return 0;
1299}
1300
1301static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1302{
1303 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301304 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001305
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301306 vif = ath6kl_vif_first(ar);
1307 if (!vif)
1308 return -EIO;
1309
1310 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001311 return -EIO;
1312
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301313 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001314 ar->tx_pwr = 0;
1315
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301316 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001317 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1318 return -EIO;
1319 }
1320
1321 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1322 5 * HZ);
1323
1324 if (signal_pending(current)) {
1325 ath6kl_err("target did not respond\n");
1326 return -EINTR;
1327 }
1328 }
1329
1330 *dbm = ar->tx_pwr;
1331 return 0;
1332}
1333
1334static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1335 struct net_device *dev,
1336 bool pmgmt, int timeout)
1337{
1338 struct ath6kl *ar = ath6kl_priv(dev);
1339 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301340 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001341
1342 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1343 __func__, pmgmt, timeout);
1344
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301345 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001346 return -EIO;
1347
1348 if (pmgmt) {
1349 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1350 mode.pwr_mode = REC_POWER;
1351 } else {
1352 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1353 mode.pwr_mode = MAX_PERF_POWER;
1354 }
1355
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301356 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
1357 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001358 ath6kl_err("wmi_powermode_cmd failed\n");
1359 return -EIO;
1360 }
1361
1362 return 0;
1363}
1364
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301365static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1366 char *name,
1367 enum nl80211_iftype type,
1368 u32 *flags,
1369 struct vif_params *params)
1370{
1371 struct ath6kl *ar = wiphy_priv(wiphy);
1372 struct net_device *ndev;
1373 u8 if_idx, nw_type;
1374
Kalle Valo71f96ee2011-11-14 19:31:30 +02001375 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301376 ath6kl_err("Reached maximum number of supported vif\n");
1377 return ERR_PTR(-EINVAL);
1378 }
1379
1380 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1381 ath6kl_err("Not a supported interface type\n");
1382 return ERR_PTR(-EINVAL);
1383 }
1384
1385 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1386 if (!ndev)
1387 return ERR_PTR(-ENOMEM);
1388
1389 ar->num_vif++;
1390
1391 return ndev;
1392}
1393
1394static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1395 struct net_device *ndev)
1396{
1397 struct ath6kl *ar = wiphy_priv(wiphy);
1398 struct ath6kl_vif *vif = netdev_priv(ndev);
1399
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301400 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301401 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301402 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301403
1404 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1405
1406 ath6kl_deinit_if_data(vif);
1407
1408 return 0;
1409}
1410
Kalle Valobdcd8172011-07-18 00:22:30 +03001411static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1412 struct net_device *ndev,
1413 enum nl80211_iftype type, u32 *flags,
1414 struct vif_params *params)
1415{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301416 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001417
1418 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1419
Kalle Valobdcd8172011-07-18 00:22:30 +03001420 switch (type) {
1421 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301422 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001423 break;
1424 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301425 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001426 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001427 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301428 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001429 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001430 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301431 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001432 break;
1433 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301434 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001435 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001436 default:
1437 ath6kl_err("invalid interface type %u\n", type);
1438 return -EOPNOTSUPP;
1439 }
1440
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301441 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001442
1443 return 0;
1444}
1445
1446static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1447 struct net_device *dev,
1448 struct cfg80211_ibss_params *ibss_param)
1449{
1450 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301451 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001452 int status;
1453
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301454 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001455 return -EIO;
1456
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301457 vif->ssid_len = ibss_param->ssid_len;
1458 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001459
1460 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301461 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001462
1463 if (ibss_param->channel_fixed) {
1464 /*
1465 * TODO: channel_fixed: The channel should be fixed, do not
1466 * search for IBSSs to join on other channels. Target
1467 * firmware does not support this feature, needs to be
1468 * updated.
1469 */
1470 return -EOPNOTSUPP;
1471 }
1472
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301473 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001474 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301475 memcpy(vif->req_bssid, ibss_param->bssid,
1476 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001477
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301478 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001479
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301480 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001481 if (status)
1482 return status;
1483
1484 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301485 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1486 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001487 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301488 ath6kl_set_cipher(vif, 0, true);
1489 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001490 }
1491
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301492 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001493
1494 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1495 "%s: connect called with authmode %d dot11 auth %d"
1496 " PW crypto %d PW crypto len %d GRP crypto %d"
1497 " GRP crypto len %d channel hint %u\n",
1498 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301499 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1500 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301501 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001502
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301503 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301504 vif->dot11_auth_mode, vif->auth_mode,
1505 vif->prwise_crypto,
1506 vif->prwise_crypto_len,
1507 vif->grp_crypto, vif->grp_crypto_len,
1508 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301509 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001510 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301511 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001512
1513 return 0;
1514}
1515
1516static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1517 struct net_device *dev)
1518{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301519 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001520
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301521 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001522 return -EIO;
1523
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301524 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301525 memset(vif->ssid, 0, sizeof(vif->ssid));
1526 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001527
1528 return 0;
1529}
1530
1531static const u32 cipher_suites[] = {
1532 WLAN_CIPHER_SUITE_WEP40,
1533 WLAN_CIPHER_SUITE_WEP104,
1534 WLAN_CIPHER_SUITE_TKIP,
1535 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001536 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001537 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001538};
1539
1540static bool is_rate_legacy(s32 rate)
1541{
1542 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1543 6000, 9000, 12000, 18000, 24000,
1544 36000, 48000, 54000
1545 };
1546 u8 i;
1547
1548 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1549 if (rate == legacy[i])
1550 return true;
1551
1552 return false;
1553}
1554
1555static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1556{
1557 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1558 52000, 58500, 65000, 72200
1559 };
1560 u8 i;
1561
1562 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1563 if (rate == ht20[i]) {
1564 if (i == ARRAY_SIZE(ht20) - 1)
1565 /* last rate uses sgi */
1566 *sgi = true;
1567 else
1568 *sgi = false;
1569
1570 *mcs = i;
1571 return true;
1572 }
1573 }
1574 return false;
1575}
1576
1577static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1578{
1579 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1580 81000, 108000, 121500, 135000,
1581 150000
1582 };
1583 u8 i;
1584
1585 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1586 if (rate == ht40[i]) {
1587 if (i == ARRAY_SIZE(ht40) - 1)
1588 /* last rate uses sgi */
1589 *sgi = true;
1590 else
1591 *sgi = false;
1592
1593 *mcs = i;
1594 return true;
1595 }
1596 }
1597
1598 return false;
1599}
1600
1601static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1602 u8 *mac, struct station_info *sinfo)
1603{
1604 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301605 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001606 long left;
1607 bool sgi;
1608 s32 rate;
1609 int ret;
1610 u8 mcs;
1611
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301612 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001613 return -ENOENT;
1614
1615 if (down_interruptible(&ar->sem))
1616 return -EBUSY;
1617
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301618 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001619
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301620 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001621
1622 if (ret != 0) {
1623 up(&ar->sem);
1624 return -EIO;
1625 }
1626
1627 left = wait_event_interruptible_timeout(ar->event_wq,
1628 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301629 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001630 WMI_TIMEOUT);
1631
1632 up(&ar->sem);
1633
1634 if (left == 0)
1635 return -ETIMEDOUT;
1636 else if (left < 0)
1637 return left;
1638
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301639 if (vif->target_stats.rx_byte) {
1640 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001641 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301642 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001643 sinfo->filled |= STATION_INFO_RX_PACKETS;
1644 }
1645
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301646 if (vif->target_stats.tx_byte) {
1647 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001648 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301649 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001650 sinfo->filled |= STATION_INFO_TX_PACKETS;
1651 }
1652
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301653 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001654 sinfo->filled |= STATION_INFO_SIGNAL;
1655
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301656 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001657
1658 if (is_rate_legacy(rate)) {
1659 sinfo->txrate.legacy = rate / 100;
1660 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1661 if (sgi) {
1662 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1663 sinfo->txrate.mcs = mcs - 1;
1664 } else {
1665 sinfo->txrate.mcs = mcs;
1666 }
1667
1668 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1669 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1670 if (sgi) {
1671 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1672 sinfo->txrate.mcs = mcs - 1;
1673 } else {
1674 sinfo->txrate.mcs = mcs;
1675 }
1676
1677 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1678 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1679 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001680 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1681 "invalid rate from stats: %d\n", rate);
1682 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001683 return 0;
1684 }
1685
1686 sinfo->filled |= STATION_INFO_TX_BITRATE;
1687
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301688 if (test_bit(CONNECTED, &vif->flags) &&
1689 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301690 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001691 sinfo->filled |= STATION_INFO_BSS_PARAM;
1692 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301693 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1694 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001695 }
1696
Kalle Valobdcd8172011-07-18 00:22:30 +03001697 return 0;
1698}
1699
1700static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1701 struct cfg80211_pmksa *pmksa)
1702{
1703 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301704 struct ath6kl_vif *vif = netdev_priv(netdev);
1705
1706 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001707 pmksa->pmkid, true);
1708}
1709
1710static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1711 struct cfg80211_pmksa *pmksa)
1712{
1713 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301714 struct ath6kl_vif *vif = netdev_priv(netdev);
1715
1716 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001717 pmksa->pmkid, false);
1718}
1719
1720static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1721{
1722 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301723 struct ath6kl_vif *vif = netdev_priv(netdev);
1724
1725 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301726 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1727 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001728 return 0;
1729}
1730
Raja Mani6cb3c712011-11-07 22:52:45 +02001731static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1732{
1733 struct ath6kl_vif *vif;
1734 int ret, pos, left;
1735 u32 filter = 0;
1736 u16 i;
1737 u8 mask[WOW_MASK_SIZE];
1738
1739 vif = ath6kl_vif_first(ar);
1740 if (!vif)
1741 return -EIO;
1742
1743 if (!ath6kl_cfg80211_ready(vif))
1744 return -EIO;
1745
1746 if (!test_bit(CONNECTED, &vif->flags))
1747 return -EINVAL;
1748
1749 /* Clear existing WOW patterns */
1750 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
1751 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
1752 WOW_LIST_ID, i);
1753 /* Configure new WOW patterns */
1754 for (i = 0; i < wow->n_patterns; i++) {
1755
1756 /*
1757 * Convert given nl80211 specific mask value to equivalent
1758 * driver specific mask value and send it to the chip along
1759 * with patterns. For example, If the mask value defined in
1760 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1761 * then equivalent driver specific mask value is
1762 * "0xFF 0x00 0xFF 0x00".
1763 */
1764 memset(&mask, 0, sizeof(mask));
1765 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1766 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1767 mask[pos] = 0xFF;
1768 }
1769 /*
1770 * Note: Pattern's offset is not passed as part of wowlan
1771 * parameter from CFG layer. So it's always passed as ZERO
1772 * to the firmware. It means, given WOW patterns are always
1773 * matched from the first byte of received pkt in the firmware.
1774 */
1775 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1776 vif->fw_vif_idx, WOW_LIST_ID,
1777 wow->patterns[i].pattern_len,
1778 0 /* pattern offset */,
1779 wow->patterns[i].pattern, mask);
1780 if (ret)
1781 return ret;
1782 }
1783
1784 if (wow->disconnect)
1785 filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1786
1787 if (wow->magic_pkt)
1788 filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1789
1790 if (wow->gtk_rekey_failure)
1791 filter |= WOW_FILTER_OPTION_GTK_ERROR;
1792
1793 if (wow->eap_identity_req)
1794 filter |= WOW_FILTER_OPTION_EAP_REQ;
1795
1796 if (wow->four_way_handshake)
1797 filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1798
1799 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
1800 ATH6KL_WOW_MODE_ENABLE,
1801 filter,
1802 WOW_HOST_REQ_DELAY);
1803 if (ret)
1804 return ret;
1805
1806 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1807 ATH6KL_HOST_MODE_ASLEEP);
1808 if (ret)
1809 return ret;
1810
1811 if (ar->tx_pending[ar->ctrl_ep]) {
1812 left = wait_event_interruptible_timeout(ar->event_wq,
1813 ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
1814 if (left == 0) {
1815 ath6kl_warn("clear wmi ctrl data timeout\n");
1816 ret = -ETIMEDOUT;
1817 } else if (left < 0) {
1818 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
1819 ret = left;
1820 }
1821 }
1822
1823 return ret;
1824}
1825
1826static int ath6kl_wow_resume(struct ath6kl *ar)
1827{
1828 struct ath6kl_vif *vif;
1829 int ret;
1830
1831 vif = ath6kl_vif_first(ar);
1832 if (!vif)
1833 return -EIO;
1834
1835 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1836 ATH6KL_HOST_MODE_AWAKE);
1837 return ret;
1838}
1839
Kalle Valo52d81a62011-11-01 08:44:21 +02001840int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02001841 enum ath6kl_cfg_suspend_mode mode,
1842 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02001843{
1844 int ret;
1845
Kalle Valo52d81a62011-11-01 08:44:21 +02001846 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02001847 case ATH6KL_CFG_SUSPEND_WOW:
1848
1849 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
1850
1851 /* Flush all non control pkts in TX path */
1852 ath6kl_tx_data_cleanup(ar);
1853
1854 ret = ath6kl_wow_suspend(ar, wow);
1855 if (ret) {
1856 ath6kl_err("wow suspend failed: %d\n", ret);
1857 return ret;
1858 }
1859 ar->state = ATH6KL_STATE_WOW;
1860 break;
1861
Kalle Valo52d81a62011-11-01 08:44:21 +02001862 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02001863
Kalle Valo7125f012011-12-13 14:51:37 +02001864 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02001865
Kalle Valo52d81a62011-11-01 08:44:21 +02001866 /* save the current power mode before enabling power save */
1867 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
1868
1869 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
1870 if (ret) {
1871 ath6kl_warn("wmi powermode command failed during suspend: %d\n",
1872 ret);
1873 }
1874
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001875 ar->state = ATH6KL_STATE_DEEPSLEEP;
1876
Kalle Valo52d81a62011-11-01 08:44:21 +02001877 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001878
1879 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02001880
Kalle Valo7125f012011-12-13 14:51:37 +02001881 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02001882
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001883 if (ar->state == ATH6KL_STATE_OFF) {
1884 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
1885 "suspend hw off, no action for cutpower\n");
1886 break;
1887 }
1888
1889 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
1890
1891 ret = ath6kl_init_hw_stop(ar);
1892 if (ret) {
1893 ath6kl_warn("failed to stop hw during suspend: %d\n",
1894 ret);
1895 }
1896
1897 ar->state = ATH6KL_STATE_CUTPOWER;
1898
1899 break;
1900
Kalle Valo10509f92011-12-13 14:52:07 +02001901 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
1902 /*
1903 * Nothing needed for schedule scan, firmware is already in
1904 * wow mode and sleeping most of the time.
1905 */
1906 break;
1907
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001908 default:
1909 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02001910 }
1911
1912 return 0;
1913}
1914
1915int ath6kl_cfg80211_resume(struct ath6kl *ar)
1916{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001917 int ret;
1918
1919 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02001920 case ATH6KL_STATE_WOW:
1921 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
1922
1923 ret = ath6kl_wow_resume(ar);
1924 if (ret) {
1925 ath6kl_warn("wow mode resume failed: %d\n", ret);
1926 return ret;
1927 }
1928
1929 ar->state = ATH6KL_STATE_ON;
1930 break;
1931
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001932 case ATH6KL_STATE_DEEPSLEEP:
1933 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
1934 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
1935 ar->wmi->saved_pwr_mode);
1936 if (ret) {
1937 ath6kl_warn("wmi powermode command failed during resume: %d\n",
1938 ret);
1939 }
1940 }
1941
1942 ar->state = ATH6KL_STATE_ON;
1943
1944 break;
1945
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001946 case ATH6KL_STATE_CUTPOWER:
1947 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
1948
1949 ret = ath6kl_init_hw_start(ar);
1950 if (ret) {
1951 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
1952 return ret;
1953 }
Raja Manid7c44e02011-11-07 22:52:46 +02001954 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001955
Kalle Valo10509f92011-12-13 14:52:07 +02001956 case ATH6KL_STATE_SCHED_SCAN:
1957 break;
1958
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001959 default:
1960 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02001961 }
1962
1963 return 0;
1964}
1965
Kalle Valoabcb3442011-07-22 08:26:20 +03001966#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02001967
1968/* hif layer decides what suspend mode to use */
1969static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03001970 struct cfg80211_wowlan *wow)
1971{
1972 struct ath6kl *ar = wiphy_priv(wiphy);
1973
Raja Mani0f60e9f2011-11-07 22:52:45 +02001974 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03001975}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03001976
Kalle Valo52d81a62011-11-01 08:44:21 +02001977static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03001978{
1979 struct ath6kl *ar = wiphy_priv(wiphy);
1980
1981 return ath6kl_hif_resume(ar);
1982}
Raja Mania918fb32011-11-07 22:52:46 +02001983
1984/*
1985 * FIXME: WOW suspend mode is selected if the host sdio controller supports
1986 * both sdio irq wake up and keep power. The target pulls sdio data line to
1987 * wake up the host when WOW pattern matches. This causes sdio irq handler
1988 * is being called in the host side which internally hits ath6kl's RX path.
1989 *
1990 * Since sdio interrupt is not disabled, RX path executes even before
1991 * the host executes the actual resume operation from PM module.
1992 *
1993 * In the current scenario, WOW resume should happen before start processing
1994 * any data from the target. So It's required to perform WOW resume in RX path.
1995 * Ideally we should perform WOW resume only in the actual platform
1996 * resume path. This area needs bit rework to avoid WOW resume in RX path.
1997 *
1998 * ath6kl_check_wow_status() is called from ath6kl_rx().
1999 */
2000void ath6kl_check_wow_status(struct ath6kl *ar)
2001{
2002 if (ar->state == ATH6KL_STATE_WOW)
2003 ath6kl_cfg80211_resume(ar);
2004}
2005
2006#else
2007
2008void ath6kl_check_wow_status(struct ath6kl *ar)
2009{
2010}
Kalle Valoabcb3442011-07-22 08:26:20 +03002011#endif
2012
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002013static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2014 struct ieee80211_channel *chan,
2015 enum nl80211_channel_type channel_type)
2016{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302017 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002018
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302019 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002020 return -EIO;
2021
2022 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2023 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302024 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002025
2026 return 0;
2027}
2028
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002029static bool ath6kl_is_p2p_ie(const u8 *pos)
2030{
2031 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2032 pos[2] == 0x50 && pos[3] == 0x6f &&
2033 pos[4] == 0x9a && pos[5] == 0x09;
2034}
2035
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302036static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2037 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002038{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302039 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002040 const u8 *pos;
2041 u8 *buf = NULL;
2042 size_t len = 0;
2043 int ret;
2044
2045 /*
2046 * Filter out P2P IE(s) since they will be included depending on
2047 * the Probe Request frame in ath6kl_send_go_probe_resp().
2048 */
2049
2050 if (ies && ies_len) {
2051 buf = kmalloc(ies_len, GFP_KERNEL);
2052 if (buf == NULL)
2053 return -ENOMEM;
2054 pos = ies;
2055 while (pos + 1 < ies + ies_len) {
2056 if (pos + 2 + pos[1] > ies + ies_len)
2057 break;
2058 if (!ath6kl_is_p2p_ie(pos)) {
2059 memcpy(buf + len, pos, 2 + pos[1]);
2060 len += 2 + pos[1];
2061 }
2062 pos += 2 + pos[1];
2063 }
2064 }
2065
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302066 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2067 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002068 kfree(buf);
2069 return ret;
2070}
2071
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002072static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
2073 struct beacon_parameters *info, bool add)
2074{
2075 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302076 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002077 struct ieee80211_mgmt *mgmt;
2078 u8 *ies;
2079 int ies_len;
2080 struct wmi_connect_cmd p;
2081 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302082 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002083
2084 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
2085
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302086 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002087 return -EIO;
2088
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302089 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002090 return -EOPNOTSUPP;
2091
2092 if (info->beacon_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302093 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2094 WMI_FRAME_BEACON,
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002095 info->beacon_ies,
2096 info->beacon_ies_len);
2097 if (res)
2098 return res;
2099 }
2100 if (info->proberesp_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302101 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002102 info->proberesp_ies_len);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002103 if (res)
2104 return res;
2105 }
2106 if (info->assocresp_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302107 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2108 WMI_FRAME_ASSOC_RESP,
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002109 info->assocresp_ies,
2110 info->assocresp_ies_len);
2111 if (res)
2112 return res;
2113 }
2114
2115 if (!add)
2116 return 0;
2117
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002118 ar->ap_mode_bkey.valid = false;
2119
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002120 /* TODO:
2121 * info->interval
2122 * info->dtim_period
2123 */
2124
2125 if (info->head == NULL)
2126 return -EINVAL;
2127 mgmt = (struct ieee80211_mgmt *) info->head;
2128 ies = mgmt->u.beacon.variable;
2129 if (ies > info->head + info->head_len)
2130 return -EINVAL;
2131 ies_len = info->head + info->head_len - ies;
2132
2133 if (info->ssid == NULL)
2134 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302135 memcpy(vif->ssid, info->ssid, info->ssid_len);
2136 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002137 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
2138 return -EOPNOTSUPP; /* TODO */
2139
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302140 ret = ath6kl_set_auth_type(vif, info->auth_type);
2141 if (ret)
2142 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002143
2144 memset(&p, 0, sizeof(p));
2145
2146 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2147 switch (info->crypto.akm_suites[i]) {
2148 case WLAN_AKM_SUITE_8021X:
2149 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2150 p.auth_mode |= WPA_AUTH;
2151 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2152 p.auth_mode |= WPA2_AUTH;
2153 break;
2154 case WLAN_AKM_SUITE_PSK:
2155 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2156 p.auth_mode |= WPA_PSK_AUTH;
2157 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2158 p.auth_mode |= WPA2_PSK_AUTH;
2159 break;
2160 }
2161 }
2162 if (p.auth_mode == 0)
2163 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302164 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002165
2166 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2167 switch (info->crypto.ciphers_pairwise[i]) {
2168 case WLAN_CIPHER_SUITE_WEP40:
2169 case WLAN_CIPHER_SUITE_WEP104:
2170 p.prwise_crypto_type |= WEP_CRYPT;
2171 break;
2172 case WLAN_CIPHER_SUITE_TKIP:
2173 p.prwise_crypto_type |= TKIP_CRYPT;
2174 break;
2175 case WLAN_CIPHER_SUITE_CCMP:
2176 p.prwise_crypto_type |= AES_CRYPT;
2177 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002178 case WLAN_CIPHER_SUITE_SMS4:
2179 p.prwise_crypto_type |= WAPI_CRYPT;
2180 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002181 }
2182 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002183 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002184 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302185 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002186 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302187 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002188
2189 switch (info->crypto.cipher_group) {
2190 case WLAN_CIPHER_SUITE_WEP40:
2191 case WLAN_CIPHER_SUITE_WEP104:
2192 p.grp_crypto_type = WEP_CRYPT;
2193 break;
2194 case WLAN_CIPHER_SUITE_TKIP:
2195 p.grp_crypto_type = TKIP_CRYPT;
2196 break;
2197 case WLAN_CIPHER_SUITE_CCMP:
2198 p.grp_crypto_type = AES_CRYPT;
2199 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002200 case WLAN_CIPHER_SUITE_SMS4:
2201 p.grp_crypto_type = WAPI_CRYPT;
2202 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002203 default:
2204 p.grp_crypto_type = NONE_CRYPT;
2205 break;
2206 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302207 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002208
2209 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302210 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002211
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302212 p.ssid_len = vif->ssid_len;
2213 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2214 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302215 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002216
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002217 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2218 p.nw_subtype = SUBTYPE_P2PGO;
2219 } else {
2220 /*
2221 * Due to firmware limitation, it is not possible to
2222 * do P2P mgmt operations in AP mode
2223 */
2224 p.nw_subtype = SUBTYPE_NONE;
2225 }
2226
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302227 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002228 if (res < 0)
2229 return res;
2230
2231 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002232}
2233
2234static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
2235 struct beacon_parameters *info)
2236{
2237 return ath6kl_ap_beacon(wiphy, dev, info, true);
2238}
2239
2240static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
2241 struct beacon_parameters *info)
2242{
2243 return ath6kl_ap_beacon(wiphy, dev, info, false);
2244}
2245
2246static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
2247{
2248 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302249 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002250
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302251 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002252 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302253 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002254 return -ENOTCONN;
2255
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302256 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302257 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002258
2259 return 0;
2260}
2261
Jouni Malinen23875132011-08-30 21:57:53 +03002262static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2263 u8 *mac, struct station_parameters *params)
2264{
2265 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302266 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002267
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302268 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002269 return -EOPNOTSUPP;
2270
2271 /* Use this only for authorizing/unauthorizing a station */
2272 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2273 return -EOPNOTSUPP;
2274
2275 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302276 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2277 WMI_AP_MLME_AUTHORIZE, mac, 0);
2278 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2279 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002280}
2281
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002282static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2283 struct net_device *dev,
2284 struct ieee80211_channel *chan,
2285 enum nl80211_channel_type channel_type,
2286 unsigned int duration,
2287 u64 *cookie)
2288{
2289 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302290 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002291 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002292
2293 /* TODO: if already pending or ongoing remain-on-channel,
2294 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002295 id = ++vif->last_roc_id;
2296 if (id == 0) {
2297 /* Do not use 0 as the cookie value */
2298 id = ++vif->last_roc_id;
2299 }
2300 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002301
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302302 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2303 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002304}
2305
2306static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2307 struct net_device *dev,
2308 u64 cookie)
2309{
2310 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302311 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002312
Jouni Malinen10522612011-10-27 16:00:13 +03002313 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002314 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002315 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002316
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302317 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002318}
2319
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302320static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2321 const u8 *buf, size_t len,
2322 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002323{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302324 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002325 const u8 *pos;
2326 u8 *p2p;
2327 int p2p_len;
2328 int ret;
2329 const struct ieee80211_mgmt *mgmt;
2330
2331 mgmt = (const struct ieee80211_mgmt *) buf;
2332
2333 /* Include P2P IE(s) from the frame generated in user space. */
2334
2335 p2p = kmalloc(len, GFP_KERNEL);
2336 if (p2p == NULL)
2337 return -ENOMEM;
2338 p2p_len = 0;
2339
2340 pos = mgmt->u.probe_resp.variable;
2341 while (pos + 1 < buf + len) {
2342 if (pos + 2 + pos[1] > buf + len)
2343 break;
2344 if (ath6kl_is_p2p_ie(pos)) {
2345 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2346 p2p_len += 2 + pos[1];
2347 }
2348 pos += 2 + pos[1];
2349 }
2350
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302351 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2352 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002353 kfree(p2p);
2354 return ret;
2355}
2356
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002357static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2358 struct ieee80211_channel *chan, bool offchan,
2359 enum nl80211_channel_type channel_type,
2360 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002361 const u8 *buf, size_t len, bool no_cck,
2362 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002363{
2364 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302365 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002366 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002367 const struct ieee80211_mgmt *mgmt;
2368
2369 mgmt = (const struct ieee80211_mgmt *) buf;
2370 if (buf + len >= mgmt->u.probe_resp.variable &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302371 vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002372 ieee80211_is_probe_resp(mgmt->frame_control)) {
2373 /*
2374 * Send Probe Response frame in AP mode using a separate WMI
2375 * command to allow the target to fill in the generic IEs.
2376 */
2377 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302378 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002379 chan->center_freq);
2380 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002381
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302382 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002383 if (id == 0) {
2384 /*
2385 * 0 is a reserved value in the WMI command and shall not be
2386 * used for the command.
2387 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302388 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002389 }
2390
2391 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002392
2393 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
2394 ar->fw_capabilities)) {
2395 /*
2396 * If capable of doing P2P mgmt operations using
2397 * station interface, send additional information like
2398 * supported rates to advertise and xmit rates for
2399 * probe requests
2400 */
2401 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2402 chan->center_freq, wait,
2403 buf, len, no_cck);
2404 } else {
2405 return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
2406 chan->center_freq, wait,
2407 buf, len);
2408 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002409}
2410
Jouni Malinenae32c302011-08-30 21:58:01 +03002411static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2412 struct net_device *dev,
2413 u16 frame_type, bool reg)
2414{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302415 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002416
2417 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2418 __func__, frame_type, reg);
2419 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2420 /*
2421 * Note: This notification callback is not allowed to sleep, so
2422 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2423 * hardcode target to report Probe Request frames all the time.
2424 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302425 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002426 }
2427}
2428
Kalle Valo10509f92011-12-13 14:52:07 +02002429static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2430 struct net_device *dev,
2431 struct cfg80211_sched_scan_request *request)
2432{
2433 struct ath6kl *ar = ath6kl_priv(dev);
2434 struct ath6kl_vif *vif = netdev_priv(dev);
2435 u16 interval;
2436 int ret;
2437 u8 i;
2438
2439 if (ar->state != ATH6KL_STATE_ON)
2440 return -EIO;
2441
2442 if (vif->sme_state != SME_DISCONNECTED)
2443 return -EBUSY;
2444
2445 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2446 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2447 i, DISABLE_SSID_FLAG,
2448 0, NULL);
2449 }
2450
2451 /* fw uses seconds, also make sure that it's >0 */
2452 interval = max_t(u16, 1, request->interval / 1000);
2453
2454 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2455 interval, interval,
2456 10, 0, 0, 0, 3, 0, 0, 0);
2457
2458 if (request->n_ssids && request->ssids[0].ssid_len) {
2459 for (i = 0; i < request->n_ssids; i++) {
2460 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2461 i, SPECIFIC_SSID_FLAG,
2462 request->ssids[i].ssid_len,
2463 request->ssids[i].ssid);
2464 }
2465 }
2466
2467 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2468 ATH6KL_WOW_MODE_ENABLE,
2469 WOW_FILTER_SSID,
2470 WOW_HOST_REQ_DELAY);
2471 if (ret) {
2472 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2473 return ret;
2474 }
2475
2476 /* this also clears IE in fw if it's not set */
2477 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2478 WMI_FRAME_PROBE_REQ,
2479 request->ie, request->ie_len);
2480 if (ret) {
2481 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
2482 ret);
2483 return ret;
2484 }
2485
2486 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2487 ATH6KL_HOST_MODE_ASLEEP);
2488 if (ret) {
2489 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
2490 ret);
2491 return ret;
2492 }
2493
2494 ar->state = ATH6KL_STATE_SCHED_SCAN;
2495
2496 return ret;
2497}
2498
2499static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
2500 struct net_device *dev)
2501{
2502 struct ath6kl_vif *vif = netdev_priv(dev);
2503 bool stopped;
2504
2505 stopped = __ath6kl_cfg80211_sscan_stop(vif);
2506
2507 if (!stopped)
2508 return -EIO;
2509
2510 return 0;
2511}
2512
Jouni Malinenf80574a2011-08-30 21:58:04 +03002513static const struct ieee80211_txrx_stypes
2514ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2515 [NL80211_IFTYPE_STATION] = {
2516 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2517 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2518 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2519 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2520 },
2521 [NL80211_IFTYPE_P2P_CLIENT] = {
2522 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2523 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2524 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2525 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2526 },
2527 [NL80211_IFTYPE_P2P_GO] = {
2528 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2529 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2530 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2531 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2532 },
2533};
2534
Kalle Valobdcd8172011-07-18 00:22:30 +03002535static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302536 .add_virtual_intf = ath6kl_cfg80211_add_iface,
2537 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03002538 .change_virtual_intf = ath6kl_cfg80211_change_iface,
2539 .scan = ath6kl_cfg80211_scan,
2540 .connect = ath6kl_cfg80211_connect,
2541 .disconnect = ath6kl_cfg80211_disconnect,
2542 .add_key = ath6kl_cfg80211_add_key,
2543 .get_key = ath6kl_cfg80211_get_key,
2544 .del_key = ath6kl_cfg80211_del_key,
2545 .set_default_key = ath6kl_cfg80211_set_default_key,
2546 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
2547 .set_tx_power = ath6kl_cfg80211_set_txpower,
2548 .get_tx_power = ath6kl_cfg80211_get_txpower,
2549 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
2550 .join_ibss = ath6kl_cfg80211_join_ibss,
2551 .leave_ibss = ath6kl_cfg80211_leave_ibss,
2552 .get_station = ath6kl_get_station,
2553 .set_pmksa = ath6kl_set_pmksa,
2554 .del_pmksa = ath6kl_del_pmksa,
2555 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03002556 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03002557#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002558 .suspend = __ath6kl_cfg80211_suspend,
2559 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03002560#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002561 .set_channel = ath6kl_set_channel,
2562 .add_beacon = ath6kl_add_beacon,
2563 .set_beacon = ath6kl_set_beacon,
2564 .del_beacon = ath6kl_del_beacon,
Jouni Malinen23875132011-08-30 21:57:53 +03002565 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002566 .remain_on_channel = ath6kl_remain_on_channel,
2567 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002568 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03002569 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02002570 .sched_scan_start = ath6kl_cfg80211_sscan_start,
2571 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03002572};
2573
Kalle Valo7125f012011-12-13 14:51:37 +02002574void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02002575{
Kalle Valo10509f92011-12-13 14:52:07 +02002576 ath6kl_cfg80211_sscan_disable(vif);
2577
Kalle Valoec4b7f62011-11-01 08:44:04 +02002578 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02002579 case SME_DISCONNECTED:
2580 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02002581 case SME_CONNECTING:
2582 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
2583 NULL, 0,
2584 WLAN_STATUS_UNSPECIFIED_FAILURE,
2585 GFP_KERNEL);
2586 break;
2587 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02002588 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
2589 break;
2590 }
2591
2592 if (test_bit(CONNECTED, &vif->flags) ||
2593 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02002594 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002595
2596 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02002597 clear_bit(CONNECTED, &vif->flags);
2598 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002599
2600 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02002601 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
2602 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
2603 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02002604
2605 ath6kl_cfg80211_scan_complete_event(vif, true);
2606}
2607
Kalle Valo7125f012011-12-13 14:51:37 +02002608void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
2609{
2610 struct ath6kl_vif *vif;
2611
2612 vif = ath6kl_vif_first(ar);
2613 if (!vif) {
2614 /* save the current power mode before enabling power save */
2615 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2616
2617 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
2618 ath6kl_warn("ath6kl_deep_sleep_enable: "
2619 "wmi_powermode_cmd failed\n");
2620 return;
2621 }
2622
2623 /*
2624 * FIXME: we should take ar->list_lock to protect changes in the
2625 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
2626 * sleeps.
2627 */
2628 list_for_each_entry(vif, &ar->vif_list, list)
2629 ath6kl_cfg80211_stop(vif);
2630}
2631
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302632struct ath6kl *ath6kl_core_alloc(struct device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +03002633{
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002634 struct ath6kl *ar;
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302635 struct wiphy *wiphy;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302636 u8 ctr;
Kalle Valobdcd8172011-07-18 00:22:30 +03002637
2638 /* create a new wiphy for use with cfg80211 */
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302639 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302640
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302641 if (!wiphy) {
Kalle Valobdcd8172011-07-18 00:22:30 +03002642 ath6kl_err("couldn't allocate wiphy device\n");
Kalle Valobdcd8172011-07-18 00:22:30 +03002643 return NULL;
2644 }
2645
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302646 ar = wiphy_priv(wiphy);
Vasanthakumar Thiagarajan774439a2011-11-18 10:05:26 +05302647 ar->p2p = !!ath6kl_p2p;
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302648 ar->wiphy = wiphy;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302649 ar->dev = dev;
2650
Kalle Valo71f96ee2011-11-14 19:31:30 +02002651 ar->vif_max = 1;
2652
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +05302653 ar->max_norm_iface = 1;
2654
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302655 spin_lock_init(&ar->lock);
2656 spin_lock_init(&ar->mcastpsq_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302657 spin_lock_init(&ar->list_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302658
2659 init_waitqueue_head(&ar->event_wq);
2660 sema_init(&ar->sem, 1);
2661
2662 INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302663 INIT_LIST_HEAD(&ar->vif_list);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302664
2665 clear_bit(WMI_ENABLED, &ar->flag);
2666 clear_bit(SKIP_SCAN, &ar->flag);
2667 clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
2668
2669 ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL;
2670 ar->listen_intvl_b = 0;
2671 ar->tx_pwr = 0;
2672
2673 ar->intra_bss = 1;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302674 ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
2675
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002676 ar->state = ATH6KL_STATE_OFF;
2677
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302678 memset((u8 *)ar->sta_list, 0,
2679 AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
2680
2681 /* Init the PS queues */
2682 for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
2683 spin_lock_init(&ar->sta_list[ctr].psq_lock);
2684 skb_queue_head_init(&ar->sta_list[ctr].psq);
2685 }
2686
2687 skb_queue_head_init(&ar->mcastpsq);
2688
2689 memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3);
2690
2691 return ar;
2692}
2693
2694int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
2695{
2696 struct wiphy *wiphy = ar->wiphy;
2697 int ret;
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002698
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302699 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
Jouni Malinenf80574a2011-08-30 21:58:04 +03002700
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302701 wiphy->max_remain_on_channel_duration = 5000;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002702
Kalle Valobdcd8172011-07-18 00:22:30 +03002703 /* set device pointer for wiphy */
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302704 set_wiphy_dev(wiphy, ar->dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03002705
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302706 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302707 BIT(NL80211_IFTYPE_ADHOC) |
2708 BIT(NL80211_IFTYPE_AP);
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002709 if (ar->p2p) {
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302710 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302711 BIT(NL80211_IFTYPE_P2P_CLIENT);
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002712 }
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302713
Kalle Valobdcd8172011-07-18 00:22:30 +03002714 /* max num of ssids that can be probed during scanning */
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302715 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
2716 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
2717 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
2718 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
2719 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
Kalle Valobdcd8172011-07-18 00:22:30 +03002720
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302721 wiphy->cipher_suites = cipher_suites;
2722 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
Kalle Valobdcd8172011-07-18 00:22:30 +03002723
Raja Manieae9e062011-11-07 22:52:46 +02002724 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
2725 WIPHY_WOWLAN_DISCONNECT |
2726 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
2727 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
2728 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
2729 WIPHY_WOWLAN_4WAY_HANDSHAKE;
2730 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
2731 wiphy->wowlan.pattern_min_len = 1;
2732 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
2733
Kalle Valo10509f92011-12-13 14:52:07 +02002734 wiphy->max_sched_scan_ssids = 10;
2735
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302736 ret = wiphy_register(wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03002737 if (ret < 0) {
2738 ath6kl_err("couldn't register wiphy device\n");
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302739 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +03002740 }
2741
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302742 return 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03002743}
2744
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302745static int ath6kl_init_if_data(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03002746{
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302747 vif->aggr_cntxt = aggr_init(vif->ndev);
2748 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302749 ath6kl_err("failed to initialize aggr\n");
2750 return -ENOMEM;
2751 }
Kalle Valobdcd8172011-07-18 00:22:30 +03002752
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302753 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302754 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02002755 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
2756 (unsigned long) vif);
2757
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302758 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05302759 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302760
2761 return 0;
2762}
2763
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302764void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302765{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302766 struct ath6kl *ar = vif->ar;
2767
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302768 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302769
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302770 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
2771
2772 if (vif->nw_type == ADHOC_NETWORK)
2773 ar->ibss_if_active = false;
2774
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302775 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302776
2777 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302778}
2779
2780struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302781 enum nl80211_iftype type, u8 fw_vif_idx,
2782 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302783{
2784 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302785 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302786
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302787 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302788 if (!ndev)
2789 return NULL;
2790
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302791 vif = netdev_priv(ndev);
2792 ndev->ieee80211_ptr = &vif->wdev;
2793 vif->wdev.wiphy = ar->wiphy;
2794 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302795 vif->ndev = ndev;
2796 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
2797 vif->wdev.netdev = ndev;
2798 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302799 vif->fw_vif_idx = fw_vif_idx;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302800 vif->nw_type = vif->next_mode = nw_type;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302801
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302802 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
2803 if (fw_vif_idx != 0)
2804 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
2805 0x2;
2806
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302807 init_netdev(ndev);
2808
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05302809 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302810
2811 /* TODO: Pass interface specific pointer instead of ar */
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302812 if (ath6kl_init_if_data(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302813 goto err;
2814
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302815 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302816 goto err;
2817
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302818 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05302819 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302820 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302821 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302822 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302823
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302824 if (type == NL80211_IFTYPE_ADHOC)
2825 ar->ibss_if_active = true;
2826
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05302827 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302828 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05302829 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302830
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302831 return ndev;
2832
2833err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302834 aggr_module_destroy(vif->aggr_cntxt);
2835 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302836 return NULL;
2837}
2838
2839void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar)
2840{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302841 wiphy_unregister(ar->wiphy);
2842 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03002843}