1bdcd8170SKalle Valo /*
2bdcd8170SKalle Valo  * Copyright (c) 2004-2011 Atheros Communications Inc.
3bdcd8170SKalle Valo  *
4bdcd8170SKalle Valo  * Permission to use, copy, modify, and/or distribute this software for any
5bdcd8170SKalle Valo  * purpose with or without fee is hereby granted, provided that the above
6bdcd8170SKalle Valo  * copyright notice and this permission notice appear in all copies.
7bdcd8170SKalle Valo  *
8bdcd8170SKalle Valo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9bdcd8170SKalle Valo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10bdcd8170SKalle Valo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11bdcd8170SKalle Valo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12bdcd8170SKalle Valo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13bdcd8170SKalle Valo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14bdcd8170SKalle Valo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15bdcd8170SKalle Valo  */
16bdcd8170SKalle Valo 
17bdcd8170SKalle Valo #include "core.h"
18bdcd8170SKalle Valo #include "cfg80211.h"
19bdcd8170SKalle Valo #include "debug.h"
20abcb344bSKalle Valo #include "hif-ops.h"
21003353b0SKalle Valo #include "testmode.h"
22bdcd8170SKalle Valo 
236bbc7c35SJouni Malinen static unsigned int ath6kl_p2p;
246bbc7c35SJouni Malinen 
256bbc7c35SJouni Malinen module_param(ath6kl_p2p, uint, 0644);
266bbc7c35SJouni Malinen 
27bdcd8170SKalle Valo #define RATETAB_ENT(_rate, _rateid, _flags) {   \
28bdcd8170SKalle Valo 	.bitrate    = (_rate),                  \
29bdcd8170SKalle Valo 	.flags      = (_flags),                 \
30bdcd8170SKalle Valo 	.hw_value   = (_rateid),                \
31bdcd8170SKalle Valo }
32bdcd8170SKalle Valo 
33bdcd8170SKalle Valo #define CHAN2G(_channel, _freq, _flags) {   \
34bdcd8170SKalle Valo 	.band           = IEEE80211_BAND_2GHZ,  \
35bdcd8170SKalle Valo 	.hw_value       = (_channel),           \
36bdcd8170SKalle Valo 	.center_freq    = (_freq),              \
37bdcd8170SKalle Valo 	.flags          = (_flags),             \
38bdcd8170SKalle Valo 	.max_antenna_gain   = 0,                \
39bdcd8170SKalle Valo 	.max_power      = 30,                   \
40bdcd8170SKalle Valo }
41bdcd8170SKalle Valo 
42bdcd8170SKalle Valo #define CHAN5G(_channel, _flags) {		    \
43bdcd8170SKalle Valo 	.band           = IEEE80211_BAND_5GHZ,      \
44bdcd8170SKalle Valo 	.hw_value       = (_channel),               \
45bdcd8170SKalle Valo 	.center_freq    = 5000 + (5 * (_channel)),  \
46bdcd8170SKalle Valo 	.flags          = (_flags),                 \
47bdcd8170SKalle Valo 	.max_antenna_gain   = 0,                    \
48bdcd8170SKalle Valo 	.max_power      = 30,                       \
49bdcd8170SKalle Valo }
50bdcd8170SKalle Valo 
51bdcd8170SKalle Valo static struct ieee80211_rate ath6kl_rates[] = {
52bdcd8170SKalle Valo 	RATETAB_ENT(10, 0x1, 0),
53bdcd8170SKalle Valo 	RATETAB_ENT(20, 0x2, 0),
54bdcd8170SKalle Valo 	RATETAB_ENT(55, 0x4, 0),
55bdcd8170SKalle Valo 	RATETAB_ENT(110, 0x8, 0),
56bdcd8170SKalle Valo 	RATETAB_ENT(60, 0x10, 0),
57bdcd8170SKalle Valo 	RATETAB_ENT(90, 0x20, 0),
58bdcd8170SKalle Valo 	RATETAB_ENT(120, 0x40, 0),
59bdcd8170SKalle Valo 	RATETAB_ENT(180, 0x80, 0),
60bdcd8170SKalle Valo 	RATETAB_ENT(240, 0x100, 0),
61bdcd8170SKalle Valo 	RATETAB_ENT(360, 0x200, 0),
62bdcd8170SKalle Valo 	RATETAB_ENT(480, 0x400, 0),
63bdcd8170SKalle Valo 	RATETAB_ENT(540, 0x800, 0),
64bdcd8170SKalle Valo };
65bdcd8170SKalle Valo 
66bdcd8170SKalle Valo #define ath6kl_a_rates     (ath6kl_rates + 4)
67bdcd8170SKalle Valo #define ath6kl_a_rates_size    8
68bdcd8170SKalle Valo #define ath6kl_g_rates     (ath6kl_rates + 0)
69bdcd8170SKalle Valo #define ath6kl_g_rates_size    12
70bdcd8170SKalle Valo 
71bdcd8170SKalle Valo static struct ieee80211_channel ath6kl_2ghz_channels[] = {
72bdcd8170SKalle Valo 	CHAN2G(1, 2412, 0),
73bdcd8170SKalle Valo 	CHAN2G(2, 2417, 0),
74bdcd8170SKalle Valo 	CHAN2G(3, 2422, 0),
75bdcd8170SKalle Valo 	CHAN2G(4, 2427, 0),
76bdcd8170SKalle Valo 	CHAN2G(5, 2432, 0),
77bdcd8170SKalle Valo 	CHAN2G(6, 2437, 0),
78bdcd8170SKalle Valo 	CHAN2G(7, 2442, 0),
79bdcd8170SKalle Valo 	CHAN2G(8, 2447, 0),
80bdcd8170SKalle Valo 	CHAN2G(9, 2452, 0),
81bdcd8170SKalle Valo 	CHAN2G(10, 2457, 0),
82bdcd8170SKalle Valo 	CHAN2G(11, 2462, 0),
83bdcd8170SKalle Valo 	CHAN2G(12, 2467, 0),
84bdcd8170SKalle Valo 	CHAN2G(13, 2472, 0),
85bdcd8170SKalle Valo 	CHAN2G(14, 2484, 0),
86bdcd8170SKalle Valo };
87bdcd8170SKalle Valo 
88bdcd8170SKalle Valo static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
89bdcd8170SKalle Valo 	CHAN5G(34, 0), CHAN5G(36, 0),
90bdcd8170SKalle Valo 	CHAN5G(38, 0), CHAN5G(40, 0),
91bdcd8170SKalle Valo 	CHAN5G(42, 0), CHAN5G(44, 0),
92bdcd8170SKalle Valo 	CHAN5G(46, 0), CHAN5G(48, 0),
93bdcd8170SKalle Valo 	CHAN5G(52, 0), CHAN5G(56, 0),
94bdcd8170SKalle Valo 	CHAN5G(60, 0), CHAN5G(64, 0),
95bdcd8170SKalle Valo 	CHAN5G(100, 0), CHAN5G(104, 0),
96bdcd8170SKalle Valo 	CHAN5G(108, 0), CHAN5G(112, 0),
97bdcd8170SKalle Valo 	CHAN5G(116, 0), CHAN5G(120, 0),
98bdcd8170SKalle Valo 	CHAN5G(124, 0), CHAN5G(128, 0),
99bdcd8170SKalle Valo 	CHAN5G(132, 0), CHAN5G(136, 0),
100bdcd8170SKalle Valo 	CHAN5G(140, 0), CHAN5G(149, 0),
101bdcd8170SKalle Valo 	CHAN5G(153, 0), CHAN5G(157, 0),
102bdcd8170SKalle Valo 	CHAN5G(161, 0), CHAN5G(165, 0),
103bdcd8170SKalle Valo 	CHAN5G(184, 0), CHAN5G(188, 0),
104bdcd8170SKalle Valo 	CHAN5G(192, 0), CHAN5G(196, 0),
105bdcd8170SKalle Valo 	CHAN5G(200, 0), CHAN5G(204, 0),
106bdcd8170SKalle Valo 	CHAN5G(208, 0), CHAN5G(212, 0),
107bdcd8170SKalle Valo 	CHAN5G(216, 0),
108bdcd8170SKalle Valo };
109bdcd8170SKalle Valo 
110bdcd8170SKalle Valo static struct ieee80211_supported_band ath6kl_band_2ghz = {
111bdcd8170SKalle Valo 	.n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
112bdcd8170SKalle Valo 	.channels = ath6kl_2ghz_channels,
113bdcd8170SKalle Valo 	.n_bitrates = ath6kl_g_rates_size,
114bdcd8170SKalle Valo 	.bitrates = ath6kl_g_rates,
115bdcd8170SKalle Valo };
116bdcd8170SKalle Valo 
117bdcd8170SKalle Valo static struct ieee80211_supported_band ath6kl_band_5ghz = {
118bdcd8170SKalle Valo 	.n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
119bdcd8170SKalle Valo 	.channels = ath6kl_5ghz_a_channels,
120bdcd8170SKalle Valo 	.n_bitrates = ath6kl_a_rates_size,
121bdcd8170SKalle Valo 	.bitrates = ath6kl_a_rates,
122bdcd8170SKalle Valo };
123bdcd8170SKalle Valo 
124837cb97eSJouni Malinen #define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
125837cb97eSJouni Malinen 
126240d2799SVasanthakumar Thiagarajan static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
127bdcd8170SKalle Valo 				  enum nl80211_wpa_versions wpa_version)
128bdcd8170SKalle Valo {
129bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
130bdcd8170SKalle Valo 
131bdcd8170SKalle Valo 	if (!wpa_version) {
1323450334fSVasanthakumar Thiagarajan 		vif->auth_mode = NONE_AUTH;
133bdcd8170SKalle Valo 	} else if (wpa_version & NL80211_WPA_VERSION_2) {
1343450334fSVasanthakumar Thiagarajan 		vif->auth_mode = WPA2_AUTH;
135bdcd8170SKalle Valo 	} else if (wpa_version & NL80211_WPA_VERSION_1) {
1363450334fSVasanthakumar Thiagarajan 		vif->auth_mode = WPA_AUTH;
137bdcd8170SKalle Valo 	} else {
138bdcd8170SKalle Valo 		ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
139bdcd8170SKalle Valo 		return -ENOTSUPP;
140bdcd8170SKalle Valo 	}
141bdcd8170SKalle Valo 
142bdcd8170SKalle Valo 	return 0;
143bdcd8170SKalle Valo }
144bdcd8170SKalle Valo 
145240d2799SVasanthakumar Thiagarajan static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
146bdcd8170SKalle Valo 				enum nl80211_auth_type auth_type)
147bdcd8170SKalle Valo {
148bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
149bdcd8170SKalle Valo 
150bdcd8170SKalle Valo 	switch (auth_type) {
151bdcd8170SKalle Valo 	case NL80211_AUTHTYPE_OPEN_SYSTEM:
1523450334fSVasanthakumar Thiagarajan 		vif->dot11_auth_mode = OPEN_AUTH;
153bdcd8170SKalle Valo 		break;
154bdcd8170SKalle Valo 	case NL80211_AUTHTYPE_SHARED_KEY:
1553450334fSVasanthakumar Thiagarajan 		vif->dot11_auth_mode = SHARED_AUTH;
156bdcd8170SKalle Valo 		break;
157bdcd8170SKalle Valo 	case NL80211_AUTHTYPE_NETWORK_EAP:
1583450334fSVasanthakumar Thiagarajan 		vif->dot11_auth_mode = LEAP_AUTH;
159bdcd8170SKalle Valo 		break;
160bdcd8170SKalle Valo 
161bdcd8170SKalle Valo 	case NL80211_AUTHTYPE_AUTOMATIC:
1623450334fSVasanthakumar Thiagarajan 		vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
163bdcd8170SKalle Valo 		break;
164bdcd8170SKalle Valo 
165bdcd8170SKalle Valo 	default:
166bdcd8170SKalle Valo 		ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type);
167bdcd8170SKalle Valo 		return -ENOTSUPP;
168bdcd8170SKalle Valo 	}
169bdcd8170SKalle Valo 
170bdcd8170SKalle Valo 	return 0;
171bdcd8170SKalle Valo }
172bdcd8170SKalle Valo 
173240d2799SVasanthakumar Thiagarajan static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
174bdcd8170SKalle Valo {
1753450334fSVasanthakumar Thiagarajan 	u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
1763450334fSVasanthakumar Thiagarajan 	u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
1773450334fSVasanthakumar Thiagarajan 		&vif->grp_crypto_len;
178bdcd8170SKalle Valo 
179bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
180bdcd8170SKalle Valo 		   __func__, cipher, ucast);
181bdcd8170SKalle Valo 
182bdcd8170SKalle Valo 	switch (cipher) {
183bdcd8170SKalle Valo 	case 0:
184bdcd8170SKalle Valo 		/* our own hack to use value 0 as no crypto used */
185bdcd8170SKalle Valo 		*ar_cipher = NONE_CRYPT;
186bdcd8170SKalle Valo 		*ar_cipher_len = 0;
187bdcd8170SKalle Valo 		break;
188bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_WEP40:
189bdcd8170SKalle Valo 		*ar_cipher = WEP_CRYPT;
190bdcd8170SKalle Valo 		*ar_cipher_len = 5;
191bdcd8170SKalle Valo 		break;
192bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_WEP104:
193bdcd8170SKalle Valo 		*ar_cipher = WEP_CRYPT;
194bdcd8170SKalle Valo 		*ar_cipher_len = 13;
195bdcd8170SKalle Valo 		break;
196bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_TKIP:
197bdcd8170SKalle Valo 		*ar_cipher = TKIP_CRYPT;
198bdcd8170SKalle Valo 		*ar_cipher_len = 0;
199bdcd8170SKalle Valo 		break;
200bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_CCMP:
201bdcd8170SKalle Valo 		*ar_cipher = AES_CRYPT;
202bdcd8170SKalle Valo 		*ar_cipher_len = 0;
203bdcd8170SKalle Valo 		break;
2045e07021eSDai Shuibing 	case WLAN_CIPHER_SUITE_SMS4:
2055e07021eSDai Shuibing 		*ar_cipher = WAPI_CRYPT;
2065e07021eSDai Shuibing 		*ar_cipher_len = 0;
2075e07021eSDai Shuibing 		break;
208bdcd8170SKalle Valo 	default:
209bdcd8170SKalle Valo 		ath6kl_err("cipher 0x%x not supported\n", cipher);
210bdcd8170SKalle Valo 		return -ENOTSUPP;
211bdcd8170SKalle Valo 	}
212bdcd8170SKalle Valo 
213bdcd8170SKalle Valo 	return 0;
214bdcd8170SKalle Valo }
215bdcd8170SKalle Valo 
216240d2799SVasanthakumar Thiagarajan static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
217bdcd8170SKalle Valo {
218bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
219bdcd8170SKalle Valo 
220bdcd8170SKalle Valo 	if (key_mgmt == WLAN_AKM_SUITE_PSK) {
2213450334fSVasanthakumar Thiagarajan 		if (vif->auth_mode == WPA_AUTH)
2223450334fSVasanthakumar Thiagarajan 			vif->auth_mode = WPA_PSK_AUTH;
2233450334fSVasanthakumar Thiagarajan 		else if (vif->auth_mode == WPA2_AUTH)
2243450334fSVasanthakumar Thiagarajan 			vif->auth_mode = WPA2_PSK_AUTH;
225837cb97eSJouni Malinen 	} else if (key_mgmt == 0x00409600) {
2263450334fSVasanthakumar Thiagarajan 		if (vif->auth_mode == WPA_AUTH)
2273450334fSVasanthakumar Thiagarajan 			vif->auth_mode = WPA_AUTH_CCKM;
2283450334fSVasanthakumar Thiagarajan 		else if (vif->auth_mode == WPA2_AUTH)
2293450334fSVasanthakumar Thiagarajan 			vif->auth_mode = WPA2_AUTH_CCKM;
230bdcd8170SKalle Valo 	} else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
2313450334fSVasanthakumar Thiagarajan 		vif->auth_mode = NONE_AUTH;
232bdcd8170SKalle Valo 	}
233bdcd8170SKalle Valo }
234bdcd8170SKalle Valo 
235990bd915SVasanthakumar Thiagarajan static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
236bdcd8170SKalle Valo {
237990bd915SVasanthakumar Thiagarajan 	struct ath6kl *ar = vif->ar;
23859c98449SVasanthakumar Thiagarajan 
239bdcd8170SKalle Valo 	if (!test_bit(WMI_READY, &ar->flag)) {
240bdcd8170SKalle Valo 		ath6kl_err("wmi is not ready\n");
241bdcd8170SKalle Valo 		return false;
242bdcd8170SKalle Valo 	}
243bdcd8170SKalle Valo 
24459c98449SVasanthakumar Thiagarajan 	if (!test_bit(WLAN_ENABLED, &vif->flags)) {
245bdcd8170SKalle Valo 		ath6kl_err("wlan disabled\n");
246bdcd8170SKalle Valo 		return false;
247bdcd8170SKalle Valo 	}
248bdcd8170SKalle Valo 
249bdcd8170SKalle Valo 	return true;
250bdcd8170SKalle Valo }
251bdcd8170SKalle Valo 
2526981ffdcSKevin Fang static bool ath6kl_is_wpa_ie(const u8 *pos)
2536981ffdcSKevin Fang {
2546981ffdcSKevin Fang 	return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
2556981ffdcSKevin Fang 		pos[2] == 0x00 && pos[3] == 0x50 &&
2566981ffdcSKevin Fang 		pos[4] == 0xf2 && pos[5] == 0x01;
2576981ffdcSKevin Fang }
2586981ffdcSKevin Fang 
2596981ffdcSKevin Fang static bool ath6kl_is_rsn_ie(const u8 *pos)
2606981ffdcSKevin Fang {
2616981ffdcSKevin Fang 	return pos[0] == WLAN_EID_RSN;
2626981ffdcSKevin Fang }
2636981ffdcSKevin Fang 
26463541212SAarthi Thiruvengadam static bool ath6kl_is_wps_ie(const u8 *pos)
26563541212SAarthi Thiruvengadam {
26663541212SAarthi Thiruvengadam 	return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
26763541212SAarthi Thiruvengadam 		pos[1] >= 4 &&
26863541212SAarthi Thiruvengadam 		pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
26963541212SAarthi Thiruvengadam 		pos[5] == 0x04);
27063541212SAarthi Thiruvengadam }
27163541212SAarthi Thiruvengadam 
272334234b5SVasanthakumar Thiagarajan static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
2736981ffdcSKevin Fang 				    size_t ies_len)
2746981ffdcSKevin Fang {
275334234b5SVasanthakumar Thiagarajan 	struct ath6kl *ar = vif->ar;
2766981ffdcSKevin Fang 	const u8 *pos;
2776981ffdcSKevin Fang 	u8 *buf = NULL;
2786981ffdcSKevin Fang 	size_t len = 0;
2796981ffdcSKevin Fang 	int ret;
2806981ffdcSKevin Fang 
2816981ffdcSKevin Fang 	/*
28263541212SAarthi Thiruvengadam 	 * Clear previously set flag
28363541212SAarthi Thiruvengadam 	 */
28463541212SAarthi Thiruvengadam 
28563541212SAarthi Thiruvengadam 	ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
28663541212SAarthi Thiruvengadam 
28763541212SAarthi Thiruvengadam 	/*
2886981ffdcSKevin Fang 	 * Filter out RSN/WPA IE(s)
2896981ffdcSKevin Fang 	 */
2906981ffdcSKevin Fang 
2916981ffdcSKevin Fang 	if (ies && ies_len) {
2926981ffdcSKevin Fang 		buf = kmalloc(ies_len, GFP_KERNEL);
2936981ffdcSKevin Fang 		if (buf == NULL)
2946981ffdcSKevin Fang 			return -ENOMEM;
2956981ffdcSKevin Fang 		pos = ies;
2966981ffdcSKevin Fang 
2976981ffdcSKevin Fang 		while (pos + 1 < ies + ies_len) {
2986981ffdcSKevin Fang 			if (pos + 2 + pos[1] > ies + ies_len)
2996981ffdcSKevin Fang 				break;
3006981ffdcSKevin Fang 			if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
3016981ffdcSKevin Fang 				memcpy(buf + len, pos, 2 + pos[1]);
3026981ffdcSKevin Fang 				len += 2 + pos[1];
3036981ffdcSKevin Fang 			}
30463541212SAarthi Thiruvengadam 
30563541212SAarthi Thiruvengadam 			if (ath6kl_is_wps_ie(pos))
30663541212SAarthi Thiruvengadam 				ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
30763541212SAarthi Thiruvengadam 
3086981ffdcSKevin Fang 			pos += 2 + pos[1];
3096981ffdcSKevin Fang 		}
3106981ffdcSKevin Fang 	}
3116981ffdcSKevin Fang 
312334234b5SVasanthakumar Thiagarajan 	ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
313334234b5SVasanthakumar Thiagarajan 				       WMI_FRAME_ASSOC_REQ, buf, len);
3146981ffdcSKevin Fang 	kfree(buf);
3156981ffdcSKevin Fang 	return ret;
3166981ffdcSKevin Fang }
3176981ffdcSKevin Fang 
31855055976SVasanthakumar Thiagarajan static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
31955055976SVasanthakumar Thiagarajan {
32055055976SVasanthakumar Thiagarajan 	switch (type) {
32155055976SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_STATION:
32255055976SVasanthakumar Thiagarajan 		*nw_type = INFRA_NETWORK;
32355055976SVasanthakumar Thiagarajan 		break;
32455055976SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_ADHOC:
32555055976SVasanthakumar Thiagarajan 		*nw_type = ADHOC_NETWORK;
32655055976SVasanthakumar Thiagarajan 		break;
32755055976SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_AP:
32855055976SVasanthakumar Thiagarajan 		*nw_type = AP_NETWORK;
32955055976SVasanthakumar Thiagarajan 		break;
33055055976SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_P2P_CLIENT:
33155055976SVasanthakumar Thiagarajan 		*nw_type = INFRA_NETWORK;
33255055976SVasanthakumar Thiagarajan 		break;
33355055976SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_P2P_GO:
33455055976SVasanthakumar Thiagarajan 		*nw_type = AP_NETWORK;
33555055976SVasanthakumar Thiagarajan 		break;
33655055976SVasanthakumar Thiagarajan 	default:
33755055976SVasanthakumar Thiagarajan 		ath6kl_err("invalid interface type %u\n", type);
33855055976SVasanthakumar Thiagarajan 		return -ENOTSUPP;
33955055976SVasanthakumar Thiagarajan 	}
34055055976SVasanthakumar Thiagarajan 
34155055976SVasanthakumar Thiagarajan 	return 0;
34255055976SVasanthakumar Thiagarajan }
34355055976SVasanthakumar Thiagarajan 
34455055976SVasanthakumar Thiagarajan static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
34555055976SVasanthakumar Thiagarajan 				   u8 *if_idx, u8 *nw_type)
34655055976SVasanthakumar Thiagarajan {
34755055976SVasanthakumar Thiagarajan 	int i;
34855055976SVasanthakumar Thiagarajan 
34955055976SVasanthakumar Thiagarajan 	if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
35055055976SVasanthakumar Thiagarajan 		return false;
35155055976SVasanthakumar Thiagarajan 
35255055976SVasanthakumar Thiagarajan 	if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
35355055976SVasanthakumar Thiagarajan 	    ar->num_vif))
35455055976SVasanthakumar Thiagarajan 		return false;
35555055976SVasanthakumar Thiagarajan 
35655055976SVasanthakumar Thiagarajan 	if (type == NL80211_IFTYPE_STATION ||
35755055976SVasanthakumar Thiagarajan 	    type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
35871f96ee6SKalle Valo 		for (i = 0; i < ar->vif_max; i++) {
35955055976SVasanthakumar Thiagarajan 			if ((ar->avail_idx_map >> i) & BIT(0)) {
36055055976SVasanthakumar Thiagarajan 				*if_idx = i;
36155055976SVasanthakumar Thiagarajan 				return true;
36255055976SVasanthakumar Thiagarajan 			}
36355055976SVasanthakumar Thiagarajan 		}
36455055976SVasanthakumar Thiagarajan 	}
36555055976SVasanthakumar Thiagarajan 
3663226f68aSVasanthakumar Thiagarajan 	if (type == NL80211_IFTYPE_P2P_CLIENT ||
3673226f68aSVasanthakumar Thiagarajan 	    type == NL80211_IFTYPE_P2P_GO) {
36871f96ee6SKalle Valo 		for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
3693226f68aSVasanthakumar Thiagarajan 			if ((ar->avail_idx_map >> i) & BIT(0)) {
3703226f68aSVasanthakumar Thiagarajan 				*if_idx = i;
3713226f68aSVasanthakumar Thiagarajan 				return true;
3723226f68aSVasanthakumar Thiagarajan 			}
3733226f68aSVasanthakumar Thiagarajan 		}
3743226f68aSVasanthakumar Thiagarajan 	}
3753226f68aSVasanthakumar Thiagarajan 
37655055976SVasanthakumar Thiagarajan 	return false;
37755055976SVasanthakumar Thiagarajan }
37855055976SVasanthakumar Thiagarajan 
379bdcd8170SKalle Valo static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
380bdcd8170SKalle Valo 				   struct cfg80211_connect_params *sme)
381bdcd8170SKalle Valo {
382bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(dev);
38359c98449SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
384bdcd8170SKalle Valo 	int status;
385bdcd8170SKalle Valo 
38614ee6f6bSVasanthakumar Thiagarajan 	vif->sme_state = SME_CONNECTING;
387bdcd8170SKalle Valo 
388990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
389bdcd8170SKalle Valo 		return -EIO;
390bdcd8170SKalle Valo 
391bdcd8170SKalle Valo 	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
392bdcd8170SKalle Valo 		ath6kl_err("destroy in progress\n");
393bdcd8170SKalle Valo 		return -EBUSY;
394bdcd8170SKalle Valo 	}
395bdcd8170SKalle Valo 
396bdcd8170SKalle Valo 	if (test_bit(SKIP_SCAN, &ar->flag) &&
397bdcd8170SKalle Valo 	    ((sme->channel && sme->channel->center_freq == 0) ||
398bdcd8170SKalle Valo 	     (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
399bdcd8170SKalle Valo 		ath6kl_err("SkipScan: channel or bssid invalid\n");
400bdcd8170SKalle Valo 		return -EINVAL;
401bdcd8170SKalle Valo 	}
402bdcd8170SKalle Valo 
403bdcd8170SKalle Valo 	if (down_interruptible(&ar->sem)) {
404bdcd8170SKalle Valo 		ath6kl_err("busy, couldn't get access\n");
405bdcd8170SKalle Valo 		return -ERESTARTSYS;
406bdcd8170SKalle Valo 	}
407bdcd8170SKalle Valo 
408bdcd8170SKalle Valo 	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
409bdcd8170SKalle Valo 		ath6kl_err("busy, destroy in progress\n");
410bdcd8170SKalle Valo 		up(&ar->sem);
411bdcd8170SKalle Valo 		return -EBUSY;
412bdcd8170SKalle Valo 	}
413bdcd8170SKalle Valo 
414bdcd8170SKalle Valo 	if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
415bdcd8170SKalle Valo 		/*
416bdcd8170SKalle Valo 		 * sleep until the command queue drains
417bdcd8170SKalle Valo 		 */
418bdcd8170SKalle Valo 		wait_event_interruptible_timeout(ar->event_wq,
419bdcd8170SKalle Valo 			ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
420bdcd8170SKalle Valo 			WMI_TIMEOUT);
421bdcd8170SKalle Valo 		if (signal_pending(current)) {
422bdcd8170SKalle Valo 			ath6kl_err("cmd queue drain timeout\n");
423bdcd8170SKalle Valo 			up(&ar->sem);
424bdcd8170SKalle Valo 			return -EINTR;
425bdcd8170SKalle Valo 		}
426bdcd8170SKalle Valo 	}
427bdcd8170SKalle Valo 
4286981ffdcSKevin Fang 	if (sme->ie && (sme->ie_len > 0)) {
429334234b5SVasanthakumar Thiagarajan 		status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
430743b4518SDan Carpenter 		if (status) {
431743b4518SDan Carpenter 			up(&ar->sem);
4326981ffdcSKevin Fang 			return status;
433743b4518SDan Carpenter 		}
434542c519aSRaja Mani 	} else
435542c519aSRaja Mani 		ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
4366981ffdcSKevin Fang 
43759c98449SVasanthakumar Thiagarajan 	if (test_bit(CONNECTED, &vif->flags) &&
4383450334fSVasanthakumar Thiagarajan 	    vif->ssid_len == sme->ssid_len &&
4393450334fSVasanthakumar Thiagarajan 	    !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
440cf5333d7SVasanthakumar Thiagarajan 		vif->reconnect_flag = true;
441334234b5SVasanthakumar Thiagarajan 		status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
442334234b5SVasanthakumar Thiagarajan 						  vif->req_bssid,
443f74bac54SVasanthakumar Thiagarajan 						  vif->ch_hint);
444bdcd8170SKalle Valo 
445bdcd8170SKalle Valo 		up(&ar->sem);
446bdcd8170SKalle Valo 		if (status) {
447bdcd8170SKalle Valo 			ath6kl_err("wmi_reconnect_cmd failed\n");
448bdcd8170SKalle Valo 			return -EIO;
449bdcd8170SKalle Valo 		}
450bdcd8170SKalle Valo 		return 0;
4513450334fSVasanthakumar Thiagarajan 	} else if (vif->ssid_len == sme->ssid_len &&
4523450334fSVasanthakumar Thiagarajan 		   !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
453240d2799SVasanthakumar Thiagarajan 		ath6kl_disconnect(vif);
454bdcd8170SKalle Valo 	}
455bdcd8170SKalle Valo 
4563450334fSVasanthakumar Thiagarajan 	memset(vif->ssid, 0, sizeof(vif->ssid));
4573450334fSVasanthakumar Thiagarajan 	vif->ssid_len = sme->ssid_len;
4583450334fSVasanthakumar Thiagarajan 	memcpy(vif->ssid, sme->ssid, sme->ssid_len);
459bdcd8170SKalle Valo 
460bdcd8170SKalle Valo 	if (sme->channel)
461f74bac54SVasanthakumar Thiagarajan 		vif->ch_hint = sme->channel->center_freq;
462bdcd8170SKalle Valo 
4638c8b65e3SVasanthakumar Thiagarajan 	memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
464bdcd8170SKalle Valo 	if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
4658c8b65e3SVasanthakumar Thiagarajan 		memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
466bdcd8170SKalle Valo 
467240d2799SVasanthakumar Thiagarajan 	ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
468bdcd8170SKalle Valo 
469240d2799SVasanthakumar Thiagarajan 	status = ath6kl_set_auth_type(vif, sme->auth_type);
470bdcd8170SKalle Valo 	if (status) {
471bdcd8170SKalle Valo 		up(&ar->sem);
472bdcd8170SKalle Valo 		return status;
473bdcd8170SKalle Valo 	}
474bdcd8170SKalle Valo 
475bdcd8170SKalle Valo 	if (sme->crypto.n_ciphers_pairwise)
476240d2799SVasanthakumar Thiagarajan 		ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
477bdcd8170SKalle Valo 	else
478240d2799SVasanthakumar Thiagarajan 		ath6kl_set_cipher(vif, 0, true);
479bdcd8170SKalle Valo 
480240d2799SVasanthakumar Thiagarajan 	ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
481bdcd8170SKalle Valo 
482bdcd8170SKalle Valo 	if (sme->crypto.n_akm_suites)
483240d2799SVasanthakumar Thiagarajan 		ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
484bdcd8170SKalle Valo 
485bdcd8170SKalle Valo 	if ((sme->key_len) &&
4863450334fSVasanthakumar Thiagarajan 	    (vif->auth_mode == NONE_AUTH) &&
4873450334fSVasanthakumar Thiagarajan 	    (vif->prwise_crypto == WEP_CRYPT)) {
488bdcd8170SKalle Valo 		struct ath6kl_key *key = NULL;
489bdcd8170SKalle Valo 
490bdcd8170SKalle Valo 		if (sme->key_idx < WMI_MIN_KEY_INDEX ||
491bdcd8170SKalle Valo 		    sme->key_idx > WMI_MAX_KEY_INDEX) {
492bdcd8170SKalle Valo 			ath6kl_err("key index %d out of bounds\n",
493bdcd8170SKalle Valo 				   sme->key_idx);
494bdcd8170SKalle Valo 			up(&ar->sem);
495bdcd8170SKalle Valo 			return -ENOENT;
496bdcd8170SKalle Valo 		}
497bdcd8170SKalle Valo 
4986f2a73f9SVasanthakumar Thiagarajan 		key = &vif->keys[sme->key_idx];
499bdcd8170SKalle Valo 		key->key_len = sme->key_len;
500bdcd8170SKalle Valo 		memcpy(key->key, sme->key, key->key_len);
5013450334fSVasanthakumar Thiagarajan 		key->cipher = vif->prwise_crypto;
5023450334fSVasanthakumar Thiagarajan 		vif->def_txkey_index = sme->key_idx;
503bdcd8170SKalle Valo 
504334234b5SVasanthakumar Thiagarajan 		ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
5053450334fSVasanthakumar Thiagarajan 				      vif->prwise_crypto,
506bdcd8170SKalle Valo 				      GROUP_USAGE | TX_USAGE,
507bdcd8170SKalle Valo 				      key->key_len,
508f4bb9a6fSJouni Malinen 				      NULL, 0,
509bdcd8170SKalle Valo 				      key->key, KEY_OP_INIT_VAL, NULL,
510bdcd8170SKalle Valo 				      NO_SYNC_WMIFLAG);
511bdcd8170SKalle Valo 	}
512bdcd8170SKalle Valo 
513bdcd8170SKalle Valo 	if (!ar->usr_bss_filter) {
51459c98449SVasanthakumar Thiagarajan 		clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
515240d2799SVasanthakumar Thiagarajan 		if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
516240d2799SVasanthakumar Thiagarajan 		    ALL_BSS_FILTER, 0) != 0) {
517bdcd8170SKalle Valo 			ath6kl_err("couldn't set bss filtering\n");
518bdcd8170SKalle Valo 			up(&ar->sem);
519bdcd8170SKalle Valo 			return -EIO;
520bdcd8170SKalle Valo 		}
521bdcd8170SKalle Valo 	}
522bdcd8170SKalle Valo 
523f5938f24SVasanthakumar Thiagarajan 	vif->nw_type = vif->next_mode;
524bdcd8170SKalle Valo 
525bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
526bdcd8170SKalle Valo 		   "%s: connect called with authmode %d dot11 auth %d"
527bdcd8170SKalle Valo 		   " PW crypto %d PW crypto len %d GRP crypto %d"
528bdcd8170SKalle Valo 		   " GRP crypto len %d channel hint %u\n",
529bdcd8170SKalle Valo 		   __func__,
5303450334fSVasanthakumar Thiagarajan 		   vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
5313450334fSVasanthakumar Thiagarajan 		   vif->prwise_crypto_len, vif->grp_crypto,
532f74bac54SVasanthakumar Thiagarajan 		   vif->grp_crypto_len, vif->ch_hint);
533bdcd8170SKalle Valo 
534cf5333d7SVasanthakumar Thiagarajan 	vif->reconnect_flag = 0;
535334234b5SVasanthakumar Thiagarajan 	status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
5363450334fSVasanthakumar Thiagarajan 					vif->dot11_auth_mode, vif->auth_mode,
5373450334fSVasanthakumar Thiagarajan 					vif->prwise_crypto,
5383450334fSVasanthakumar Thiagarajan 					vif->prwise_crypto_len,
5393450334fSVasanthakumar Thiagarajan 					vif->grp_crypto, vif->grp_crypto_len,
5403450334fSVasanthakumar Thiagarajan 					vif->ssid_len, vif->ssid,
541f74bac54SVasanthakumar Thiagarajan 					vif->req_bssid, vif->ch_hint,
542bdcd8170SKalle Valo 					ar->connect_ctrl_flags);
543bdcd8170SKalle Valo 
544bdcd8170SKalle Valo 	up(&ar->sem);
545bdcd8170SKalle Valo 
546bdcd8170SKalle Valo 	if (status == -EINVAL) {
5473450334fSVasanthakumar Thiagarajan 		memset(vif->ssid, 0, sizeof(vif->ssid));
5483450334fSVasanthakumar Thiagarajan 		vif->ssid_len = 0;
549bdcd8170SKalle Valo 		ath6kl_err("invalid request\n");
550bdcd8170SKalle Valo 		return -ENOENT;
551bdcd8170SKalle Valo 	} else if (status) {
552bdcd8170SKalle Valo 		ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
553bdcd8170SKalle Valo 		return -EIO;
554bdcd8170SKalle Valo 	}
555bdcd8170SKalle Valo 
556bdcd8170SKalle Valo 	if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
5573450334fSVasanthakumar Thiagarajan 	    ((vif->auth_mode == WPA_PSK_AUTH)
5583450334fSVasanthakumar Thiagarajan 	     || (vif->auth_mode == WPA2_PSK_AUTH))) {
559de3ad713SVasanthakumar Thiagarajan 		mod_timer(&vif->disconnect_timer,
560bdcd8170SKalle Valo 			  jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
561bdcd8170SKalle Valo 	}
562bdcd8170SKalle Valo 
563bdcd8170SKalle Valo 	ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
56459c98449SVasanthakumar Thiagarajan 	set_bit(CONNECT_PEND, &vif->flags);
565bdcd8170SKalle Valo 
566bdcd8170SKalle Valo 	return 0;
567bdcd8170SKalle Valo }
568bdcd8170SKalle Valo 
5694eab6f4fSRaja Mani static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
5704eab6f4fSRaja Mani 				    enum network_type nw_type,
5714eab6f4fSRaja Mani 				    const u8 *bssid,
57201cac476SJouni Malinen 				    struct ieee80211_channel *chan,
57301cac476SJouni Malinen 				    const u8 *beacon_ie, size_t beacon_ie_len)
57401cac476SJouni Malinen {
575240d2799SVasanthakumar Thiagarajan 	struct ath6kl *ar = vif->ar;
57601cac476SJouni Malinen 	struct cfg80211_bss *bss;
5774eab6f4fSRaja Mani 	u16 cap_mask, cap_val;
57801cac476SJouni Malinen 	u8 *ie;
57901cac476SJouni Malinen 
5804eab6f4fSRaja Mani 	if (nw_type & ADHOC_NETWORK) {
5814eab6f4fSRaja Mani 		cap_mask = WLAN_CAPABILITY_IBSS;
5824eab6f4fSRaja Mani 		cap_val = WLAN_CAPABILITY_IBSS;
5834eab6f4fSRaja Mani 	} else {
5844eab6f4fSRaja Mani 		cap_mask = WLAN_CAPABILITY_ESS;
5854eab6f4fSRaja Mani 		cap_val = WLAN_CAPABILITY_ESS;
5864eab6f4fSRaja Mani 	}
5874eab6f4fSRaja Mani 
588be98e3a4SVasanthakumar Thiagarajan 	bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
5894eab6f4fSRaja Mani 			       vif->ssid, vif->ssid_len,
5904eab6f4fSRaja Mani 			       cap_mask, cap_val);
59101cac476SJouni Malinen 	if (bss == NULL) {
59201cac476SJouni Malinen 		/*
59301cac476SJouni Malinen 		 * Since cfg80211 may not yet know about the BSS,
59401cac476SJouni Malinen 		 * generate a partial entry until the first BSS info
59501cac476SJouni Malinen 		 * event becomes available.
59601cac476SJouni Malinen 		 *
59701cac476SJouni Malinen 		 * Prepend SSID element since it is not included in the Beacon
59801cac476SJouni Malinen 		 * IEs from the target.
59901cac476SJouni Malinen 		 */
6003450334fSVasanthakumar Thiagarajan 		ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
60101cac476SJouni Malinen 		if (ie == NULL)
60201cac476SJouni Malinen 			return -ENOMEM;
60301cac476SJouni Malinen 		ie[0] = WLAN_EID_SSID;
6043450334fSVasanthakumar Thiagarajan 		ie[1] = vif->ssid_len;
6053450334fSVasanthakumar Thiagarajan 		memcpy(ie + 2, vif->ssid, vif->ssid_len);
6063450334fSVasanthakumar Thiagarajan 		memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
607be98e3a4SVasanthakumar Thiagarajan 		bss = cfg80211_inform_bss(ar->wiphy, chan,
6084eab6f4fSRaja Mani 					  bssid, 0, cap_val, 100,
6093450334fSVasanthakumar Thiagarajan 					  ie, 2 + vif->ssid_len + beacon_ie_len,
61001cac476SJouni Malinen 					  0, GFP_KERNEL);
61101cac476SJouni Malinen 		if (bss)
6124eab6f4fSRaja Mani 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
6134eab6f4fSRaja Mani 				   "cfg80211\n", bssid);
61401cac476SJouni Malinen 		kfree(ie);
61501cac476SJouni Malinen 	} else
61601cac476SJouni Malinen 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
61701cac476SJouni Malinen 			   "entry\n");
61801cac476SJouni Malinen 
61901cac476SJouni Malinen 	if (bss == NULL)
62001cac476SJouni Malinen 		return -ENOMEM;
62101cac476SJouni Malinen 
62201cac476SJouni Malinen 	cfg80211_put_bss(bss);
62301cac476SJouni Malinen 
62401cac476SJouni Malinen 	return 0;
62501cac476SJouni Malinen }
62601cac476SJouni Malinen 
627240d2799SVasanthakumar Thiagarajan void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
628bdcd8170SKalle Valo 				   u8 *bssid, u16 listen_intvl,
629bdcd8170SKalle Valo 				   u16 beacon_intvl,
630bdcd8170SKalle Valo 				   enum network_type nw_type,
631bdcd8170SKalle Valo 				   u8 beacon_ie_len, u8 assoc_req_len,
632bdcd8170SKalle Valo 				   u8 assoc_resp_len, u8 *assoc_info)
633bdcd8170SKalle Valo {
63401cac476SJouni Malinen 	struct ieee80211_channel *chan;
635240d2799SVasanthakumar Thiagarajan 	struct ath6kl *ar = vif->ar;
636bdcd8170SKalle Valo 
637bdcd8170SKalle Valo 	/* capinfo + listen interval */
638bdcd8170SKalle Valo 	u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
639bdcd8170SKalle Valo 
640bdcd8170SKalle Valo 	/* capinfo + status code +  associd */
641bdcd8170SKalle Valo 	u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
642bdcd8170SKalle Valo 
643bdcd8170SKalle Valo 	u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
644bdcd8170SKalle Valo 	u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
645bdcd8170SKalle Valo 	    assoc_resp_ie_offset;
646bdcd8170SKalle Valo 
647bdcd8170SKalle Valo 	assoc_req_len -= assoc_req_ie_offset;
648bdcd8170SKalle Valo 	assoc_resp_len -= assoc_resp_ie_offset;
649bdcd8170SKalle Valo 
65032c10874SJouni Malinen 	/*
65132c10874SJouni Malinen 	 * Store Beacon interval here; DTIM period will be available only once
65232c10874SJouni Malinen 	 * a Beacon frame from the AP is seen.
65332c10874SJouni Malinen 	 */
654cf5333d7SVasanthakumar Thiagarajan 	vif->assoc_bss_beacon_int = beacon_intvl;
65559c98449SVasanthakumar Thiagarajan 	clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
65632c10874SJouni Malinen 
657bdcd8170SKalle Valo 	if (nw_type & ADHOC_NETWORK) {
658551959d8SVasanthakumar Thiagarajan 		if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
659bdcd8170SKalle Valo 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
660bdcd8170SKalle Valo 				   "%s: ath6k not in ibss mode\n", __func__);
661bdcd8170SKalle Valo 			return;
662bdcd8170SKalle Valo 		}
663bdcd8170SKalle Valo 	}
664bdcd8170SKalle Valo 
665bdcd8170SKalle Valo 	if (nw_type & INFRA_NETWORK) {
666551959d8SVasanthakumar Thiagarajan 		if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
667551959d8SVasanthakumar Thiagarajan 		    vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
668bdcd8170SKalle Valo 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
669bdcd8170SKalle Valo 				   "%s: ath6k not in station mode\n", __func__);
670bdcd8170SKalle Valo 			return;
671bdcd8170SKalle Valo 		}
672bdcd8170SKalle Valo 	}
673bdcd8170SKalle Valo 
674be98e3a4SVasanthakumar Thiagarajan 	chan = ieee80211_get_channel(ar->wiphy, (int) channel);
675bdcd8170SKalle Valo 
6764eab6f4fSRaja Mani 	if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info,
6774eab6f4fSRaja Mani 				     beacon_ie_len) < 0) {
6784eab6f4fSRaja Mani 		ath6kl_err("could not add cfg80211 bss entry\n");
679bdcd8170SKalle Valo 		return;
680bdcd8170SKalle Valo 	}
681bdcd8170SKalle Valo 
6824eab6f4fSRaja Mani 	if (nw_type & ADHOC_NETWORK) {
6834eab6f4fSRaja Mani 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
6844eab6f4fSRaja Mani 			   nw_type & ADHOC_CREATOR ? "creator" : "joiner");
6854eab6f4fSRaja Mani 		cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
68601cac476SJouni Malinen 		return;
68701cac476SJouni Malinen 	}
68801cac476SJouni Malinen 
68914ee6f6bSVasanthakumar Thiagarajan 	if (vif->sme_state == SME_CONNECTING) {
690bdcd8170SKalle Valo 		/* inform connect result to cfg80211 */
69114ee6f6bSVasanthakumar Thiagarajan 		vif->sme_state = SME_CONNECTED;
692240d2799SVasanthakumar Thiagarajan 		cfg80211_connect_result(vif->ndev, bssid,
693bdcd8170SKalle Valo 					assoc_req_ie, assoc_req_len,
694bdcd8170SKalle Valo 					assoc_resp_ie, assoc_resp_len,
695bdcd8170SKalle Valo 					WLAN_STATUS_SUCCESS, GFP_KERNEL);
69614ee6f6bSVasanthakumar Thiagarajan 	} else if (vif->sme_state == SME_CONNECTED) {
697bdcd8170SKalle Valo 		/* inform roam event to cfg80211 */
698240d2799SVasanthakumar Thiagarajan 		cfg80211_roamed(vif->ndev, chan, bssid,
699bdcd8170SKalle Valo 				assoc_req_ie, assoc_req_len,
700bdcd8170SKalle Valo 				assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
701bdcd8170SKalle Valo 	}
702bdcd8170SKalle Valo }
703bdcd8170SKalle Valo 
704bdcd8170SKalle Valo static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
705bdcd8170SKalle Valo 				      struct net_device *dev, u16 reason_code)
706bdcd8170SKalle Valo {
707bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
7083450334fSVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
709bdcd8170SKalle Valo 
710bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
711bdcd8170SKalle Valo 		   reason_code);
712bdcd8170SKalle Valo 
713990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
714bdcd8170SKalle Valo 		return -EIO;
715bdcd8170SKalle Valo 
716bdcd8170SKalle Valo 	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
717bdcd8170SKalle Valo 		ath6kl_err("busy, destroy in progress\n");
718bdcd8170SKalle Valo 		return -EBUSY;
719bdcd8170SKalle Valo 	}
720bdcd8170SKalle Valo 
721bdcd8170SKalle Valo 	if (down_interruptible(&ar->sem)) {
722bdcd8170SKalle Valo 		ath6kl_err("busy, couldn't get access\n");
723bdcd8170SKalle Valo 		return -ERESTARTSYS;
724bdcd8170SKalle Valo 	}
725bdcd8170SKalle Valo 
726cf5333d7SVasanthakumar Thiagarajan 	vif->reconnect_flag = 0;
727240d2799SVasanthakumar Thiagarajan 	ath6kl_disconnect(vif);
7283450334fSVasanthakumar Thiagarajan 	memset(vif->ssid, 0, sizeof(vif->ssid));
7293450334fSVasanthakumar Thiagarajan 	vif->ssid_len = 0;
730bdcd8170SKalle Valo 
731bdcd8170SKalle Valo 	if (!test_bit(SKIP_SCAN, &ar->flag))
7328c8b65e3SVasanthakumar Thiagarajan 		memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
733bdcd8170SKalle Valo 
734bdcd8170SKalle Valo 	up(&ar->sem);
735bdcd8170SKalle Valo 
73614ee6f6bSVasanthakumar Thiagarajan 	vif->sme_state = SME_DISCONNECTED;
737170826ddSVasanthakumar Thiagarajan 
738bdcd8170SKalle Valo 	return 0;
739bdcd8170SKalle Valo }
740bdcd8170SKalle Valo 
741240d2799SVasanthakumar Thiagarajan void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
742bdcd8170SKalle Valo 				      u8 *bssid, u8 assoc_resp_len,
743bdcd8170SKalle Valo 				      u8 *assoc_info, u16 proto_reason)
744bdcd8170SKalle Valo {
745240d2799SVasanthakumar Thiagarajan 	struct ath6kl *ar = vif->ar;
74659c98449SVasanthakumar Thiagarajan 
74714ee6f6bSVasanthakumar Thiagarajan 	if (vif->scan_req) {
74814ee6f6bSVasanthakumar Thiagarajan 		cfg80211_scan_done(vif->scan_req, true);
74914ee6f6bSVasanthakumar Thiagarajan 		vif->scan_req = NULL;
750bdcd8170SKalle Valo 	}
751bdcd8170SKalle Valo 
752f5938f24SVasanthakumar Thiagarajan 	if (vif->nw_type & ADHOC_NETWORK) {
753551959d8SVasanthakumar Thiagarajan 		if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
754bdcd8170SKalle Valo 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
755bdcd8170SKalle Valo 				   "%s: ath6k not in ibss mode\n", __func__);
756bdcd8170SKalle Valo 			return;
757bdcd8170SKalle Valo 		}
758bdcd8170SKalle Valo 		memset(bssid, 0, ETH_ALEN);
759240d2799SVasanthakumar Thiagarajan 		cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
760bdcd8170SKalle Valo 		return;
761bdcd8170SKalle Valo 	}
762bdcd8170SKalle Valo 
763f5938f24SVasanthakumar Thiagarajan 	if (vif->nw_type & INFRA_NETWORK) {
764551959d8SVasanthakumar Thiagarajan 		if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
765551959d8SVasanthakumar Thiagarajan 		    vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
766bdcd8170SKalle Valo 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
767bdcd8170SKalle Valo 				   "%s: ath6k not in station mode\n", __func__);
768bdcd8170SKalle Valo 			return;
769bdcd8170SKalle Valo 		}
770bdcd8170SKalle Valo 	}
771bdcd8170SKalle Valo 
7721de547d6SVasanthakumar Thiagarajan 	/*
7731de547d6SVasanthakumar Thiagarajan 	 * Send a disconnect command to target when a disconnect event is
7741de547d6SVasanthakumar Thiagarajan 	 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
7751de547d6SVasanthakumar Thiagarajan 	 * request from host) to make the firmware stop trying to connect even
7761de547d6SVasanthakumar Thiagarajan 	 * after giving disconnect event. There will be one more disconnect
7771de547d6SVasanthakumar Thiagarajan 	 * event for this disconnect command with reason code DISCONNECT_CMD
7781de547d6SVasanthakumar Thiagarajan 	 * which will be notified to cfg80211.
7791de547d6SVasanthakumar Thiagarajan 	 */
780bdcd8170SKalle Valo 
7811de547d6SVasanthakumar Thiagarajan 	if (reason != DISCONNECT_CMD) {
782334234b5SVasanthakumar Thiagarajan 		ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
783bdcd8170SKalle Valo 		return;
784bdcd8170SKalle Valo 	}
785bdcd8170SKalle Valo 
78659c98449SVasanthakumar Thiagarajan 	clear_bit(CONNECT_PEND, &vif->flags);
787bdcd8170SKalle Valo 
78814ee6f6bSVasanthakumar Thiagarajan 	if (vif->sme_state == SME_CONNECTING) {
789240d2799SVasanthakumar Thiagarajan 		cfg80211_connect_result(vif->ndev,
790bdcd8170SKalle Valo 				bssid, NULL, 0,
791bdcd8170SKalle Valo 				NULL, 0,
792bdcd8170SKalle Valo 				WLAN_STATUS_UNSPECIFIED_FAILURE,
793bdcd8170SKalle Valo 				GFP_KERNEL);
79414ee6f6bSVasanthakumar Thiagarajan 	} else if (vif->sme_state == SME_CONNECTED) {
795240d2799SVasanthakumar Thiagarajan 		cfg80211_disconnected(vif->ndev, reason,
796bdcd8170SKalle Valo 				NULL, 0, GFP_KERNEL);
797bdcd8170SKalle Valo 	}
798bdcd8170SKalle Valo 
79914ee6f6bSVasanthakumar Thiagarajan 	vif->sme_state = SME_DISCONNECTED;
800bdcd8170SKalle Valo }
801bdcd8170SKalle Valo 
802bdcd8170SKalle Valo static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
803bdcd8170SKalle Valo 				struct cfg80211_scan_request *request)
804bdcd8170SKalle Valo {
805bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
80659c98449SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(ndev);
8071276c9efSEdward Lu 	s8 n_channels = 0;
8081276c9efSEdward Lu 	u16 *channels = NULL;
809bdcd8170SKalle Valo 	int ret = 0;
810f1f92179SVasanthakumar Thiagarajan 	u32 force_fg_scan = 0;
811bdcd8170SKalle Valo 
812990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
813bdcd8170SKalle Valo 		return -EIO;
814bdcd8170SKalle Valo 
815bdcd8170SKalle Valo 	if (!ar->usr_bss_filter) {
81659c98449SVasanthakumar Thiagarajan 		clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
8171b1e6ee3SJouni Malinen 		ret = ath6kl_wmi_bssfilter_cmd(
818240d2799SVasanthakumar Thiagarajan 			ar->wmi, vif->fw_vif_idx,
81959c98449SVasanthakumar Thiagarajan 			(test_bit(CONNECTED, &vif->flags) ?
8201b1e6ee3SJouni Malinen 			 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
8211b1e6ee3SJouni Malinen 		if (ret) {
822bdcd8170SKalle Valo 			ath6kl_err("couldn't set bss filtering\n");
8231b1e6ee3SJouni Malinen 			return ret;
824bdcd8170SKalle Valo 		}
825bdcd8170SKalle Valo 	}
826bdcd8170SKalle Valo 
827bdcd8170SKalle Valo 	if (request->n_ssids && request->ssids[0].ssid_len) {
828bdcd8170SKalle Valo 		u8 i;
829bdcd8170SKalle Valo 
830bdcd8170SKalle Valo 		if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
831bdcd8170SKalle Valo 			request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
832bdcd8170SKalle Valo 
833bdcd8170SKalle Valo 		for (i = 0; i < request->n_ssids; i++)
834334234b5SVasanthakumar Thiagarajan 			ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
835334234b5SVasanthakumar Thiagarajan 						  i + 1, SPECIFIC_SSID_FLAG,
836bdcd8170SKalle Valo 						  request->ssids[i].ssid_len,
837bdcd8170SKalle Valo 						  request->ssids[i].ssid);
838bdcd8170SKalle Valo 	}
839bdcd8170SKalle Valo 
840b84da8c7SJouni Malinen 	if (request->ie) {
841334234b5SVasanthakumar Thiagarajan 		ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
842334234b5SVasanthakumar Thiagarajan 					       WMI_FRAME_PROBE_REQ,
843b84da8c7SJouni Malinen 					       request->ie, request->ie_len);
844b84da8c7SJouni Malinen 		if (ret) {
845b84da8c7SJouni Malinen 			ath6kl_err("failed to set Probe Request appie for "
846b84da8c7SJouni Malinen 				   "scan");
847b84da8c7SJouni Malinen 			return ret;
848b84da8c7SJouni Malinen 		}
849b84da8c7SJouni Malinen 	}
850b84da8c7SJouni Malinen 
85111869befSJouni Malinen 	/*
85211869befSJouni Malinen 	 * Scan only the requested channels if the request specifies a set of
85311869befSJouni Malinen 	 * channels. If the list is longer than the target supports, do not
85411869befSJouni Malinen 	 * configure the list and instead, scan all available channels.
85511869befSJouni Malinen 	 */
85611869befSJouni Malinen 	if (request->n_channels > 0 &&
85711869befSJouni Malinen 	    request->n_channels <= WMI_MAX_CHANNELS) {
8581276c9efSEdward Lu 		u8 i;
8591276c9efSEdward Lu 
86011869befSJouni Malinen 		n_channels = request->n_channels;
8611276c9efSEdward Lu 
8621276c9efSEdward Lu 		channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
8631276c9efSEdward Lu 		if (channels == NULL) {
8641276c9efSEdward Lu 			ath6kl_warn("failed to set scan channels, "
8651276c9efSEdward Lu 				    "scan all channels");
8661276c9efSEdward Lu 			n_channels = 0;
8671276c9efSEdward Lu 		}
8681276c9efSEdward Lu 
8691276c9efSEdward Lu 		for (i = 0; i < n_channels; i++)
8701276c9efSEdward Lu 			channels[i] = request->channels[i]->center_freq;
8711276c9efSEdward Lu 	}
8721276c9efSEdward Lu 
87359c98449SVasanthakumar Thiagarajan 	if (test_bit(CONNECTED, &vif->flags))
874f1f92179SVasanthakumar Thiagarajan 		force_fg_scan = 1;
875f1f92179SVasanthakumar Thiagarajan 
876334234b5SVasanthakumar Thiagarajan 	ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN,
877334234b5SVasanthakumar Thiagarajan 				       force_fg_scan, false, 0, 0, n_channels,
878334234b5SVasanthakumar Thiagarajan 				       channels);
8791b1e6ee3SJouni Malinen 	if (ret)
880bdcd8170SKalle Valo 		ath6kl_err("wmi_startscan_cmd failed\n");
88111869befSJouni Malinen 	else
88214ee6f6bSVasanthakumar Thiagarajan 		vif->scan_req = request;
883bdcd8170SKalle Valo 
8841276c9efSEdward Lu 	kfree(channels);
8851276c9efSEdward Lu 
886bdcd8170SKalle Valo 	return ret;
887bdcd8170SKalle Valo }
888bdcd8170SKalle Valo 
8891c17d313SKalle Valo void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
890bdcd8170SKalle Valo {
891240d2799SVasanthakumar Thiagarajan 	struct ath6kl *ar = vif->ar;
8926fd1eaceSKalle Valo 	int i;
893bdcd8170SKalle Valo 
8941c17d313SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
8951c17d313SKalle Valo 		   aborted ? " aborted" : "");
896bdcd8170SKalle Valo 
89714ee6f6bSVasanthakumar Thiagarajan 	if (!vif->scan_req)
8986fd1eaceSKalle Valo 		return;
8996fd1eaceSKalle Valo 
9001c17d313SKalle Valo 	if (aborted)
9016fd1eaceSKalle Valo 		goto out;
902bdcd8170SKalle Valo 
90314ee6f6bSVasanthakumar Thiagarajan 	if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
90414ee6f6bSVasanthakumar Thiagarajan 		for (i = 0; i < vif->scan_req->n_ssids; i++) {
905334234b5SVasanthakumar Thiagarajan 			ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
906334234b5SVasanthakumar Thiagarajan 						  i + 1, DISABLE_SSID_FLAG,
907bdcd8170SKalle Valo 						  0, NULL);
908bdcd8170SKalle Valo 		}
909bdcd8170SKalle Valo 	}
9106fd1eaceSKalle Valo 
9116fd1eaceSKalle Valo out:
912cb93821aSKalle Valo 	cfg80211_scan_done(vif->scan_req, aborted);
91314ee6f6bSVasanthakumar Thiagarajan 	vif->scan_req = NULL;
914bdcd8170SKalle Valo }
915bdcd8170SKalle Valo 
916bdcd8170SKalle Valo static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
917bdcd8170SKalle Valo 				   u8 key_index, bool pairwise,
918bdcd8170SKalle Valo 				   const u8 *mac_addr,
919bdcd8170SKalle Valo 				   struct key_params *params)
920bdcd8170SKalle Valo {
921bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
92259c98449SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(ndev);
923bdcd8170SKalle Valo 	struct ath6kl_key *key = NULL;
924bdcd8170SKalle Valo 	u8 key_usage;
925bdcd8170SKalle Valo 	u8 key_type;
926bdcd8170SKalle Valo 
927990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
928bdcd8170SKalle Valo 		return -EIO;
929bdcd8170SKalle Valo 
930837cb97eSJouni Malinen 	if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
931837cb97eSJouni Malinen 		if (params->key_len != WMI_KRK_LEN)
932837cb97eSJouni Malinen 			return -EINVAL;
933240d2799SVasanthakumar Thiagarajan 		return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
934240d2799SVasanthakumar Thiagarajan 					      params->key);
935837cb97eSJouni Malinen 	}
936837cb97eSJouni Malinen 
937bdcd8170SKalle Valo 	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
938bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
939bdcd8170SKalle Valo 			   "%s: key index %d out of bounds\n", __func__,
940bdcd8170SKalle Valo 			   key_index);
941bdcd8170SKalle Valo 		return -ENOENT;
942bdcd8170SKalle Valo 	}
943bdcd8170SKalle Valo 
9446f2a73f9SVasanthakumar Thiagarajan 	key = &vif->keys[key_index];
945bdcd8170SKalle Valo 	memset(key, 0, sizeof(struct ath6kl_key));
946bdcd8170SKalle Valo 
947bdcd8170SKalle Valo 	if (pairwise)
948bdcd8170SKalle Valo 		key_usage = PAIRWISE_USAGE;
949bdcd8170SKalle Valo 	else
950bdcd8170SKalle Valo 		key_usage = GROUP_USAGE;
951bdcd8170SKalle Valo 
952bdcd8170SKalle Valo 	if (params) {
9535e07021eSDai Shuibing 		int seq_len = params->seq_len;
9545e07021eSDai Shuibing 		if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
9555e07021eSDai Shuibing 		    seq_len > ATH6KL_KEY_SEQ_LEN) {
9565e07021eSDai Shuibing 			/* Only first half of the WPI PN is configured */
9575e07021eSDai Shuibing 			seq_len = ATH6KL_KEY_SEQ_LEN;
9585e07021eSDai Shuibing 		}
959bdcd8170SKalle Valo 		if (params->key_len > WLAN_MAX_KEY_LEN ||
9605e07021eSDai Shuibing 		    seq_len > sizeof(key->seq))
961bdcd8170SKalle Valo 			return -EINVAL;
962bdcd8170SKalle Valo 
963bdcd8170SKalle Valo 		key->key_len = params->key_len;
964bdcd8170SKalle Valo 		memcpy(key->key, params->key, key->key_len);
9655e07021eSDai Shuibing 		key->seq_len = seq_len;
966bdcd8170SKalle Valo 		memcpy(key->seq, params->seq, key->seq_len);
967bdcd8170SKalle Valo 		key->cipher = params->cipher;
968bdcd8170SKalle Valo 	}
969bdcd8170SKalle Valo 
970bdcd8170SKalle Valo 	switch (key->cipher) {
971bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_WEP40:
972bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_WEP104:
973bdcd8170SKalle Valo 		key_type = WEP_CRYPT;
974bdcd8170SKalle Valo 		break;
975bdcd8170SKalle Valo 
976bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_TKIP:
977bdcd8170SKalle Valo 		key_type = TKIP_CRYPT;
978bdcd8170SKalle Valo 		break;
979bdcd8170SKalle Valo 
980bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_CCMP:
981bdcd8170SKalle Valo 		key_type = AES_CRYPT;
982bdcd8170SKalle Valo 		break;
9835e07021eSDai Shuibing 	case WLAN_CIPHER_SUITE_SMS4:
9845e07021eSDai Shuibing 		key_type = WAPI_CRYPT;
9855e07021eSDai Shuibing 		break;
986bdcd8170SKalle Valo 
987bdcd8170SKalle Valo 	default:
988bdcd8170SKalle Valo 		return -ENOTSUPP;
989bdcd8170SKalle Valo 	}
990bdcd8170SKalle Valo 
9913450334fSVasanthakumar Thiagarajan 	if (((vif->auth_mode == WPA_PSK_AUTH)
9923450334fSVasanthakumar Thiagarajan 	     || (vif->auth_mode == WPA2_PSK_AUTH))
993bdcd8170SKalle Valo 	    && (key_usage & GROUP_USAGE))
994de3ad713SVasanthakumar Thiagarajan 		del_timer(&vif->disconnect_timer);
995bdcd8170SKalle Valo 
996bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
997bdcd8170SKalle Valo 		   "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
998bdcd8170SKalle Valo 		   __func__, key_index, key->key_len, key_type,
999bdcd8170SKalle Valo 		   key_usage, key->seq_len);
1000bdcd8170SKalle Valo 
1001f5938f24SVasanthakumar Thiagarajan 	if (vif->nw_type == AP_NETWORK && !pairwise &&
10029a5b1318SJouni Malinen 	    (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
10039a5b1318SJouni Malinen 		ar->ap_mode_bkey.valid = true;
10049a5b1318SJouni Malinen 		ar->ap_mode_bkey.key_index = key_index;
10059a5b1318SJouni Malinen 		ar->ap_mode_bkey.key_type = key_type;
10069a5b1318SJouni Malinen 		ar->ap_mode_bkey.key_len = key->key_len;
10079a5b1318SJouni Malinen 		memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
100859c98449SVasanthakumar Thiagarajan 		if (!test_bit(CONNECTED, &vif->flags)) {
10099a5b1318SJouni Malinen 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
10109a5b1318SJouni Malinen 				   "key configuration until AP mode has been "
10119a5b1318SJouni Malinen 				   "started\n");
10129a5b1318SJouni Malinen 			/*
10139a5b1318SJouni Malinen 			 * The key will be set in ath6kl_connect_ap_mode() once
10149a5b1318SJouni Malinen 			 * the connected event is received from the target.
10159a5b1318SJouni Malinen 			 */
10169a5b1318SJouni Malinen 			return 0;
10179a5b1318SJouni Malinen 		}
10189a5b1318SJouni Malinen 	}
10199a5b1318SJouni Malinen 
1020f5938f24SVasanthakumar Thiagarajan 	if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
102159c98449SVasanthakumar Thiagarajan 	    !test_bit(CONNECTED, &vif->flags)) {
1022151411e8SJouni Malinen 		/*
1023151411e8SJouni Malinen 		 * Store the key locally so that it can be re-configured after
1024151411e8SJouni Malinen 		 * the AP mode has properly started
1025151411e8SJouni Malinen 		 * (ath6kl_install_statioc_wep_keys).
1026151411e8SJouni Malinen 		 */
1027151411e8SJouni Malinen 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1028151411e8SJouni Malinen 			   "until AP mode has been started\n");
10296f2a73f9SVasanthakumar Thiagarajan 		vif->wep_key_list[key_index].key_len = key->key_len;
10306f2a73f9SVasanthakumar Thiagarajan 		memcpy(vif->wep_key_list[key_index].key, key->key,
10316f2a73f9SVasanthakumar Thiagarajan 		       key->key_len);
1032151411e8SJouni Malinen 		return 0;
1033151411e8SJouni Malinen 	}
1034151411e8SJouni Malinen 
10357cefa44fSVasanthakumar Thiagarajan 	return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
1036bdcd8170SKalle Valo 				     key_type, key_usage, key->key_len,
1037f4bb9a6fSJouni Malinen 				     key->seq, key->seq_len, key->key,
1038f4bb9a6fSJouni Malinen 				     KEY_OP_INIT_VAL,
1039bdcd8170SKalle Valo 				     (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
1040bdcd8170SKalle Valo }
1041bdcd8170SKalle Valo 
1042bdcd8170SKalle Valo static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1043bdcd8170SKalle Valo 				   u8 key_index, bool pairwise,
1044bdcd8170SKalle Valo 				   const u8 *mac_addr)
1045bdcd8170SKalle Valo {
1046bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
10476f2a73f9SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(ndev);
1048bdcd8170SKalle Valo 
1049bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1050bdcd8170SKalle Valo 
1051990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
1052bdcd8170SKalle Valo 		return -EIO;
1053bdcd8170SKalle Valo 
1054bdcd8170SKalle Valo 	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1055bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1056bdcd8170SKalle Valo 			   "%s: key index %d out of bounds\n", __func__,
1057bdcd8170SKalle Valo 			   key_index);
1058bdcd8170SKalle Valo 		return -ENOENT;
1059bdcd8170SKalle Valo 	}
1060bdcd8170SKalle Valo 
10616f2a73f9SVasanthakumar Thiagarajan 	if (!vif->keys[key_index].key_len) {
1062bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1063bdcd8170SKalle Valo 			   "%s: index %d is empty\n", __func__, key_index);
1064bdcd8170SKalle Valo 		return 0;
1065bdcd8170SKalle Valo 	}
1066bdcd8170SKalle Valo 
10676f2a73f9SVasanthakumar Thiagarajan 	vif->keys[key_index].key_len = 0;
1068bdcd8170SKalle Valo 
1069334234b5SVasanthakumar Thiagarajan 	return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
1070bdcd8170SKalle Valo }
1071bdcd8170SKalle Valo 
1072bdcd8170SKalle Valo static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1073bdcd8170SKalle Valo 				   u8 key_index, bool pairwise,
1074bdcd8170SKalle Valo 				   const u8 *mac_addr, void *cookie,
1075bdcd8170SKalle Valo 				   void (*callback) (void *cookie,
1076bdcd8170SKalle Valo 						     struct key_params *))
1077bdcd8170SKalle Valo {
10786f2a73f9SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(ndev);
1079bdcd8170SKalle Valo 	struct ath6kl_key *key = NULL;
1080bdcd8170SKalle Valo 	struct key_params params;
1081bdcd8170SKalle Valo 
1082bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1083bdcd8170SKalle Valo 
1084990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
1085bdcd8170SKalle Valo 		return -EIO;
1086bdcd8170SKalle Valo 
1087bdcd8170SKalle Valo 	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1088bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1089bdcd8170SKalle Valo 			   "%s: key index %d out of bounds\n", __func__,
1090bdcd8170SKalle Valo 			   key_index);
1091bdcd8170SKalle Valo 		return -ENOENT;
1092bdcd8170SKalle Valo 	}
1093bdcd8170SKalle Valo 
10946f2a73f9SVasanthakumar Thiagarajan 	key = &vif->keys[key_index];
1095bdcd8170SKalle Valo 	memset(&params, 0, sizeof(params));
1096bdcd8170SKalle Valo 	params.cipher = key->cipher;
1097bdcd8170SKalle Valo 	params.key_len = key->key_len;
1098bdcd8170SKalle Valo 	params.seq_len = key->seq_len;
1099bdcd8170SKalle Valo 	params.seq = key->seq;
1100bdcd8170SKalle Valo 	params.key = key->key;
1101bdcd8170SKalle Valo 
1102bdcd8170SKalle Valo 	callback(cookie, &params);
1103bdcd8170SKalle Valo 
1104bdcd8170SKalle Valo 	return key->key_len ? 0 : -ENOENT;
1105bdcd8170SKalle Valo }
1106bdcd8170SKalle Valo 
1107bdcd8170SKalle Valo static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1108bdcd8170SKalle Valo 					   struct net_device *ndev,
1109bdcd8170SKalle Valo 					   u8 key_index, bool unicast,
1110bdcd8170SKalle Valo 					   bool multicast)
1111bdcd8170SKalle Valo {
1112bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
111359c98449SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(ndev);
1114bdcd8170SKalle Valo 	struct ath6kl_key *key = NULL;
1115bdcd8170SKalle Valo 	u8 key_usage;
1116229ed6b5SEdward Lu 	enum crypto_type key_type = NONE_CRYPT;
1117bdcd8170SKalle Valo 
1118bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1119bdcd8170SKalle Valo 
1120990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
1121bdcd8170SKalle Valo 		return -EIO;
1122bdcd8170SKalle Valo 
1123bdcd8170SKalle Valo 	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1124bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1125bdcd8170SKalle Valo 			   "%s: key index %d out of bounds\n",
1126bdcd8170SKalle Valo 			   __func__, key_index);
1127bdcd8170SKalle Valo 		return -ENOENT;
1128bdcd8170SKalle Valo 	}
1129bdcd8170SKalle Valo 
11306f2a73f9SVasanthakumar Thiagarajan 	if (!vif->keys[key_index].key_len) {
1131bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1132bdcd8170SKalle Valo 			   __func__, key_index);
1133bdcd8170SKalle Valo 		return -EINVAL;
1134bdcd8170SKalle Valo 	}
1135bdcd8170SKalle Valo 
11363450334fSVasanthakumar Thiagarajan 	vif->def_txkey_index = key_index;
11376f2a73f9SVasanthakumar Thiagarajan 	key = &vif->keys[vif->def_txkey_index];
1138bdcd8170SKalle Valo 	key_usage = GROUP_USAGE;
11393450334fSVasanthakumar Thiagarajan 	if (vif->prwise_crypto == WEP_CRYPT)
1140bdcd8170SKalle Valo 		key_usage |= TX_USAGE;
1141229ed6b5SEdward Lu 	if (unicast)
11423450334fSVasanthakumar Thiagarajan 		key_type = vif->prwise_crypto;
1143229ed6b5SEdward Lu 	if (multicast)
11443450334fSVasanthakumar Thiagarajan 		key_type = vif->grp_crypto;
1145bdcd8170SKalle Valo 
1146f5938f24SVasanthakumar Thiagarajan 	if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
11479a5b1318SJouni Malinen 		return 0; /* Delay until AP mode has been started */
11489a5b1318SJouni Malinen 
1149f3e61eceSJouni Malinen 	return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1150334234b5SVasanthakumar Thiagarajan 				     vif->def_txkey_index,
1151229ed6b5SEdward Lu 				     key_type, key_usage,
1152f4bb9a6fSJouni Malinen 				     key->key_len, key->seq, key->seq_len,
1153f4bb9a6fSJouni Malinen 				     key->key,
1154bdcd8170SKalle Valo 				     KEY_OP_INIT_VAL, NULL,
1155bdcd8170SKalle Valo 				     SYNC_BOTH_WMIFLAG);
1156bdcd8170SKalle Valo }
1157bdcd8170SKalle Valo 
1158240d2799SVasanthakumar Thiagarajan void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
1159bdcd8170SKalle Valo 				       bool ismcast)
1160bdcd8170SKalle Valo {
1161bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1162bdcd8170SKalle Valo 		   "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1163bdcd8170SKalle Valo 
1164240d2799SVasanthakumar Thiagarajan 	cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
1165bdcd8170SKalle Valo 				     (ismcast ? NL80211_KEYTYPE_GROUP :
1166bdcd8170SKalle Valo 				      NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1167bdcd8170SKalle Valo 				     GFP_KERNEL);
1168bdcd8170SKalle Valo }
1169bdcd8170SKalle Valo 
1170bdcd8170SKalle Valo static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1171bdcd8170SKalle Valo {
1172bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
1173990bd915SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif;
1174bdcd8170SKalle Valo 	int ret;
1175bdcd8170SKalle Valo 
1176bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1177bdcd8170SKalle Valo 		   changed);
1178bdcd8170SKalle Valo 
1179990bd915SVasanthakumar Thiagarajan 	vif = ath6kl_vif_first(ar);
1180990bd915SVasanthakumar Thiagarajan 	if (!vif)
1181990bd915SVasanthakumar Thiagarajan 		return -EIO;
1182990bd915SVasanthakumar Thiagarajan 
1183990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
1184bdcd8170SKalle Valo 		return -EIO;
1185bdcd8170SKalle Valo 
1186bdcd8170SKalle Valo 	if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1187bdcd8170SKalle Valo 		ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1188bdcd8170SKalle Valo 		if (ret != 0) {
1189bdcd8170SKalle Valo 			ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1190bdcd8170SKalle Valo 			return -EIO;
1191bdcd8170SKalle Valo 		}
1192bdcd8170SKalle Valo 	}
1193bdcd8170SKalle Valo 
1194bdcd8170SKalle Valo 	return 0;
1195bdcd8170SKalle Valo }
1196bdcd8170SKalle Valo 
1197bdcd8170SKalle Valo /*
1198bdcd8170SKalle Valo  * The type nl80211_tx_power_setting replaces the following
1199bdcd8170SKalle Valo  * data type from 2.6.36 onwards
1200bdcd8170SKalle Valo */
1201bdcd8170SKalle Valo static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1202bdcd8170SKalle Valo 				       enum nl80211_tx_power_setting type,
1203bdcd8170SKalle Valo 				       int dbm)
1204bdcd8170SKalle Valo {
1205bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
1206990bd915SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif;
1207bdcd8170SKalle Valo 	u8 ath6kl_dbm;
1208bdcd8170SKalle Valo 
1209bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1210bdcd8170SKalle Valo 		   type, dbm);
1211bdcd8170SKalle Valo 
1212990bd915SVasanthakumar Thiagarajan 	vif = ath6kl_vif_first(ar);
1213990bd915SVasanthakumar Thiagarajan 	if (!vif)
1214990bd915SVasanthakumar Thiagarajan 		return -EIO;
1215990bd915SVasanthakumar Thiagarajan 
1216990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
1217bdcd8170SKalle Valo 		return -EIO;
1218bdcd8170SKalle Valo 
1219bdcd8170SKalle Valo 	switch (type) {
1220bdcd8170SKalle Valo 	case NL80211_TX_POWER_AUTOMATIC:
1221bdcd8170SKalle Valo 		return 0;
1222bdcd8170SKalle Valo 	case NL80211_TX_POWER_LIMITED:
1223bdcd8170SKalle Valo 		ar->tx_pwr = ath6kl_dbm = dbm;
1224bdcd8170SKalle Valo 		break;
1225bdcd8170SKalle Valo 	default:
1226bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1227bdcd8170SKalle Valo 			   __func__, type);
1228bdcd8170SKalle Valo 		return -EOPNOTSUPP;
1229bdcd8170SKalle Valo 	}
1230bdcd8170SKalle Valo 
1231990bd915SVasanthakumar Thiagarajan 	ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm);
1232bdcd8170SKalle Valo 
1233bdcd8170SKalle Valo 	return 0;
1234bdcd8170SKalle Valo }
1235bdcd8170SKalle Valo 
1236bdcd8170SKalle Valo static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1237bdcd8170SKalle Valo {
1238bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
1239990bd915SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif;
1240bdcd8170SKalle Valo 
1241990bd915SVasanthakumar Thiagarajan 	vif = ath6kl_vif_first(ar);
1242990bd915SVasanthakumar Thiagarajan 	if (!vif)
1243990bd915SVasanthakumar Thiagarajan 		return -EIO;
1244990bd915SVasanthakumar Thiagarajan 
1245990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
1246bdcd8170SKalle Valo 		return -EIO;
1247bdcd8170SKalle Valo 
124859c98449SVasanthakumar Thiagarajan 	if (test_bit(CONNECTED, &vif->flags)) {
1249bdcd8170SKalle Valo 		ar->tx_pwr = 0;
1250bdcd8170SKalle Valo 
1251990bd915SVasanthakumar Thiagarajan 		if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
1252bdcd8170SKalle Valo 			ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1253bdcd8170SKalle Valo 			return -EIO;
1254bdcd8170SKalle Valo 		}
1255bdcd8170SKalle Valo 
1256bdcd8170SKalle Valo 		wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1257bdcd8170SKalle Valo 						 5 * HZ);
1258bdcd8170SKalle Valo 
1259bdcd8170SKalle Valo 		if (signal_pending(current)) {
1260bdcd8170SKalle Valo 			ath6kl_err("target did not respond\n");
1261bdcd8170SKalle Valo 			return -EINTR;
1262bdcd8170SKalle Valo 		}
1263bdcd8170SKalle Valo 	}
1264bdcd8170SKalle Valo 
1265bdcd8170SKalle Valo 	*dbm = ar->tx_pwr;
1266bdcd8170SKalle Valo 	return 0;
1267bdcd8170SKalle Valo }
1268bdcd8170SKalle Valo 
1269bdcd8170SKalle Valo static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1270bdcd8170SKalle Valo 					  struct net_device *dev,
1271bdcd8170SKalle Valo 					  bool pmgmt, int timeout)
1272bdcd8170SKalle Valo {
1273bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(dev);
1274bdcd8170SKalle Valo 	struct wmi_power_mode_cmd mode;
1275334234b5SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
1276bdcd8170SKalle Valo 
1277bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1278bdcd8170SKalle Valo 		   __func__, pmgmt, timeout);
1279bdcd8170SKalle Valo 
1280990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
1281bdcd8170SKalle Valo 		return -EIO;
1282bdcd8170SKalle Valo 
1283bdcd8170SKalle Valo 	if (pmgmt) {
1284bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1285bdcd8170SKalle Valo 		mode.pwr_mode = REC_POWER;
1286bdcd8170SKalle Valo 	} else {
1287bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1288bdcd8170SKalle Valo 		mode.pwr_mode = MAX_PERF_POWER;
1289bdcd8170SKalle Valo 	}
1290bdcd8170SKalle Valo 
1291334234b5SVasanthakumar Thiagarajan 	if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
1292334234b5SVasanthakumar Thiagarajan 	     mode.pwr_mode) != 0) {
1293bdcd8170SKalle Valo 		ath6kl_err("wmi_powermode_cmd failed\n");
1294bdcd8170SKalle Valo 		return -EIO;
1295bdcd8170SKalle Valo 	}
1296bdcd8170SKalle Valo 
1297bdcd8170SKalle Valo 	return 0;
1298bdcd8170SKalle Valo }
1299bdcd8170SKalle Valo 
130055055976SVasanthakumar Thiagarajan static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
130155055976SVasanthakumar Thiagarajan 						    char *name,
130255055976SVasanthakumar Thiagarajan 						    enum nl80211_iftype type,
130355055976SVasanthakumar Thiagarajan 						    u32 *flags,
130455055976SVasanthakumar Thiagarajan 						    struct vif_params *params)
130555055976SVasanthakumar Thiagarajan {
130655055976SVasanthakumar Thiagarajan 	struct ath6kl *ar = wiphy_priv(wiphy);
130755055976SVasanthakumar Thiagarajan 	struct net_device *ndev;
130855055976SVasanthakumar Thiagarajan 	u8 if_idx, nw_type;
130955055976SVasanthakumar Thiagarajan 
131071f96ee6SKalle Valo 	if (ar->num_vif == ar->vif_max) {
131155055976SVasanthakumar Thiagarajan 		ath6kl_err("Reached maximum number of supported vif\n");
131255055976SVasanthakumar Thiagarajan 		return ERR_PTR(-EINVAL);
131355055976SVasanthakumar Thiagarajan 	}
131455055976SVasanthakumar Thiagarajan 
131555055976SVasanthakumar Thiagarajan 	if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
131655055976SVasanthakumar Thiagarajan 		ath6kl_err("Not a supported interface type\n");
131755055976SVasanthakumar Thiagarajan 		return ERR_PTR(-EINVAL);
131855055976SVasanthakumar Thiagarajan 	}
131955055976SVasanthakumar Thiagarajan 
132055055976SVasanthakumar Thiagarajan 	ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
132155055976SVasanthakumar Thiagarajan 	if (!ndev)
132255055976SVasanthakumar Thiagarajan 		return ERR_PTR(-ENOMEM);
132355055976SVasanthakumar Thiagarajan 
132455055976SVasanthakumar Thiagarajan 	ar->num_vif++;
132555055976SVasanthakumar Thiagarajan 
132655055976SVasanthakumar Thiagarajan 	return ndev;
132755055976SVasanthakumar Thiagarajan }
132855055976SVasanthakumar Thiagarajan 
132955055976SVasanthakumar Thiagarajan static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
133055055976SVasanthakumar Thiagarajan 				     struct net_device *ndev)
133155055976SVasanthakumar Thiagarajan {
133255055976SVasanthakumar Thiagarajan 	struct ath6kl *ar = wiphy_priv(wiphy);
133355055976SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(ndev);
133455055976SVasanthakumar Thiagarajan 
133511f6e40dSVasanthakumar Thiagarajan 	spin_lock_bh(&ar->list_lock);
133655055976SVasanthakumar Thiagarajan 	list_del(&vif->list);
133711f6e40dSVasanthakumar Thiagarajan 	spin_unlock_bh(&ar->list_lock);
133855055976SVasanthakumar Thiagarajan 
133955055976SVasanthakumar Thiagarajan 	ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
134055055976SVasanthakumar Thiagarajan 
134155055976SVasanthakumar Thiagarajan 	ath6kl_deinit_if_data(vif);
134255055976SVasanthakumar Thiagarajan 
134355055976SVasanthakumar Thiagarajan 	return 0;
134455055976SVasanthakumar Thiagarajan }
134555055976SVasanthakumar Thiagarajan 
1346bdcd8170SKalle Valo static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1347bdcd8170SKalle Valo 					struct net_device *ndev,
1348bdcd8170SKalle Valo 					enum nl80211_iftype type, u32 *flags,
1349bdcd8170SKalle Valo 					struct vif_params *params)
1350bdcd8170SKalle Valo {
1351f5938f24SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(ndev);
1352bdcd8170SKalle Valo 
1353bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1354bdcd8170SKalle Valo 
1355bdcd8170SKalle Valo 	switch (type) {
1356bdcd8170SKalle Valo 	case NL80211_IFTYPE_STATION:
1357f5938f24SVasanthakumar Thiagarajan 		vif->next_mode = INFRA_NETWORK;
1358bdcd8170SKalle Valo 		break;
1359bdcd8170SKalle Valo 	case NL80211_IFTYPE_ADHOC:
1360f5938f24SVasanthakumar Thiagarajan 		vif->next_mode = ADHOC_NETWORK;
1361bdcd8170SKalle Valo 		break;
13626e4604c8SJouni Malinen 	case NL80211_IFTYPE_AP:
1363f5938f24SVasanthakumar Thiagarajan 		vif->next_mode = AP_NETWORK;
13646e4604c8SJouni Malinen 		break;
13656b5e5d25SJouni Malinen 	case NL80211_IFTYPE_P2P_CLIENT:
1366f5938f24SVasanthakumar Thiagarajan 		vif->next_mode = INFRA_NETWORK;
13676b5e5d25SJouni Malinen 		break;
13686b5e5d25SJouni Malinen 	case NL80211_IFTYPE_P2P_GO:
1369f5938f24SVasanthakumar Thiagarajan 		vif->next_mode = AP_NETWORK;
13706b5e5d25SJouni Malinen 		break;
1371bdcd8170SKalle Valo 	default:
1372bdcd8170SKalle Valo 		ath6kl_err("invalid interface type %u\n", type);
1373bdcd8170SKalle Valo 		return -EOPNOTSUPP;
1374bdcd8170SKalle Valo 	}
1375bdcd8170SKalle Valo 
1376551959d8SVasanthakumar Thiagarajan 	vif->wdev.iftype = type;
1377bdcd8170SKalle Valo 
1378bdcd8170SKalle Valo 	return 0;
1379bdcd8170SKalle Valo }
1380bdcd8170SKalle Valo 
1381bdcd8170SKalle Valo static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1382bdcd8170SKalle Valo 				     struct net_device *dev,
1383bdcd8170SKalle Valo 				     struct cfg80211_ibss_params *ibss_param)
1384bdcd8170SKalle Valo {
1385bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(dev);
138659c98449SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
1387bdcd8170SKalle Valo 	int status;
1388bdcd8170SKalle Valo 
1389990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
1390bdcd8170SKalle Valo 		return -EIO;
1391bdcd8170SKalle Valo 
13923450334fSVasanthakumar Thiagarajan 	vif->ssid_len = ibss_param->ssid_len;
13933450334fSVasanthakumar Thiagarajan 	memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
1394bdcd8170SKalle Valo 
1395bdcd8170SKalle Valo 	if (ibss_param->channel)
1396f74bac54SVasanthakumar Thiagarajan 		vif->ch_hint = ibss_param->channel->center_freq;
1397bdcd8170SKalle Valo 
1398bdcd8170SKalle Valo 	if (ibss_param->channel_fixed) {
1399bdcd8170SKalle Valo 		/*
1400bdcd8170SKalle Valo 		 * TODO: channel_fixed: The channel should be fixed, do not
1401bdcd8170SKalle Valo 		 * search for IBSSs to join on other channels. Target
1402bdcd8170SKalle Valo 		 * firmware does not support this feature, needs to be
1403bdcd8170SKalle Valo 		 * updated.
1404bdcd8170SKalle Valo 		 */
1405bdcd8170SKalle Valo 		return -EOPNOTSUPP;
1406bdcd8170SKalle Valo 	}
1407bdcd8170SKalle Valo 
14088c8b65e3SVasanthakumar Thiagarajan 	memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
1409bdcd8170SKalle Valo 	if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
14108c8b65e3SVasanthakumar Thiagarajan 		memcpy(vif->req_bssid, ibss_param->bssid,
14118c8b65e3SVasanthakumar Thiagarajan 		       sizeof(vif->req_bssid));
1412bdcd8170SKalle Valo 
1413240d2799SVasanthakumar Thiagarajan 	ath6kl_set_wpa_version(vif, 0);
1414bdcd8170SKalle Valo 
1415240d2799SVasanthakumar Thiagarajan 	status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
1416bdcd8170SKalle Valo 	if (status)
1417bdcd8170SKalle Valo 		return status;
1418bdcd8170SKalle Valo 
1419bdcd8170SKalle Valo 	if (ibss_param->privacy) {
1420240d2799SVasanthakumar Thiagarajan 		ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1421240d2799SVasanthakumar Thiagarajan 		ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
1422bdcd8170SKalle Valo 	} else {
1423240d2799SVasanthakumar Thiagarajan 		ath6kl_set_cipher(vif, 0, true);
1424240d2799SVasanthakumar Thiagarajan 		ath6kl_set_cipher(vif, 0, false);
1425bdcd8170SKalle Valo 	}
1426bdcd8170SKalle Valo 
1427f5938f24SVasanthakumar Thiagarajan 	vif->nw_type = vif->next_mode;
1428bdcd8170SKalle Valo 
1429bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1430bdcd8170SKalle Valo 		   "%s: connect called with authmode %d dot11 auth %d"
1431bdcd8170SKalle Valo 		   " PW crypto %d PW crypto len %d GRP crypto %d"
1432bdcd8170SKalle Valo 		   " GRP crypto len %d channel hint %u\n",
1433bdcd8170SKalle Valo 		   __func__,
14343450334fSVasanthakumar Thiagarajan 		   vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
14353450334fSVasanthakumar Thiagarajan 		   vif->prwise_crypto_len, vif->grp_crypto,
1436f74bac54SVasanthakumar Thiagarajan 		   vif->grp_crypto_len, vif->ch_hint);
1437bdcd8170SKalle Valo 
1438334234b5SVasanthakumar Thiagarajan 	status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
14393450334fSVasanthakumar Thiagarajan 					vif->dot11_auth_mode, vif->auth_mode,
14403450334fSVasanthakumar Thiagarajan 					vif->prwise_crypto,
14413450334fSVasanthakumar Thiagarajan 					vif->prwise_crypto_len,
14423450334fSVasanthakumar Thiagarajan 					vif->grp_crypto, vif->grp_crypto_len,
14433450334fSVasanthakumar Thiagarajan 					vif->ssid_len, vif->ssid,
1444f74bac54SVasanthakumar Thiagarajan 					vif->req_bssid, vif->ch_hint,
1445bdcd8170SKalle Valo 					ar->connect_ctrl_flags);
144659c98449SVasanthakumar Thiagarajan 	set_bit(CONNECT_PEND, &vif->flags);
1447bdcd8170SKalle Valo 
1448bdcd8170SKalle Valo 	return 0;
1449bdcd8170SKalle Valo }
1450bdcd8170SKalle Valo 
1451bdcd8170SKalle Valo static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1452bdcd8170SKalle Valo 				      struct net_device *dev)
1453bdcd8170SKalle Valo {
14543450334fSVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
1455bdcd8170SKalle Valo 
1456990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
1457bdcd8170SKalle Valo 		return -EIO;
1458bdcd8170SKalle Valo 
1459240d2799SVasanthakumar Thiagarajan 	ath6kl_disconnect(vif);
14603450334fSVasanthakumar Thiagarajan 	memset(vif->ssid, 0, sizeof(vif->ssid));
14613450334fSVasanthakumar Thiagarajan 	vif->ssid_len = 0;
1462bdcd8170SKalle Valo 
1463bdcd8170SKalle Valo 	return 0;
1464bdcd8170SKalle Valo }
1465bdcd8170SKalle Valo 
1466bdcd8170SKalle Valo static const u32 cipher_suites[] = {
1467bdcd8170SKalle Valo 	WLAN_CIPHER_SUITE_WEP40,
1468bdcd8170SKalle Valo 	WLAN_CIPHER_SUITE_WEP104,
1469bdcd8170SKalle Valo 	WLAN_CIPHER_SUITE_TKIP,
1470bdcd8170SKalle Valo 	WLAN_CIPHER_SUITE_CCMP,
1471837cb97eSJouni Malinen 	CCKM_KRK_CIPHER_SUITE,
14725e07021eSDai Shuibing 	WLAN_CIPHER_SUITE_SMS4,
1473bdcd8170SKalle Valo };
1474bdcd8170SKalle Valo 
1475bdcd8170SKalle Valo static bool is_rate_legacy(s32 rate)
1476bdcd8170SKalle Valo {
1477bdcd8170SKalle Valo 	static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1478bdcd8170SKalle Valo 		6000, 9000, 12000, 18000, 24000,
1479bdcd8170SKalle Valo 		36000, 48000, 54000
1480bdcd8170SKalle Valo 	};
1481bdcd8170SKalle Valo 	u8 i;
1482bdcd8170SKalle Valo 
1483bdcd8170SKalle Valo 	for (i = 0; i < ARRAY_SIZE(legacy); i++)
1484bdcd8170SKalle Valo 		if (rate == legacy[i])
1485bdcd8170SKalle Valo 			return true;
1486bdcd8170SKalle Valo 
1487bdcd8170SKalle Valo 	return false;
1488bdcd8170SKalle Valo }
1489bdcd8170SKalle Valo 
1490bdcd8170SKalle Valo static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1491bdcd8170SKalle Valo {
1492bdcd8170SKalle Valo 	static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1493bdcd8170SKalle Valo 		52000, 58500, 65000, 72200
1494bdcd8170SKalle Valo 	};
1495bdcd8170SKalle Valo 	u8 i;
1496bdcd8170SKalle Valo 
1497bdcd8170SKalle Valo 	for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1498bdcd8170SKalle Valo 		if (rate == ht20[i]) {
1499bdcd8170SKalle Valo 			if (i == ARRAY_SIZE(ht20) - 1)
1500bdcd8170SKalle Valo 				/* last rate uses sgi */
1501bdcd8170SKalle Valo 				*sgi = true;
1502bdcd8170SKalle Valo 			else
1503bdcd8170SKalle Valo 				*sgi = false;
1504bdcd8170SKalle Valo 
1505bdcd8170SKalle Valo 			*mcs = i;
1506bdcd8170SKalle Valo 			return true;
1507bdcd8170SKalle Valo 		}
1508bdcd8170SKalle Valo 	}
1509bdcd8170SKalle Valo 	return false;
1510bdcd8170SKalle Valo }
1511bdcd8170SKalle Valo 
1512bdcd8170SKalle Valo static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1513bdcd8170SKalle Valo {
1514bdcd8170SKalle Valo 	static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1515bdcd8170SKalle Valo 		81000, 108000, 121500, 135000,
1516bdcd8170SKalle Valo 		150000
1517bdcd8170SKalle Valo 	};
1518bdcd8170SKalle Valo 	u8 i;
1519bdcd8170SKalle Valo 
1520bdcd8170SKalle Valo 	for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1521bdcd8170SKalle Valo 		if (rate == ht40[i]) {
1522bdcd8170SKalle Valo 			if (i == ARRAY_SIZE(ht40) - 1)
1523bdcd8170SKalle Valo 				/* last rate uses sgi */
1524bdcd8170SKalle Valo 				*sgi = true;
1525bdcd8170SKalle Valo 			else
1526bdcd8170SKalle Valo 				*sgi = false;
1527bdcd8170SKalle Valo 
1528bdcd8170SKalle Valo 			*mcs = i;
1529bdcd8170SKalle Valo 			return true;
1530bdcd8170SKalle Valo 		}
1531bdcd8170SKalle Valo 	}
1532bdcd8170SKalle Valo 
1533bdcd8170SKalle Valo 	return false;
1534bdcd8170SKalle Valo }
1535bdcd8170SKalle Valo 
1536bdcd8170SKalle Valo static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1537bdcd8170SKalle Valo 			      u8 *mac, struct station_info *sinfo)
1538bdcd8170SKalle Valo {
1539bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(dev);
154059c98449SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
1541bdcd8170SKalle Valo 	long left;
1542bdcd8170SKalle Valo 	bool sgi;
1543bdcd8170SKalle Valo 	s32 rate;
1544bdcd8170SKalle Valo 	int ret;
1545bdcd8170SKalle Valo 	u8 mcs;
1546bdcd8170SKalle Valo 
15478c8b65e3SVasanthakumar Thiagarajan 	if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
1548bdcd8170SKalle Valo 		return -ENOENT;
1549bdcd8170SKalle Valo 
1550bdcd8170SKalle Valo 	if (down_interruptible(&ar->sem))
1551bdcd8170SKalle Valo 		return -EBUSY;
1552bdcd8170SKalle Valo 
1553b95907a7SVasanthakumar Thiagarajan 	set_bit(STATS_UPDATE_PEND, &vif->flags);
1554bdcd8170SKalle Valo 
1555334234b5SVasanthakumar Thiagarajan 	ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
1556bdcd8170SKalle Valo 
1557bdcd8170SKalle Valo 	if (ret != 0) {
1558bdcd8170SKalle Valo 		up(&ar->sem);
1559bdcd8170SKalle Valo 		return -EIO;
1560bdcd8170SKalle Valo 	}
1561bdcd8170SKalle Valo 
1562bdcd8170SKalle Valo 	left = wait_event_interruptible_timeout(ar->event_wq,
1563bdcd8170SKalle Valo 						!test_bit(STATS_UPDATE_PEND,
1564b95907a7SVasanthakumar Thiagarajan 							  &vif->flags),
1565bdcd8170SKalle Valo 						WMI_TIMEOUT);
1566bdcd8170SKalle Valo 
1567bdcd8170SKalle Valo 	up(&ar->sem);
1568bdcd8170SKalle Valo 
1569bdcd8170SKalle Valo 	if (left == 0)
1570bdcd8170SKalle Valo 		return -ETIMEDOUT;
1571bdcd8170SKalle Valo 	else if (left < 0)
1572bdcd8170SKalle Valo 		return left;
1573bdcd8170SKalle Valo 
1574b95907a7SVasanthakumar Thiagarajan 	if (vif->target_stats.rx_byte) {
1575b95907a7SVasanthakumar Thiagarajan 		sinfo->rx_bytes = vif->target_stats.rx_byte;
1576bdcd8170SKalle Valo 		sinfo->filled |= STATION_INFO_RX_BYTES;
1577b95907a7SVasanthakumar Thiagarajan 		sinfo->rx_packets = vif->target_stats.rx_pkt;
1578bdcd8170SKalle Valo 		sinfo->filled |= STATION_INFO_RX_PACKETS;
1579bdcd8170SKalle Valo 	}
1580bdcd8170SKalle Valo 
1581b95907a7SVasanthakumar Thiagarajan 	if (vif->target_stats.tx_byte) {
1582b95907a7SVasanthakumar Thiagarajan 		sinfo->tx_bytes = vif->target_stats.tx_byte;
1583bdcd8170SKalle Valo 		sinfo->filled |= STATION_INFO_TX_BYTES;
1584b95907a7SVasanthakumar Thiagarajan 		sinfo->tx_packets = vif->target_stats.tx_pkt;
1585bdcd8170SKalle Valo 		sinfo->filled |= STATION_INFO_TX_PACKETS;
1586bdcd8170SKalle Valo 	}
1587bdcd8170SKalle Valo 
1588b95907a7SVasanthakumar Thiagarajan 	sinfo->signal = vif->target_stats.cs_rssi;
1589bdcd8170SKalle Valo 	sinfo->filled |= STATION_INFO_SIGNAL;
1590bdcd8170SKalle Valo 
1591b95907a7SVasanthakumar Thiagarajan 	rate = vif->target_stats.tx_ucast_rate;
1592bdcd8170SKalle Valo 
1593bdcd8170SKalle Valo 	if (is_rate_legacy(rate)) {
1594bdcd8170SKalle Valo 		sinfo->txrate.legacy = rate / 100;
1595bdcd8170SKalle Valo 	} else if (is_rate_ht20(rate, &mcs, &sgi)) {
1596bdcd8170SKalle Valo 		if (sgi) {
1597bdcd8170SKalle Valo 			sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1598bdcd8170SKalle Valo 			sinfo->txrate.mcs = mcs - 1;
1599bdcd8170SKalle Valo 		} else {
1600bdcd8170SKalle Valo 			sinfo->txrate.mcs = mcs;
1601bdcd8170SKalle Valo 		}
1602bdcd8170SKalle Valo 
1603bdcd8170SKalle Valo 		sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1604bdcd8170SKalle Valo 	} else if (is_rate_ht40(rate, &mcs, &sgi)) {
1605bdcd8170SKalle Valo 		if (sgi) {
1606bdcd8170SKalle Valo 			sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1607bdcd8170SKalle Valo 			sinfo->txrate.mcs = mcs - 1;
1608bdcd8170SKalle Valo 		} else {
1609bdcd8170SKalle Valo 			sinfo->txrate.mcs = mcs;
1610bdcd8170SKalle Valo 		}
1611bdcd8170SKalle Valo 
1612bdcd8170SKalle Valo 		sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1613bdcd8170SKalle Valo 		sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1614bdcd8170SKalle Valo 	} else {
16159a730834SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
16169a730834SKalle Valo 			   "invalid rate from stats: %d\n", rate);
16179a730834SKalle Valo 		ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
1618bdcd8170SKalle Valo 		return 0;
1619bdcd8170SKalle Valo 	}
1620bdcd8170SKalle Valo 
1621bdcd8170SKalle Valo 	sinfo->filled |= STATION_INFO_TX_BITRATE;
1622bdcd8170SKalle Valo 
162359c98449SVasanthakumar Thiagarajan 	if (test_bit(CONNECTED, &vif->flags) &&
162459c98449SVasanthakumar Thiagarajan 	    test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
1625f5938f24SVasanthakumar Thiagarajan 	    vif->nw_type == INFRA_NETWORK) {
162632c10874SJouni Malinen 		sinfo->filled |= STATION_INFO_BSS_PARAM;
162732c10874SJouni Malinen 		sinfo->bss_param.flags = 0;
1628cf5333d7SVasanthakumar Thiagarajan 		sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1629cf5333d7SVasanthakumar Thiagarajan 		sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
163032c10874SJouni Malinen 	}
163132c10874SJouni Malinen 
1632bdcd8170SKalle Valo 	return 0;
1633bdcd8170SKalle Valo }
1634bdcd8170SKalle Valo 
1635bdcd8170SKalle Valo static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1636bdcd8170SKalle Valo 			    struct cfg80211_pmksa *pmksa)
1637bdcd8170SKalle Valo {
1638bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(netdev);
1639334234b5SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(netdev);
1640334234b5SVasanthakumar Thiagarajan 
1641334234b5SVasanthakumar Thiagarajan 	return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
1642bdcd8170SKalle Valo 				       pmksa->pmkid, true);
1643bdcd8170SKalle Valo }
1644bdcd8170SKalle Valo 
1645bdcd8170SKalle Valo static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1646bdcd8170SKalle Valo 			    struct cfg80211_pmksa *pmksa)
1647bdcd8170SKalle Valo {
1648bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(netdev);
1649334234b5SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(netdev);
1650334234b5SVasanthakumar Thiagarajan 
1651334234b5SVasanthakumar Thiagarajan 	return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
1652bdcd8170SKalle Valo 				       pmksa->pmkid, false);
1653bdcd8170SKalle Valo }
1654bdcd8170SKalle Valo 
1655bdcd8170SKalle Valo static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1656bdcd8170SKalle Valo {
1657bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(netdev);
165859c98449SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(netdev);
165959c98449SVasanthakumar Thiagarajan 
166059c98449SVasanthakumar Thiagarajan 	if (test_bit(CONNECTED, &vif->flags))
1661334234b5SVasanthakumar Thiagarajan 		return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1662334234b5SVasanthakumar Thiagarajan 					       vif->bssid, NULL, false);
1663bdcd8170SKalle Valo 	return 0;
1664bdcd8170SKalle Valo }
1665bdcd8170SKalle Valo 
16666cb3c714SRaja Mani static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
16676cb3c714SRaja Mani {
16686cb3c714SRaja Mani 	struct ath6kl_vif *vif;
16696cb3c714SRaja Mani 	int ret, pos, left;
16706cb3c714SRaja Mani 	u32 filter = 0;
16716cb3c714SRaja Mani 	u16 i;
16726cb3c714SRaja Mani 	u8 mask[WOW_MASK_SIZE];
16736cb3c714SRaja Mani 
16746cb3c714SRaja Mani 	vif = ath6kl_vif_first(ar);
16756cb3c714SRaja Mani 	if (!vif)
16766cb3c714SRaja Mani 		return -EIO;
16776cb3c714SRaja Mani 
16786cb3c714SRaja Mani 	if (!ath6kl_cfg80211_ready(vif))
16796cb3c714SRaja Mani 		return -EIO;
16806cb3c714SRaja Mani 
16816cb3c714SRaja Mani 	if (!test_bit(CONNECTED, &vif->flags))
16826cb3c714SRaja Mani 		return -EINVAL;
16836cb3c714SRaja Mani 
16846cb3c714SRaja Mani 	/* Clear existing WOW patterns */
16856cb3c714SRaja Mani 	for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
16866cb3c714SRaja Mani 		ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
16876cb3c714SRaja Mani 					       WOW_LIST_ID, i);
16886cb3c714SRaja Mani 	/* Configure new WOW patterns */
16896cb3c714SRaja Mani 	for (i = 0; i < wow->n_patterns; i++) {
16906cb3c714SRaja Mani 
16916cb3c714SRaja Mani 		/*
16926cb3c714SRaja Mani 		 * Convert given nl80211 specific mask value to equivalent
16936cb3c714SRaja Mani 		 * driver specific mask value and send it to the chip along
16946cb3c714SRaja Mani 		 * with patterns. For example, If the mask value defined in
16956cb3c714SRaja Mani 		 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
16966cb3c714SRaja Mani 		 * then equivalent driver specific mask value is
16976cb3c714SRaja Mani 		 * "0xFF 0x00 0xFF 0x00".
16986cb3c714SRaja Mani 		 */
16996cb3c714SRaja Mani 		memset(&mask, 0, sizeof(mask));
17006cb3c714SRaja Mani 		for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
17016cb3c714SRaja Mani 			if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
17026cb3c714SRaja Mani 				mask[pos] = 0xFF;
17036cb3c714SRaja Mani 		}
17046cb3c714SRaja Mani 		/*
17056cb3c714SRaja Mani 		 * Note: Pattern's offset is not passed as part of wowlan
17066cb3c714SRaja Mani 		 * parameter from CFG layer. So it's always passed as ZERO
17076cb3c714SRaja Mani 		 * to the firmware. It means, given WOW patterns are always
17086cb3c714SRaja Mani 		 * matched from the first byte of received pkt in the firmware.
17096cb3c714SRaja Mani 		 */
17106cb3c714SRaja Mani 		ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
17116cb3c714SRaja Mani 					vif->fw_vif_idx, WOW_LIST_ID,
17126cb3c714SRaja Mani 					wow->patterns[i].pattern_len,
17136cb3c714SRaja Mani 					0 /* pattern offset */,
17146cb3c714SRaja Mani 					wow->patterns[i].pattern, mask);
17156cb3c714SRaja Mani 		if (ret)
17166cb3c714SRaja Mani 			return ret;
17176cb3c714SRaja Mani 	}
17186cb3c714SRaja Mani 
17196cb3c714SRaja Mani 	if (wow->disconnect)
17206cb3c714SRaja Mani 		filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
17216cb3c714SRaja Mani 
17226cb3c714SRaja Mani 	if (wow->magic_pkt)
17236cb3c714SRaja Mani 		filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
17246cb3c714SRaja Mani 
17256cb3c714SRaja Mani 	if (wow->gtk_rekey_failure)
17266cb3c714SRaja Mani 		filter |= WOW_FILTER_OPTION_GTK_ERROR;
17276cb3c714SRaja Mani 
17286cb3c714SRaja Mani 	if (wow->eap_identity_req)
17296cb3c714SRaja Mani 		filter |= WOW_FILTER_OPTION_EAP_REQ;
17306cb3c714SRaja Mani 
17316cb3c714SRaja Mani 	if (wow->four_way_handshake)
17326cb3c714SRaja Mani 		filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
17336cb3c714SRaja Mani 
17346cb3c714SRaja Mani 	ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
17356cb3c714SRaja Mani 					  ATH6KL_WOW_MODE_ENABLE,
17366cb3c714SRaja Mani 					  filter,
17376cb3c714SRaja Mani 					  WOW_HOST_REQ_DELAY);
17386cb3c714SRaja Mani 	if (ret)
17396cb3c714SRaja Mani 		return ret;
17406cb3c714SRaja Mani 
17416cb3c714SRaja Mani 	ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
17426cb3c714SRaja Mani 						 ATH6KL_HOST_MODE_ASLEEP);
17436cb3c714SRaja Mani 	if (ret)
17446cb3c714SRaja Mani 		return ret;
17456cb3c714SRaja Mani 
17466cb3c714SRaja Mani 	if (ar->tx_pending[ar->ctrl_ep]) {
17476cb3c714SRaja Mani 		left = wait_event_interruptible_timeout(ar->event_wq,
17486cb3c714SRaja Mani 				ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
17496cb3c714SRaja Mani 		if (left == 0) {
17506cb3c714SRaja Mani 			ath6kl_warn("clear wmi ctrl data timeout\n");
17516cb3c714SRaja Mani 			ret = -ETIMEDOUT;
17526cb3c714SRaja Mani 		} else if (left < 0) {
17536cb3c714SRaja Mani 			ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
17546cb3c714SRaja Mani 			ret = left;
17556cb3c714SRaja Mani 		}
17566cb3c714SRaja Mani 	}
17576cb3c714SRaja Mani 
17586cb3c714SRaja Mani 	return ret;
17596cb3c714SRaja Mani }
17606cb3c714SRaja Mani 
17616cb3c714SRaja Mani static int ath6kl_wow_resume(struct ath6kl *ar)
17626cb3c714SRaja Mani {
17636cb3c714SRaja Mani 	struct ath6kl_vif *vif;
17646cb3c714SRaja Mani 	int ret;
17656cb3c714SRaja Mani 
17666cb3c714SRaja Mani 	vif = ath6kl_vif_first(ar);
17676cb3c714SRaja Mani 	if (!vif)
17686cb3c714SRaja Mani 		return -EIO;
17696cb3c714SRaja Mani 
17706cb3c714SRaja Mani 	ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
17716cb3c714SRaja Mani 						 ATH6KL_HOST_MODE_AWAKE);
17726cb3c714SRaja Mani 	return ret;
17736cb3c714SRaja Mani }
17746cb3c714SRaja Mani 
177552d81a68SKalle Valo int ath6kl_cfg80211_suspend(struct ath6kl *ar,
17760f60e9f4SRaja Mani 			    enum ath6kl_cfg_suspend_mode mode,
17770f60e9f4SRaja Mani 			    struct cfg80211_wowlan *wow)
177852d81a68SKalle Valo {
177952d81a68SKalle Valo 	int ret;
178052d81a68SKalle Valo 
178152d81a68SKalle Valo 	switch (mode) {
1782d7c44e0bSRaja Mani 	case ATH6KL_CFG_SUSPEND_WOW:
1783d7c44e0bSRaja Mani 
1784d7c44e0bSRaja Mani 		ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
1785d7c44e0bSRaja Mani 
1786d7c44e0bSRaja Mani 		/* Flush all non control pkts in TX path */
1787d7c44e0bSRaja Mani 		ath6kl_tx_data_cleanup(ar);
1788d7c44e0bSRaja Mani 
1789d7c44e0bSRaja Mani 		ret = ath6kl_wow_suspend(ar, wow);
1790d7c44e0bSRaja Mani 		if (ret) {
1791d7c44e0bSRaja Mani 			ath6kl_err("wow suspend failed: %d\n", ret);
1792d7c44e0bSRaja Mani 			return ret;
1793d7c44e0bSRaja Mani 		}
1794d7c44e0bSRaja Mani 		ar->state = ATH6KL_STATE_WOW;
1795d7c44e0bSRaja Mani 		break;
1796d7c44e0bSRaja Mani 
179752d81a68SKalle Valo 	case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
1798524441e3SRaja Mani 
1799524441e3SRaja Mani 		ath6kl_cfg80211_stop(ar);
1800524441e3SRaja Mani 
180152d81a68SKalle Valo 		/* save the current power mode before enabling power save */
180252d81a68SKalle Valo 		ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
180352d81a68SKalle Valo 
180452d81a68SKalle Valo 		ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
180552d81a68SKalle Valo 		if (ret) {
180652d81a68SKalle Valo 			ath6kl_warn("wmi powermode command failed during suspend: %d\n",
180752d81a68SKalle Valo 				    ret);
180852d81a68SKalle Valo 		}
180952d81a68SKalle Valo 
181076a9fbe2SKalle Valo 		ar->state = ATH6KL_STATE_DEEPSLEEP;
181176a9fbe2SKalle Valo 
181252d81a68SKalle Valo 		break;
1813b4b2a0b1SKalle Valo 
1814b4b2a0b1SKalle Valo 	case ATH6KL_CFG_SUSPEND_CUTPOWER:
1815524441e3SRaja Mani 
1816524441e3SRaja Mani 		ath6kl_cfg80211_stop(ar);
1817524441e3SRaja Mani 
1818b4b2a0b1SKalle Valo 		if (ar->state == ATH6KL_STATE_OFF) {
1819b4b2a0b1SKalle Valo 			ath6kl_dbg(ATH6KL_DBG_SUSPEND,
1820b4b2a0b1SKalle Valo 				   "suspend hw off, no action for cutpower\n");
1821b4b2a0b1SKalle Valo 			break;
1822b4b2a0b1SKalle Valo 		}
1823b4b2a0b1SKalle Valo 
1824b4b2a0b1SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
1825b4b2a0b1SKalle Valo 
1826b4b2a0b1SKalle Valo 		ret = ath6kl_init_hw_stop(ar);
1827b4b2a0b1SKalle Valo 		if (ret) {
1828b4b2a0b1SKalle Valo 			ath6kl_warn("failed to stop hw during suspend: %d\n",
1829b4b2a0b1SKalle Valo 				    ret);
1830b4b2a0b1SKalle Valo 		}
1831b4b2a0b1SKalle Valo 
1832b4b2a0b1SKalle Valo 		ar->state = ATH6KL_STATE_CUTPOWER;
1833b4b2a0b1SKalle Valo 
1834b4b2a0b1SKalle Valo 		break;
1835b4b2a0b1SKalle Valo 
1836b4b2a0b1SKalle Valo 	default:
1837b4b2a0b1SKalle Valo 		break;
183852d81a68SKalle Valo 	}
183952d81a68SKalle Valo 
184052d81a68SKalle Valo 	return 0;
184152d81a68SKalle Valo }
184252d81a68SKalle Valo 
184352d81a68SKalle Valo int ath6kl_cfg80211_resume(struct ath6kl *ar)
184452d81a68SKalle Valo {
184576a9fbe2SKalle Valo 	int ret;
184676a9fbe2SKalle Valo 
184776a9fbe2SKalle Valo 	switch (ar->state) {
1848d7c44e0bSRaja Mani 	case  ATH6KL_STATE_WOW:
1849d7c44e0bSRaja Mani 		ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
1850d7c44e0bSRaja Mani 
1851d7c44e0bSRaja Mani 		ret = ath6kl_wow_resume(ar);
1852d7c44e0bSRaja Mani 		if (ret) {
1853d7c44e0bSRaja Mani 			ath6kl_warn("wow mode resume failed: %d\n", ret);
1854d7c44e0bSRaja Mani 			return ret;
1855d7c44e0bSRaja Mani 		}
1856d7c44e0bSRaja Mani 
1857d7c44e0bSRaja Mani 		ar->state = ATH6KL_STATE_ON;
1858d7c44e0bSRaja Mani 		break;
1859d7c44e0bSRaja Mani 
186076a9fbe2SKalle Valo 	case ATH6KL_STATE_DEEPSLEEP:
186152d81a68SKalle Valo 		if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
186276a9fbe2SKalle Valo 			ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
186376a9fbe2SKalle Valo 						       ar->wmi->saved_pwr_mode);
186476a9fbe2SKalle Valo 			if (ret) {
186576a9fbe2SKalle Valo 				ath6kl_warn("wmi powermode command failed during resume: %d\n",
186676a9fbe2SKalle Valo 					    ret);
186776a9fbe2SKalle Valo 			}
186876a9fbe2SKalle Valo 		}
186976a9fbe2SKalle Valo 
187076a9fbe2SKalle Valo 		ar->state = ATH6KL_STATE_ON;
187176a9fbe2SKalle Valo 
187276a9fbe2SKalle Valo 		break;
187376a9fbe2SKalle Valo 
1874b4b2a0b1SKalle Valo 	case ATH6KL_STATE_CUTPOWER:
1875b4b2a0b1SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
1876b4b2a0b1SKalle Valo 
1877b4b2a0b1SKalle Valo 		ret = ath6kl_init_hw_start(ar);
1878b4b2a0b1SKalle Valo 		if (ret) {
1879b4b2a0b1SKalle Valo 			ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
1880b4b2a0b1SKalle Valo 			return ret;
1881b4b2a0b1SKalle Valo 		}
1882d7c44e0bSRaja Mani 		break;
1883b4b2a0b1SKalle Valo 
188476a9fbe2SKalle Valo 	default:
188576a9fbe2SKalle Valo 		break;
188652d81a68SKalle Valo 	}
188752d81a68SKalle Valo 
188852d81a68SKalle Valo 	return 0;
188952d81a68SKalle Valo }
189052d81a68SKalle Valo 
1891abcb344bSKalle Valo #ifdef CONFIG_PM
189252d81a68SKalle Valo 
189352d81a68SKalle Valo /* hif layer decides what suspend mode to use */
189452d81a68SKalle Valo static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
1895abcb344bSKalle Valo 				 struct cfg80211_wowlan *wow)
1896abcb344bSKalle Valo {
1897abcb344bSKalle Valo 	struct ath6kl *ar = wiphy_priv(wiphy);
1898abcb344bSKalle Valo 
18990f60e9f4SRaja Mani 	return ath6kl_hif_suspend(ar, wow);
1900abcb344bSKalle Valo }
1901aa6cffc1SChilam Ng 
190252d81a68SKalle Valo static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
1903aa6cffc1SChilam Ng {
1904aa6cffc1SChilam Ng 	struct ath6kl *ar = wiphy_priv(wiphy);
1905aa6cffc1SChilam Ng 
1906aa6cffc1SChilam Ng 	return ath6kl_hif_resume(ar);
1907aa6cffc1SChilam Ng }
1908a918fb3cSRaja Mani 
1909a918fb3cSRaja Mani /*
1910a918fb3cSRaja Mani  * FIXME: WOW suspend mode is selected if the host sdio controller supports
1911a918fb3cSRaja Mani  * both sdio irq wake up and keep power. The target pulls sdio data line to
1912a918fb3cSRaja Mani  * wake up the host when WOW pattern matches. This causes sdio irq handler
1913a918fb3cSRaja Mani  * is being called in the host side which internally hits ath6kl's RX path.
1914a918fb3cSRaja Mani  *
1915a918fb3cSRaja Mani  * Since sdio interrupt is not disabled, RX path executes even before
1916a918fb3cSRaja Mani  * the host executes the actual resume operation from PM module.
1917a918fb3cSRaja Mani  *
1918a918fb3cSRaja Mani  * In the current scenario, WOW resume should happen before start processing
1919a918fb3cSRaja Mani  * any data from the target. So It's required to perform WOW resume in RX path.
1920a918fb3cSRaja Mani  * Ideally we should perform WOW resume only in the actual platform
1921a918fb3cSRaja Mani  * resume path. This area needs bit rework to avoid WOW resume in RX path.
1922a918fb3cSRaja Mani  *
1923a918fb3cSRaja Mani  * ath6kl_check_wow_status() is called from ath6kl_rx().
1924a918fb3cSRaja Mani  */
1925a918fb3cSRaja Mani void ath6kl_check_wow_status(struct ath6kl *ar)
1926a918fb3cSRaja Mani {
1927a918fb3cSRaja Mani 	if (ar->state == ATH6KL_STATE_WOW)
1928a918fb3cSRaja Mani 		ath6kl_cfg80211_resume(ar);
1929a918fb3cSRaja Mani }
1930a918fb3cSRaja Mani 
1931a918fb3cSRaja Mani #else
1932a918fb3cSRaja Mani 
1933a918fb3cSRaja Mani void ath6kl_check_wow_status(struct ath6kl *ar)
1934a918fb3cSRaja Mani {
1935a918fb3cSRaja Mani }
1936abcb344bSKalle Valo #endif
1937abcb344bSKalle Valo 
19386a7c9badSJouni Malinen static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
19396a7c9badSJouni Malinen 			      struct ieee80211_channel *chan,
19406a7c9badSJouni Malinen 			      enum nl80211_channel_type channel_type)
19416a7c9badSJouni Malinen {
1942cf5333d7SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
19436a7c9badSJouni Malinen 
1944990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
19456a7c9badSJouni Malinen 		return -EIO;
19466a7c9badSJouni Malinen 
19476a7c9badSJouni Malinen 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
19486a7c9badSJouni Malinen 		   __func__, chan->center_freq, chan->hw_value);
1949cf5333d7SVasanthakumar Thiagarajan 	vif->next_chan = chan->center_freq;
19506a7c9badSJouni Malinen 
19516a7c9badSJouni Malinen 	return 0;
19526a7c9badSJouni Malinen }
19536a7c9badSJouni Malinen 
19548bdfbf40SJouni Malinen static bool ath6kl_is_p2p_ie(const u8 *pos)
19558bdfbf40SJouni Malinen {
19568bdfbf40SJouni Malinen 	return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
19578bdfbf40SJouni Malinen 		pos[2] == 0x50 && pos[3] == 0x6f &&
19588bdfbf40SJouni Malinen 		pos[4] == 0x9a && pos[5] == 0x09;
19598bdfbf40SJouni Malinen }
19608bdfbf40SJouni Malinen 
1961334234b5SVasanthakumar Thiagarajan static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
1962334234b5SVasanthakumar Thiagarajan 					const u8 *ies, size_t ies_len)
19638bdfbf40SJouni Malinen {
1964334234b5SVasanthakumar Thiagarajan 	struct ath6kl *ar = vif->ar;
19658bdfbf40SJouni Malinen 	const u8 *pos;
19668bdfbf40SJouni Malinen 	u8 *buf = NULL;
19678bdfbf40SJouni Malinen 	size_t len = 0;
19688bdfbf40SJouni Malinen 	int ret;
19698bdfbf40SJouni Malinen 
19708bdfbf40SJouni Malinen 	/*
19718bdfbf40SJouni Malinen 	 * Filter out P2P IE(s) since they will be included depending on
19728bdfbf40SJouni Malinen 	 * the Probe Request frame in ath6kl_send_go_probe_resp().
19738bdfbf40SJouni Malinen 	 */
19748bdfbf40SJouni Malinen 
19758bdfbf40SJouni Malinen 	if (ies && ies_len) {
19768bdfbf40SJouni Malinen 		buf = kmalloc(ies_len, GFP_KERNEL);
19778bdfbf40SJouni Malinen 		if (buf == NULL)
19788bdfbf40SJouni Malinen 			return -ENOMEM;
19798bdfbf40SJouni Malinen 		pos = ies;
19808bdfbf40SJouni Malinen 		while (pos + 1 < ies + ies_len) {
19818bdfbf40SJouni Malinen 			if (pos + 2 + pos[1] > ies + ies_len)
19828bdfbf40SJouni Malinen 				break;
19838bdfbf40SJouni Malinen 			if (!ath6kl_is_p2p_ie(pos)) {
19848bdfbf40SJouni Malinen 				memcpy(buf + len, pos, 2 + pos[1]);
19858bdfbf40SJouni Malinen 				len += 2 + pos[1];
19868bdfbf40SJouni Malinen 			}
19878bdfbf40SJouni Malinen 			pos += 2 + pos[1];
19888bdfbf40SJouni Malinen 		}
19898bdfbf40SJouni Malinen 	}
19908bdfbf40SJouni Malinen 
1991334234b5SVasanthakumar Thiagarajan 	ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
1992334234b5SVasanthakumar Thiagarajan 				       WMI_FRAME_PROBE_RESP, buf, len);
19938bdfbf40SJouni Malinen 	kfree(buf);
19948bdfbf40SJouni Malinen 	return ret;
19958bdfbf40SJouni Malinen }
19968bdfbf40SJouni Malinen 
19976a7c9badSJouni Malinen static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
19986a7c9badSJouni Malinen 			    struct beacon_parameters *info, bool add)
19996a7c9badSJouni Malinen {
20006a7c9badSJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
20013450334fSVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
20026a7c9badSJouni Malinen 	struct ieee80211_mgmt *mgmt;
20036a7c9badSJouni Malinen 	u8 *ies;
20046a7c9badSJouni Malinen 	int ies_len;
20056a7c9badSJouni Malinen 	struct wmi_connect_cmd p;
20066a7c9badSJouni Malinen 	int res;
2007be5abaafSVasanthakumar Thiagarajan 	int i, ret;
20086a7c9badSJouni Malinen 
20096a7c9badSJouni Malinen 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
20106a7c9badSJouni Malinen 
2011990bd915SVasanthakumar Thiagarajan 	if (!ath6kl_cfg80211_ready(vif))
20126a7c9badSJouni Malinen 		return -EIO;
20136a7c9badSJouni Malinen 
2014f5938f24SVasanthakumar Thiagarajan 	if (vif->next_mode != AP_NETWORK)
20156a7c9badSJouni Malinen 		return -EOPNOTSUPP;
20166a7c9badSJouni Malinen 
20176a7c9badSJouni Malinen 	if (info->beacon_ies) {
2018334234b5SVasanthakumar Thiagarajan 		res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2019334234b5SVasanthakumar Thiagarajan 					       WMI_FRAME_BEACON,
20206a7c9badSJouni Malinen 					       info->beacon_ies,
20216a7c9badSJouni Malinen 					       info->beacon_ies_len);
20226a7c9badSJouni Malinen 		if (res)
20236a7c9badSJouni Malinen 			return res;
20246a7c9badSJouni Malinen 	}
20256a7c9badSJouni Malinen 	if (info->proberesp_ies) {
2026334234b5SVasanthakumar Thiagarajan 		res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
20276a7c9badSJouni Malinen 						   info->proberesp_ies_len);
20286a7c9badSJouni Malinen 		if (res)
20296a7c9badSJouni Malinen 			return res;
20306a7c9badSJouni Malinen 	}
20316a7c9badSJouni Malinen 	if (info->assocresp_ies) {
2032334234b5SVasanthakumar Thiagarajan 		res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2033334234b5SVasanthakumar Thiagarajan 					       WMI_FRAME_ASSOC_RESP,
20346a7c9badSJouni Malinen 					       info->assocresp_ies,
20356a7c9badSJouni Malinen 					       info->assocresp_ies_len);
20366a7c9badSJouni Malinen 		if (res)
20376a7c9badSJouni Malinen 			return res;
20386a7c9badSJouni Malinen 	}
20396a7c9badSJouni Malinen 
20406a7c9badSJouni Malinen 	if (!add)
20416a7c9badSJouni Malinen 		return 0;
20426a7c9badSJouni Malinen 
20439a5b1318SJouni Malinen 	ar->ap_mode_bkey.valid = false;
20449a5b1318SJouni Malinen 
20456a7c9badSJouni Malinen 	/* TODO:
20466a7c9badSJouni Malinen 	 * info->interval
20476a7c9badSJouni Malinen 	 * info->dtim_period
20486a7c9badSJouni Malinen 	 */
20496a7c9badSJouni Malinen 
20506a7c9badSJouni Malinen 	if (info->head == NULL)
20516a7c9badSJouni Malinen 		return -EINVAL;
20526a7c9badSJouni Malinen 	mgmt = (struct ieee80211_mgmt *) info->head;
20536a7c9badSJouni Malinen 	ies = mgmt->u.beacon.variable;
20546a7c9badSJouni Malinen 	if (ies > info->head + info->head_len)
20556a7c9badSJouni Malinen 		return -EINVAL;
20566a7c9badSJouni Malinen 	ies_len = info->head + info->head_len - ies;
20576a7c9badSJouni Malinen 
20586a7c9badSJouni Malinen 	if (info->ssid == NULL)
20596a7c9badSJouni Malinen 		return -EINVAL;
20603450334fSVasanthakumar Thiagarajan 	memcpy(vif->ssid, info->ssid, info->ssid_len);
20613450334fSVasanthakumar Thiagarajan 	vif->ssid_len = info->ssid_len;
20626a7c9badSJouni Malinen 	if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
20636a7c9badSJouni Malinen 		return -EOPNOTSUPP; /* TODO */
20646a7c9badSJouni Malinen 
2065be5abaafSVasanthakumar Thiagarajan 	ret = ath6kl_set_auth_type(vif, info->auth_type);
2066be5abaafSVasanthakumar Thiagarajan 	if (ret)
2067be5abaafSVasanthakumar Thiagarajan 		return ret;
20686a7c9badSJouni Malinen 
20696a7c9badSJouni Malinen 	memset(&p, 0, sizeof(p));
20706a7c9badSJouni Malinen 
20716a7c9badSJouni Malinen 	for (i = 0; i < info->crypto.n_akm_suites; i++) {
20726a7c9badSJouni Malinen 		switch (info->crypto.akm_suites[i]) {
20736a7c9badSJouni Malinen 		case WLAN_AKM_SUITE_8021X:
20746a7c9badSJouni Malinen 			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
20756a7c9badSJouni Malinen 				p.auth_mode |= WPA_AUTH;
20766a7c9badSJouni Malinen 			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
20776a7c9badSJouni Malinen 				p.auth_mode |= WPA2_AUTH;
20786a7c9badSJouni Malinen 			break;
20796a7c9badSJouni Malinen 		case WLAN_AKM_SUITE_PSK:
20806a7c9badSJouni Malinen 			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
20816a7c9badSJouni Malinen 				p.auth_mode |= WPA_PSK_AUTH;
20826a7c9badSJouni Malinen 			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
20836a7c9badSJouni Malinen 				p.auth_mode |= WPA2_PSK_AUTH;
20846a7c9badSJouni Malinen 			break;
20856a7c9badSJouni Malinen 		}
20866a7c9badSJouni Malinen 	}
20876a7c9badSJouni Malinen 	if (p.auth_mode == 0)
20886a7c9badSJouni Malinen 		p.auth_mode = NONE_AUTH;
20893450334fSVasanthakumar Thiagarajan 	vif->auth_mode = p.auth_mode;
20906a7c9badSJouni Malinen 
20916a7c9badSJouni Malinen 	for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
20926a7c9badSJouni Malinen 		switch (info->crypto.ciphers_pairwise[i]) {
20936a7c9badSJouni Malinen 		case WLAN_CIPHER_SUITE_WEP40:
20946a7c9badSJouni Malinen 		case WLAN_CIPHER_SUITE_WEP104:
20956a7c9badSJouni Malinen 			p.prwise_crypto_type |= WEP_CRYPT;
20966a7c9badSJouni Malinen 			break;
20976a7c9badSJouni Malinen 		case WLAN_CIPHER_SUITE_TKIP:
20986a7c9badSJouni Malinen 			p.prwise_crypto_type |= TKIP_CRYPT;
20996a7c9badSJouni Malinen 			break;
21006a7c9badSJouni Malinen 		case WLAN_CIPHER_SUITE_CCMP:
21016a7c9badSJouni Malinen 			p.prwise_crypto_type |= AES_CRYPT;
21026a7c9badSJouni Malinen 			break;
2103b8214df1SDai Shuibing 		case WLAN_CIPHER_SUITE_SMS4:
2104b8214df1SDai Shuibing 			p.prwise_crypto_type |= WAPI_CRYPT;
2105b8214df1SDai Shuibing 			break;
21066a7c9badSJouni Malinen 		}
21076a7c9badSJouni Malinen 	}
2108229ed6b5SEdward Lu 	if (p.prwise_crypto_type == 0) {
21096a7c9badSJouni Malinen 		p.prwise_crypto_type = NONE_CRYPT;
2110240d2799SVasanthakumar Thiagarajan 		ath6kl_set_cipher(vif, 0, true);
2111229ed6b5SEdward Lu 	} else if (info->crypto.n_ciphers_pairwise == 1)
2112240d2799SVasanthakumar Thiagarajan 		ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
21136a7c9badSJouni Malinen 
21146a7c9badSJouni Malinen 	switch (info->crypto.cipher_group) {
21156a7c9badSJouni Malinen 	case WLAN_CIPHER_SUITE_WEP40:
21166a7c9badSJouni Malinen 	case WLAN_CIPHER_SUITE_WEP104:
21176a7c9badSJouni Malinen 		p.grp_crypto_type = WEP_CRYPT;
21186a7c9badSJouni Malinen 		break;
21196a7c9badSJouni Malinen 	case WLAN_CIPHER_SUITE_TKIP:
21206a7c9badSJouni Malinen 		p.grp_crypto_type = TKIP_CRYPT;
21216a7c9badSJouni Malinen 		break;
21226a7c9badSJouni Malinen 	case WLAN_CIPHER_SUITE_CCMP:
21236a7c9badSJouni Malinen 		p.grp_crypto_type = AES_CRYPT;
21246a7c9badSJouni Malinen 		break;
2125b8214df1SDai Shuibing 	case WLAN_CIPHER_SUITE_SMS4:
2126b8214df1SDai Shuibing 		p.grp_crypto_type = WAPI_CRYPT;
2127b8214df1SDai Shuibing 		break;
21286a7c9badSJouni Malinen 	default:
21296a7c9badSJouni Malinen 		p.grp_crypto_type = NONE_CRYPT;
21306a7c9badSJouni Malinen 		break;
21316a7c9badSJouni Malinen 	}
2132240d2799SVasanthakumar Thiagarajan 	ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
21336a7c9badSJouni Malinen 
21346a7c9badSJouni Malinen 	p.nw_type = AP_NETWORK;
2135f5938f24SVasanthakumar Thiagarajan 	vif->nw_type = vif->next_mode;
21366a7c9badSJouni Malinen 
21373450334fSVasanthakumar Thiagarajan 	p.ssid_len = vif->ssid_len;
21383450334fSVasanthakumar Thiagarajan 	memcpy(p.ssid, vif->ssid, vif->ssid_len);
21393450334fSVasanthakumar Thiagarajan 	p.dot11_auth_mode = vif->dot11_auth_mode;
2140cf5333d7SVasanthakumar Thiagarajan 	p.ch = cpu_to_le16(vif->next_chan);
21416a7c9badSJouni Malinen 
2142334234b5SVasanthakumar Thiagarajan 	res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
21439a5b1318SJouni Malinen 	if (res < 0)
21449a5b1318SJouni Malinen 		return res;
21459a5b1318SJouni Malinen 
21469a5b1318SJouni Malinen 	return 0;
21476a7c9badSJouni Malinen }
21486a7c9badSJouni Malinen 
21496a7c9badSJouni Malinen static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
21506a7c9badSJouni Malinen 			     struct beacon_parameters *info)
21516a7c9badSJouni Malinen {
21526a7c9badSJouni Malinen 	return ath6kl_ap_beacon(wiphy, dev, info, true);
21536a7c9badSJouni Malinen }
21546a7c9badSJouni Malinen 
21556a7c9badSJouni Malinen static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
21566a7c9badSJouni Malinen 			     struct beacon_parameters *info)
21576a7c9badSJouni Malinen {
21586a7c9badSJouni Malinen 	return ath6kl_ap_beacon(wiphy, dev, info, false);
21596a7c9badSJouni Malinen }
21606a7c9badSJouni Malinen 
21616a7c9badSJouni Malinen static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
21626a7c9badSJouni Malinen {
21636a7c9badSJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
216459c98449SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
21656a7c9badSJouni Malinen 
2166f5938f24SVasanthakumar Thiagarajan 	if (vif->nw_type != AP_NETWORK)
21676a7c9badSJouni Malinen 		return -EOPNOTSUPP;
216859c98449SVasanthakumar Thiagarajan 	if (!test_bit(CONNECTED, &vif->flags))
21696a7c9badSJouni Malinen 		return -ENOTCONN;
21706a7c9badSJouni Malinen 
2171334234b5SVasanthakumar Thiagarajan 	ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
217259c98449SVasanthakumar Thiagarajan 	clear_bit(CONNECTED, &vif->flags);
21736a7c9badSJouni Malinen 
21746a7c9badSJouni Malinen 	return 0;
21756a7c9badSJouni Malinen }
21766a7c9badSJouni Malinen 
217723875136SJouni Malinen static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
217823875136SJouni Malinen 				 u8 *mac, struct station_parameters *params)
217923875136SJouni Malinen {
218023875136SJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
2181f5938f24SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
218223875136SJouni Malinen 
2183f5938f24SVasanthakumar Thiagarajan 	if (vif->nw_type != AP_NETWORK)
218423875136SJouni Malinen 		return -EOPNOTSUPP;
218523875136SJouni Malinen 
218623875136SJouni Malinen 	/* Use this only for authorizing/unauthorizing a station */
218723875136SJouni Malinen 	if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
218823875136SJouni Malinen 		return -EOPNOTSUPP;
218923875136SJouni Malinen 
219023875136SJouni Malinen 	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
2191334234b5SVasanthakumar Thiagarajan 		return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2192334234b5SVasanthakumar Thiagarajan 					      WMI_AP_MLME_AUTHORIZE, mac, 0);
2193334234b5SVasanthakumar Thiagarajan 	return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2194334234b5SVasanthakumar Thiagarajan 				      WMI_AP_MLME_UNAUTHORIZE, mac, 0);
219523875136SJouni Malinen }
219623875136SJouni Malinen 
219763fa1e0cSJouni Malinen static int ath6kl_remain_on_channel(struct wiphy *wiphy,
219863fa1e0cSJouni Malinen 				    struct net_device *dev,
219963fa1e0cSJouni Malinen 				    struct ieee80211_channel *chan,
220063fa1e0cSJouni Malinen 				    enum nl80211_channel_type channel_type,
220163fa1e0cSJouni Malinen 				    unsigned int duration,
220263fa1e0cSJouni Malinen 				    u64 *cookie)
220363fa1e0cSJouni Malinen {
220463fa1e0cSJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
2205334234b5SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
22061052261eSJouni Malinen 	u32 id;
220763fa1e0cSJouni Malinen 
220863fa1e0cSJouni Malinen 	/* TODO: if already pending or ongoing remain-on-channel,
220963fa1e0cSJouni Malinen 	 * return -EBUSY */
22101052261eSJouni Malinen 	id = ++vif->last_roc_id;
22111052261eSJouni Malinen 	if (id == 0) {
22121052261eSJouni Malinen 		/* Do not use 0 as the cookie value */
22131052261eSJouni Malinen 		id = ++vif->last_roc_id;
22141052261eSJouni Malinen 	}
22151052261eSJouni Malinen 	*cookie = id;
221663fa1e0cSJouni Malinen 
2217334234b5SVasanthakumar Thiagarajan 	return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2218334234b5SVasanthakumar Thiagarajan 					     chan->center_freq, duration);
221963fa1e0cSJouni Malinen }
222063fa1e0cSJouni Malinen 
222163fa1e0cSJouni Malinen static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
222263fa1e0cSJouni Malinen 					   struct net_device *dev,
222363fa1e0cSJouni Malinen 					   u64 cookie)
222463fa1e0cSJouni Malinen {
222563fa1e0cSJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
2226334234b5SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
222763fa1e0cSJouni Malinen 
22281052261eSJouni Malinen 	if (cookie != vif->last_roc_id)
222963fa1e0cSJouni Malinen 		return -ENOENT;
22301052261eSJouni Malinen 	vif->last_cancel_roc_id = cookie;
223163fa1e0cSJouni Malinen 
2232334234b5SVasanthakumar Thiagarajan 	return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
223363fa1e0cSJouni Malinen }
223463fa1e0cSJouni Malinen 
2235334234b5SVasanthakumar Thiagarajan static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2236334234b5SVasanthakumar Thiagarajan 				     const u8 *buf, size_t len,
2237334234b5SVasanthakumar Thiagarajan 				     unsigned int freq)
22388bdfbf40SJouni Malinen {
2239334234b5SVasanthakumar Thiagarajan 	struct ath6kl *ar = vif->ar;
22408bdfbf40SJouni Malinen 	const u8 *pos;
22418bdfbf40SJouni Malinen 	u8 *p2p;
22428bdfbf40SJouni Malinen 	int p2p_len;
22438bdfbf40SJouni Malinen 	int ret;
22448bdfbf40SJouni Malinen 	const struct ieee80211_mgmt *mgmt;
22458bdfbf40SJouni Malinen 
22468bdfbf40SJouni Malinen 	mgmt = (const struct ieee80211_mgmt *) buf;
22478bdfbf40SJouni Malinen 
22488bdfbf40SJouni Malinen 	/* Include P2P IE(s) from the frame generated in user space. */
22498bdfbf40SJouni Malinen 
22508bdfbf40SJouni Malinen 	p2p = kmalloc(len, GFP_KERNEL);
22518bdfbf40SJouni Malinen 	if (p2p == NULL)
22528bdfbf40SJouni Malinen 		return -ENOMEM;
22538bdfbf40SJouni Malinen 	p2p_len = 0;
22548bdfbf40SJouni Malinen 
22558bdfbf40SJouni Malinen 	pos = mgmt->u.probe_resp.variable;
22568bdfbf40SJouni Malinen 	while (pos + 1 < buf + len) {
22578bdfbf40SJouni Malinen 		if (pos + 2 + pos[1] > buf + len)
22588bdfbf40SJouni Malinen 			break;
22598bdfbf40SJouni Malinen 		if (ath6kl_is_p2p_ie(pos)) {
22608bdfbf40SJouni Malinen 			memcpy(p2p + p2p_len, pos, 2 + pos[1]);
22618bdfbf40SJouni Malinen 			p2p_len += 2 + pos[1];
22628bdfbf40SJouni Malinen 		}
22638bdfbf40SJouni Malinen 		pos += 2 + pos[1];
22648bdfbf40SJouni Malinen 	}
22658bdfbf40SJouni Malinen 
2266334234b5SVasanthakumar Thiagarajan 	ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2267334234b5SVasanthakumar Thiagarajan 						 mgmt->da, p2p, p2p_len);
22688bdfbf40SJouni Malinen 	kfree(p2p);
22698bdfbf40SJouni Malinen 	return ret;
22708bdfbf40SJouni Malinen }
22718bdfbf40SJouni Malinen 
22728a6c8060SJouni Malinen static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
22738a6c8060SJouni Malinen 			  struct ieee80211_channel *chan, bool offchan,
22748a6c8060SJouni Malinen 			  enum nl80211_channel_type channel_type,
22758a6c8060SJouni Malinen 			  bool channel_type_valid, unsigned int wait,
2276e247bd90SJohannes Berg 			  const u8 *buf, size_t len, bool no_cck,
2277e247bd90SJohannes Berg 			  bool dont_wait_for_ack, u64 *cookie)
22788a6c8060SJouni Malinen {
22798a6c8060SJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
228059c98449SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
22818a6c8060SJouni Malinen 	u32 id;
22828bdfbf40SJouni Malinen 	const struct ieee80211_mgmt *mgmt;
22838bdfbf40SJouni Malinen 
22848bdfbf40SJouni Malinen 	mgmt = (const struct ieee80211_mgmt *) buf;
22858bdfbf40SJouni Malinen 	if (buf + len >= mgmt->u.probe_resp.variable &&
2286f5938f24SVasanthakumar Thiagarajan 	    vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
22878bdfbf40SJouni Malinen 	    ieee80211_is_probe_resp(mgmt->frame_control)) {
22888bdfbf40SJouni Malinen 		/*
22898bdfbf40SJouni Malinen 		 * Send Probe Response frame in AP mode using a separate WMI
22908bdfbf40SJouni Malinen 		 * command to allow the target to fill in the generic IEs.
22918bdfbf40SJouni Malinen 		 */
22928bdfbf40SJouni Malinen 		*cookie = 0; /* TX status not supported */
2293334234b5SVasanthakumar Thiagarajan 		return ath6kl_send_go_probe_resp(vif, buf, len,
22948bdfbf40SJouni Malinen 						 chan->center_freq);
22958bdfbf40SJouni Malinen 	}
22968a6c8060SJouni Malinen 
2297cf5333d7SVasanthakumar Thiagarajan 	id = vif->send_action_id++;
22988a6c8060SJouni Malinen 	if (id == 0) {
22998a6c8060SJouni Malinen 		/*
23008a6c8060SJouni Malinen 		 * 0 is a reserved value in the WMI command and shall not be
23018a6c8060SJouni Malinen 		 * used for the command.
23028a6c8060SJouni Malinen 		 */
2303cf5333d7SVasanthakumar Thiagarajan 		id = vif->send_action_id++;
23048a6c8060SJouni Malinen 	}
23058a6c8060SJouni Malinen 
23068a6c8060SJouni Malinen 	*cookie = id;
2307334234b5SVasanthakumar Thiagarajan 	return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
2308334234b5SVasanthakumar Thiagarajan 					  chan->center_freq, wait,
23098a6c8060SJouni Malinen 					  buf, len);
23108a6c8060SJouni Malinen }
23118a6c8060SJouni Malinen 
2312ae32c30aSJouni Malinen static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2313ae32c30aSJouni Malinen 				       struct net_device *dev,
2314ae32c30aSJouni Malinen 				       u16 frame_type, bool reg)
2315ae32c30aSJouni Malinen {
2316cf5333d7SVasanthakumar Thiagarajan 	struct ath6kl_vif *vif = netdev_priv(dev);
2317ae32c30aSJouni Malinen 
2318ae32c30aSJouni Malinen 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2319ae32c30aSJouni Malinen 		   __func__, frame_type, reg);
2320ae32c30aSJouni Malinen 	if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2321ae32c30aSJouni Malinen 		/*
2322ae32c30aSJouni Malinen 		 * Note: This notification callback is not allowed to sleep, so
2323ae32c30aSJouni Malinen 		 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2324ae32c30aSJouni Malinen 		 * hardcode target to report Probe Request frames all the time.
2325ae32c30aSJouni Malinen 		 */
2326cf5333d7SVasanthakumar Thiagarajan 		vif->probe_req_report = reg;
2327ae32c30aSJouni Malinen 	}
2328ae32c30aSJouni Malinen }
2329ae32c30aSJouni Malinen 
2330f80574aeSJouni Malinen static const struct ieee80211_txrx_stypes
2331f80574aeSJouni Malinen ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2332f80574aeSJouni Malinen 	[NL80211_IFTYPE_STATION] = {
2333f80574aeSJouni Malinen 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2334f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2335f80574aeSJouni Malinen 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2336f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2337f80574aeSJouni Malinen 	},
2338f80574aeSJouni Malinen 	[NL80211_IFTYPE_P2P_CLIENT] = {
2339f80574aeSJouni Malinen 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2340f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2341f80574aeSJouni Malinen 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2342f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2343f80574aeSJouni Malinen 	},
2344f80574aeSJouni Malinen 	[NL80211_IFTYPE_P2P_GO] = {
2345f80574aeSJouni Malinen 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2346f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2347f80574aeSJouni Malinen 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2348f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2349f80574aeSJouni Malinen 	},
2350f80574aeSJouni Malinen };
2351f80574aeSJouni Malinen 
2352bdcd8170SKalle Valo static struct cfg80211_ops ath6kl_cfg80211_ops = {
235355055976SVasanthakumar Thiagarajan 	.add_virtual_intf = ath6kl_cfg80211_add_iface,
235455055976SVasanthakumar Thiagarajan 	.del_virtual_intf = ath6kl_cfg80211_del_iface,
2355bdcd8170SKalle Valo 	.change_virtual_intf = ath6kl_cfg80211_change_iface,
2356bdcd8170SKalle Valo 	.scan = ath6kl_cfg80211_scan,
2357bdcd8170SKalle Valo 	.connect = ath6kl_cfg80211_connect,
2358bdcd8170SKalle Valo 	.disconnect = ath6kl_cfg80211_disconnect,
2359bdcd8170SKalle Valo 	.add_key = ath6kl_cfg80211_add_key,
2360bdcd8170SKalle Valo 	.get_key = ath6kl_cfg80211_get_key,
2361bdcd8170SKalle Valo 	.del_key = ath6kl_cfg80211_del_key,
2362bdcd8170SKalle Valo 	.set_default_key = ath6kl_cfg80211_set_default_key,
2363bdcd8170SKalle Valo 	.set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
2364bdcd8170SKalle Valo 	.set_tx_power = ath6kl_cfg80211_set_txpower,
2365bdcd8170SKalle Valo 	.get_tx_power = ath6kl_cfg80211_get_txpower,
2366bdcd8170SKalle Valo 	.set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
2367bdcd8170SKalle Valo 	.join_ibss = ath6kl_cfg80211_join_ibss,
2368bdcd8170SKalle Valo 	.leave_ibss = ath6kl_cfg80211_leave_ibss,
2369bdcd8170SKalle Valo 	.get_station = ath6kl_get_station,
2370bdcd8170SKalle Valo 	.set_pmksa = ath6kl_set_pmksa,
2371bdcd8170SKalle Valo 	.del_pmksa = ath6kl_del_pmksa,
2372bdcd8170SKalle Valo 	.flush_pmksa = ath6kl_flush_pmksa,
2373003353b0SKalle Valo 	CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
2374abcb344bSKalle Valo #ifdef CONFIG_PM
237552d81a68SKalle Valo 	.suspend = __ath6kl_cfg80211_suspend,
237652d81a68SKalle Valo 	.resume = __ath6kl_cfg80211_resume,
2377abcb344bSKalle Valo #endif
23786a7c9badSJouni Malinen 	.set_channel = ath6kl_set_channel,
23796a7c9badSJouni Malinen 	.add_beacon = ath6kl_add_beacon,
23806a7c9badSJouni Malinen 	.set_beacon = ath6kl_set_beacon,
23816a7c9badSJouni Malinen 	.del_beacon = ath6kl_del_beacon,
238223875136SJouni Malinen 	.change_station = ath6kl_change_station,
238363fa1e0cSJouni Malinen 	.remain_on_channel = ath6kl_remain_on_channel,
238463fa1e0cSJouni Malinen 	.cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
23858a6c8060SJouni Malinen 	.mgmt_tx = ath6kl_mgmt_tx,
2386ae32c30aSJouni Malinen 	.mgmt_frame_register = ath6kl_mgmt_frame_register,
2387bdcd8170SKalle Valo };
2388bdcd8170SKalle Valo 
2389ec4b7f60SKalle Valo void ath6kl_cfg80211_stop(struct ath6kl *ar)
2390ec4b7f60SKalle Valo {
2391ec4b7f60SKalle Valo 	struct ath6kl_vif *vif;
2392ec4b7f60SKalle Valo 
2393ec4b7f60SKalle Valo 	/* FIXME: for multi vif */
2394ec4b7f60SKalle Valo 	vif = ath6kl_vif_first(ar);
2395ec4b7f60SKalle Valo 	if (!vif) {
2396ec4b7f60SKalle Valo 		/* save the current power mode before enabling power save */
2397ec4b7f60SKalle Valo 		ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2398ec4b7f60SKalle Valo 
2399ec4b7f60SKalle Valo 		if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
2400ec4b7f60SKalle Valo 			ath6kl_warn("ath6kl_deep_sleep_enable: "
2401ec4b7f60SKalle Valo 				    "wmi_powermode_cmd failed\n");
2402ec4b7f60SKalle Valo 		return;
2403ec4b7f60SKalle Valo 	}
2404ec4b7f60SKalle Valo 
2405ec4b7f60SKalle Valo 	switch (vif->sme_state) {
2406ec4b7f60SKalle Valo 	case SME_CONNECTING:
2407ec4b7f60SKalle Valo 		cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
2408ec4b7f60SKalle Valo 					NULL, 0,
2409ec4b7f60SKalle Valo 					WLAN_STATUS_UNSPECIFIED_FAILURE,
2410ec4b7f60SKalle Valo 					GFP_KERNEL);
2411ec4b7f60SKalle Valo 		break;
2412ec4b7f60SKalle Valo 	case SME_CONNECTED:
2413ec4b7f60SKalle Valo 	default:
2414ec4b7f60SKalle Valo 		/*
2415ec4b7f60SKalle Valo 		 * FIXME: oddly enough smeState is in DISCONNECTED during
2416ec4b7f60SKalle Valo 		 * suspend, why? Need to send disconnected event in that
2417ec4b7f60SKalle Valo 		 * state.
2418ec4b7f60SKalle Valo 		 */
2419ec4b7f60SKalle Valo 		cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
2420ec4b7f60SKalle Valo 		break;
2421ec4b7f60SKalle Valo 	}
2422ec4b7f60SKalle Valo 
2423ec4b7f60SKalle Valo 	if (test_bit(CONNECTED, &vif->flags) ||
2424ec4b7f60SKalle Valo 	    test_bit(CONNECT_PEND, &vif->flags))
2425ec4b7f60SKalle Valo 		ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
2426ec4b7f60SKalle Valo 
2427ec4b7f60SKalle Valo 	vif->sme_state = SME_DISCONNECTED;
24281f405255SKalle Valo 	clear_bit(CONNECTED, &vif->flags);
24291f405255SKalle Valo 	clear_bit(CONNECT_PEND, &vif->flags);
2430ec4b7f60SKalle Valo 
2431ec4b7f60SKalle Valo 	/* disable scanning */
2432ec4b7f60SKalle Valo 	if (ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0,
2433ec4b7f60SKalle Valo 				      0, 0, 0, 0, 0, 0, 0) != 0)
2434ec4b7f60SKalle Valo 		printk(KERN_WARNING "ath6kl: failed to disable scan "
2435ec4b7f60SKalle Valo 		       "during suspend\n");
2436ec4b7f60SKalle Valo 
2437ec4b7f60SKalle Valo 	ath6kl_cfg80211_scan_complete_event(vif, true);
2438ec4b7f60SKalle Valo }
2439ec4b7f60SKalle Valo 
24408dafb70eSVasanthakumar Thiagarajan struct ath6kl *ath6kl_core_alloc(struct device *dev)
2441bdcd8170SKalle Valo {
24426bbc7c35SJouni Malinen 	struct ath6kl *ar;
2443be98e3a4SVasanthakumar Thiagarajan 	struct wiphy *wiphy;
24448dafb70eSVasanthakumar Thiagarajan 	u8 ctr;
2445bdcd8170SKalle Valo 
2446bdcd8170SKalle Valo 	/* create a new wiphy for use with cfg80211 */
2447be98e3a4SVasanthakumar Thiagarajan 	wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
24488dafb70eSVasanthakumar Thiagarajan 
2449be98e3a4SVasanthakumar Thiagarajan 	if (!wiphy) {
2450bdcd8170SKalle Valo 		ath6kl_err("couldn't allocate wiphy device\n");
2451bdcd8170SKalle Valo 		return NULL;
2452bdcd8170SKalle Valo 	}
2453bdcd8170SKalle Valo 
2454be98e3a4SVasanthakumar Thiagarajan 	ar = wiphy_priv(wiphy);
24556bbc7c35SJouni Malinen 	ar->p2p = !!ath6kl_p2p;
2456be98e3a4SVasanthakumar Thiagarajan 	ar->wiphy = wiphy;
24578dafb70eSVasanthakumar Thiagarajan 	ar->dev = dev;
24588dafb70eSVasanthakumar Thiagarajan 
245971f96ee6SKalle Valo 	ar->vif_max = 1;
246071f96ee6SKalle Valo 
24613226f68aSVasanthakumar Thiagarajan 	ar->max_norm_iface = 1;
24623226f68aSVasanthakumar Thiagarajan 
24638dafb70eSVasanthakumar Thiagarajan 	spin_lock_init(&ar->lock);
24648dafb70eSVasanthakumar Thiagarajan 	spin_lock_init(&ar->mcastpsq_lock);
2465990bd915SVasanthakumar Thiagarajan 	spin_lock_init(&ar->list_lock);
24668dafb70eSVasanthakumar Thiagarajan 
24678dafb70eSVasanthakumar Thiagarajan 	init_waitqueue_head(&ar->event_wq);
24688dafb70eSVasanthakumar Thiagarajan 	sema_init(&ar->sem, 1);
24698dafb70eSVasanthakumar Thiagarajan 
24708dafb70eSVasanthakumar Thiagarajan 	INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue);
2471990bd915SVasanthakumar Thiagarajan 	INIT_LIST_HEAD(&ar->vif_list);
24728dafb70eSVasanthakumar Thiagarajan 
24738dafb70eSVasanthakumar Thiagarajan 	clear_bit(WMI_ENABLED, &ar->flag);
24748dafb70eSVasanthakumar Thiagarajan 	clear_bit(SKIP_SCAN, &ar->flag);
24758dafb70eSVasanthakumar Thiagarajan 	clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
24768dafb70eSVasanthakumar Thiagarajan 
24778dafb70eSVasanthakumar Thiagarajan 	ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL;
24788dafb70eSVasanthakumar Thiagarajan 	ar->listen_intvl_b = 0;
24798dafb70eSVasanthakumar Thiagarajan 	ar->tx_pwr = 0;
24808dafb70eSVasanthakumar Thiagarajan 
24818dafb70eSVasanthakumar Thiagarajan 	ar->intra_bss = 1;
24828dafb70eSVasanthakumar Thiagarajan 	memset(&ar->sc_params, 0, sizeof(ar->sc_params));
24838dafb70eSVasanthakumar Thiagarajan 	ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT;
24848dafb70eSVasanthakumar Thiagarajan 	ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS;
24858dafb70eSVasanthakumar Thiagarajan 	ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
24868dafb70eSVasanthakumar Thiagarajan 
248776a9fbe2SKalle Valo 	ar->state = ATH6KL_STATE_OFF;
248876a9fbe2SKalle Valo 
24898dafb70eSVasanthakumar Thiagarajan 	memset((u8 *)ar->sta_list, 0,
24908dafb70eSVasanthakumar Thiagarajan 	       AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
24918dafb70eSVasanthakumar Thiagarajan 
24928dafb70eSVasanthakumar Thiagarajan 	/* Init the PS queues */
24938dafb70eSVasanthakumar Thiagarajan 	for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
24948dafb70eSVasanthakumar Thiagarajan 		spin_lock_init(&ar->sta_list[ctr].psq_lock);
24958dafb70eSVasanthakumar Thiagarajan 		skb_queue_head_init(&ar->sta_list[ctr].psq);
24968dafb70eSVasanthakumar Thiagarajan 	}
24978dafb70eSVasanthakumar Thiagarajan 
24988dafb70eSVasanthakumar Thiagarajan 	skb_queue_head_init(&ar->mcastpsq);
24998dafb70eSVasanthakumar Thiagarajan 
25008dafb70eSVasanthakumar Thiagarajan 	memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3);
25018dafb70eSVasanthakumar Thiagarajan 
25028dafb70eSVasanthakumar Thiagarajan 	return ar;
25038dafb70eSVasanthakumar Thiagarajan }
25048dafb70eSVasanthakumar Thiagarajan 
25058dafb70eSVasanthakumar Thiagarajan int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
25068dafb70eSVasanthakumar Thiagarajan {
25078dafb70eSVasanthakumar Thiagarajan 	struct wiphy *wiphy = ar->wiphy;
25088dafb70eSVasanthakumar Thiagarajan 	int ret;
25096bbc7c35SJouni Malinen 
2510be98e3a4SVasanthakumar Thiagarajan 	wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
2511f80574aeSJouni Malinen 
2512be98e3a4SVasanthakumar Thiagarajan 	wiphy->max_remain_on_channel_duration = 5000;
251363fa1e0cSJouni Malinen 
2514bdcd8170SKalle Valo 	/* set device pointer for wiphy */
25158dafb70eSVasanthakumar Thiagarajan 	set_wiphy_dev(wiphy, ar->dev);
2516bdcd8170SKalle Valo 
2517be98e3a4SVasanthakumar Thiagarajan 	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
25188dafb70eSVasanthakumar Thiagarajan 				 BIT(NL80211_IFTYPE_ADHOC) |
25198dafb70eSVasanthakumar Thiagarajan 				 BIT(NL80211_IFTYPE_AP);
25206bbc7c35SJouni Malinen 	if (ar->p2p) {
2521be98e3a4SVasanthakumar Thiagarajan 		wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
25226bbc7c35SJouni Malinen 					  BIT(NL80211_IFTYPE_P2P_CLIENT);
25236bbc7c35SJouni Malinen 	}
25248dafb70eSVasanthakumar Thiagarajan 
2525bdcd8170SKalle Valo 	/* max num of ssids that can be probed during scanning */
2526be98e3a4SVasanthakumar Thiagarajan 	wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
2527be98e3a4SVasanthakumar Thiagarajan 	wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
2528be98e3a4SVasanthakumar Thiagarajan 	wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
2529be98e3a4SVasanthakumar Thiagarajan 	wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
2530be98e3a4SVasanthakumar Thiagarajan 	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
2531bdcd8170SKalle Valo 
2532be98e3a4SVasanthakumar Thiagarajan 	wiphy->cipher_suites = cipher_suites;
2533be98e3a4SVasanthakumar Thiagarajan 	wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
2534bdcd8170SKalle Valo 
2535eae9e066SRaja Mani 	wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
2536eae9e066SRaja Mani 			      WIPHY_WOWLAN_DISCONNECT |
2537eae9e066SRaja Mani 			      WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
2538eae9e066SRaja Mani 			      WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
2539eae9e066SRaja Mani 			      WIPHY_WOWLAN_EAP_IDENTITY_REQ   |
2540eae9e066SRaja Mani 			      WIPHY_WOWLAN_4WAY_HANDSHAKE;
2541eae9e066SRaja Mani 	wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
2542eae9e066SRaja Mani 	wiphy->wowlan.pattern_min_len = 1;
2543eae9e066SRaja Mani 	wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
2544eae9e066SRaja Mani 
2545be98e3a4SVasanthakumar Thiagarajan 	ret = wiphy_register(wiphy);
2546bdcd8170SKalle Valo 	if (ret < 0) {
2547bdcd8170SKalle Valo 		ath6kl_err("couldn't register wiphy device\n");
25488dafb70eSVasanthakumar Thiagarajan 		return ret;
25498dafb70eSVasanthakumar Thiagarajan 	}
25508dafb70eSVasanthakumar Thiagarajan 
25518dafb70eSVasanthakumar Thiagarajan 	return 0;
25528dafb70eSVasanthakumar Thiagarajan }
25538dafb70eSVasanthakumar Thiagarajan 
2554108438bcSVasanthakumar Thiagarajan static int ath6kl_init_if_data(struct ath6kl_vif *vif)
25558dafb70eSVasanthakumar Thiagarajan {
25562132c69cSVasanthakumar Thiagarajan 	vif->aggr_cntxt = aggr_init(vif->ndev);
25572132c69cSVasanthakumar Thiagarajan 	if (!vif->aggr_cntxt) {
25588dafb70eSVasanthakumar Thiagarajan 		ath6kl_err("failed to initialize aggr\n");
25598dafb70eSVasanthakumar Thiagarajan 		return -ENOMEM;
25608dafb70eSVasanthakumar Thiagarajan 	}
25618dafb70eSVasanthakumar Thiagarajan 
2562de3ad713SVasanthakumar Thiagarajan 	setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
2563108438bcSVasanthakumar Thiagarajan 		    (unsigned long) vif->ndev);
2564de3ad713SVasanthakumar Thiagarajan 	set_bit(WMM_ENABLED, &vif->flags);
2565478ac027SVasanthakumar Thiagarajan 	spin_lock_init(&vif->if_lock);
25668dafb70eSVasanthakumar Thiagarajan 
25678dafb70eSVasanthakumar Thiagarajan 	return 0;
25688dafb70eSVasanthakumar Thiagarajan }
25698dafb70eSVasanthakumar Thiagarajan 
2570108438bcSVasanthakumar Thiagarajan void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
25718dafb70eSVasanthakumar Thiagarajan {
257255055976SVasanthakumar Thiagarajan 	struct ath6kl *ar = vif->ar;
257355055976SVasanthakumar Thiagarajan 
25742132c69cSVasanthakumar Thiagarajan 	aggr_module_destroy(vif->aggr_cntxt);
2575108438bcSVasanthakumar Thiagarajan 
257655055976SVasanthakumar Thiagarajan 	ar->avail_idx_map |= BIT(vif->fw_vif_idx);
257755055976SVasanthakumar Thiagarajan 
257855055976SVasanthakumar Thiagarajan 	if (vif->nw_type == ADHOC_NETWORK)
257955055976SVasanthakumar Thiagarajan 		ar->ibss_if_active = false;
258055055976SVasanthakumar Thiagarajan 
258127929723SVasanthakumar Thiagarajan 	unregister_netdevice(vif->ndev);
258255055976SVasanthakumar Thiagarajan 
258355055976SVasanthakumar Thiagarajan 	ar->num_vif--;
25848dafb70eSVasanthakumar Thiagarajan }
25858dafb70eSVasanthakumar Thiagarajan 
25868dafb70eSVasanthakumar Thiagarajan struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
258755055976SVasanthakumar Thiagarajan 					enum nl80211_iftype type, u8 fw_vif_idx,
258855055976SVasanthakumar Thiagarajan 					u8 nw_type)
25898dafb70eSVasanthakumar Thiagarajan {
25908dafb70eSVasanthakumar Thiagarajan 	struct net_device *ndev;
2591108438bcSVasanthakumar Thiagarajan 	struct ath6kl_vif *vif;
25928dafb70eSVasanthakumar Thiagarajan 
259355055976SVasanthakumar Thiagarajan 	ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
25948dafb70eSVasanthakumar Thiagarajan 	if (!ndev)
25958dafb70eSVasanthakumar Thiagarajan 		return NULL;
25968dafb70eSVasanthakumar Thiagarajan 
2597108438bcSVasanthakumar Thiagarajan 	vif = netdev_priv(ndev);
2598108438bcSVasanthakumar Thiagarajan 	ndev->ieee80211_ptr = &vif->wdev;
2599108438bcSVasanthakumar Thiagarajan 	vif->wdev.wiphy = ar->wiphy;
2600108438bcSVasanthakumar Thiagarajan 	vif->ar = ar;
2601108438bcSVasanthakumar Thiagarajan 	vif->ndev = ndev;
2602108438bcSVasanthakumar Thiagarajan 	SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
2603108438bcSVasanthakumar Thiagarajan 	vif->wdev.netdev = ndev;
2604108438bcSVasanthakumar Thiagarajan 	vif->wdev.iftype = type;
2605334234b5SVasanthakumar Thiagarajan 	vif->fw_vif_idx = fw_vif_idx;
260655055976SVasanthakumar Thiagarajan 	vif->nw_type = vif->next_mode = nw_type;
26078dafb70eSVasanthakumar Thiagarajan 
260855055976SVasanthakumar Thiagarajan 	memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
260955055976SVasanthakumar Thiagarajan 	if (fw_vif_idx != 0)
261055055976SVasanthakumar Thiagarajan 		ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
261155055976SVasanthakumar Thiagarajan 				     0x2;
261255055976SVasanthakumar Thiagarajan 
26138dafb70eSVasanthakumar Thiagarajan 	init_netdev(ndev);
26148dafb70eSVasanthakumar Thiagarajan 
2615e29f25f5SVasanthakumar Thiagarajan 	ath6kl_init_control_info(vif);
26168dafb70eSVasanthakumar Thiagarajan 
26178dafb70eSVasanthakumar Thiagarajan 	/* TODO: Pass interface specific pointer instead of ar */
2618108438bcSVasanthakumar Thiagarajan 	if (ath6kl_init_if_data(vif))
26198dafb70eSVasanthakumar Thiagarajan 		goto err;
26208dafb70eSVasanthakumar Thiagarajan 
262127929723SVasanthakumar Thiagarajan 	if (register_netdevice(ndev))
26228dafb70eSVasanthakumar Thiagarajan 		goto err;
26238dafb70eSVasanthakumar Thiagarajan 
262455055976SVasanthakumar Thiagarajan 	ar->avail_idx_map &= ~BIT(fw_vif_idx);
262514ee6f6bSVasanthakumar Thiagarajan 	vif->sme_state = SME_DISCONNECTED;
262659c98449SVasanthakumar Thiagarajan 	set_bit(WLAN_ENABLED, &vif->flags);
26278dafb70eSVasanthakumar Thiagarajan 	ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
262859c98449SVasanthakumar Thiagarajan 	set_bit(NETDEV_REGISTERED, &vif->flags);
26298dafb70eSVasanthakumar Thiagarajan 
263055055976SVasanthakumar Thiagarajan 	if (type == NL80211_IFTYPE_ADHOC)
263155055976SVasanthakumar Thiagarajan 		ar->ibss_if_active = true;
263255055976SVasanthakumar Thiagarajan 
263311f6e40dSVasanthakumar Thiagarajan 	spin_lock_bh(&ar->list_lock);
2634990bd915SVasanthakumar Thiagarajan 	list_add_tail(&vif->list, &ar->vif_list);
263511f6e40dSVasanthakumar Thiagarajan 	spin_unlock_bh(&ar->list_lock);
2636990bd915SVasanthakumar Thiagarajan 
26378dafb70eSVasanthakumar Thiagarajan 	return ndev;
26388dafb70eSVasanthakumar Thiagarajan 
26398dafb70eSVasanthakumar Thiagarajan err:
264027929723SVasanthakumar Thiagarajan 	aggr_module_destroy(vif->aggr_cntxt);
264127929723SVasanthakumar Thiagarajan 	free_netdev(ndev);
2642bdcd8170SKalle Valo 	return NULL;
2643bdcd8170SKalle Valo }
2644bdcd8170SKalle Valo 
26458dafb70eSVasanthakumar Thiagarajan void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar)
2646bdcd8170SKalle Valo {
2647be98e3a4SVasanthakumar Thiagarajan 	wiphy_unregister(ar->wiphy);
2648be98e3a4SVasanthakumar Thiagarajan 	wiphy_free(ar->wiphy);
2649bdcd8170SKalle Valo }
2650