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 
124bdcd8170SKalle Valo static int ath6kl_set_wpa_version(struct ath6kl *ar,
125bdcd8170SKalle Valo 				  enum nl80211_wpa_versions wpa_version)
126bdcd8170SKalle Valo {
127bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
128bdcd8170SKalle Valo 
129bdcd8170SKalle Valo 	if (!wpa_version) {
130bdcd8170SKalle Valo 		ar->auth_mode = NONE_AUTH;
131bdcd8170SKalle Valo 	} else if (wpa_version & NL80211_WPA_VERSION_2) {
132bdcd8170SKalle Valo 		ar->auth_mode = WPA2_AUTH;
133bdcd8170SKalle Valo 	} else if (wpa_version & NL80211_WPA_VERSION_1) {
134bdcd8170SKalle Valo 		ar->auth_mode = WPA_AUTH;
135bdcd8170SKalle Valo 	} else {
136bdcd8170SKalle Valo 		ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
137bdcd8170SKalle Valo 		return -ENOTSUPP;
138bdcd8170SKalle Valo 	}
139bdcd8170SKalle Valo 
140bdcd8170SKalle Valo 	return 0;
141bdcd8170SKalle Valo }
142bdcd8170SKalle Valo 
143bdcd8170SKalle Valo static int ath6kl_set_auth_type(struct ath6kl *ar,
144bdcd8170SKalle Valo 				enum nl80211_auth_type auth_type)
145bdcd8170SKalle Valo {
146bdcd8170SKalle Valo 
147bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
148bdcd8170SKalle Valo 
149bdcd8170SKalle Valo 	switch (auth_type) {
150bdcd8170SKalle Valo 	case NL80211_AUTHTYPE_OPEN_SYSTEM:
151bdcd8170SKalle Valo 		ar->dot11_auth_mode = OPEN_AUTH;
152bdcd8170SKalle Valo 		break;
153bdcd8170SKalle Valo 	case NL80211_AUTHTYPE_SHARED_KEY:
154bdcd8170SKalle Valo 		ar->dot11_auth_mode = SHARED_AUTH;
155bdcd8170SKalle Valo 		break;
156bdcd8170SKalle Valo 	case NL80211_AUTHTYPE_NETWORK_EAP:
157bdcd8170SKalle Valo 		ar->dot11_auth_mode = LEAP_AUTH;
158bdcd8170SKalle Valo 		break;
159bdcd8170SKalle Valo 
160bdcd8170SKalle Valo 	case NL80211_AUTHTYPE_AUTOMATIC:
161bdcd8170SKalle Valo 		ar->dot11_auth_mode = OPEN_AUTH;
162bdcd8170SKalle Valo 		ar->auto_auth_stage = AUTH_OPEN_IN_PROGRESS;
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 
173bdcd8170SKalle Valo static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast)
174bdcd8170SKalle Valo {
175bdcd8170SKalle Valo 	u8 *ar_cipher = ucast ? &ar->prwise_crypto : &ar->grp_crypto;
17638acde3cSEdward Lu 	u8 *ar_cipher_len = ucast ? &ar->prwise_crypto_len :
17738acde3cSEdward Lu 		&ar->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;
204bdcd8170SKalle Valo 	default:
205bdcd8170SKalle Valo 		ath6kl_err("cipher 0x%x not supported\n", cipher);
206bdcd8170SKalle Valo 		return -ENOTSUPP;
207bdcd8170SKalle Valo 	}
208bdcd8170SKalle Valo 
209bdcd8170SKalle Valo 	return 0;
210bdcd8170SKalle Valo }
211bdcd8170SKalle Valo 
212bdcd8170SKalle Valo static void ath6kl_set_key_mgmt(struct ath6kl *ar, u32 key_mgmt)
213bdcd8170SKalle Valo {
214bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
215bdcd8170SKalle Valo 
216bdcd8170SKalle Valo 	if (key_mgmt == WLAN_AKM_SUITE_PSK) {
217bdcd8170SKalle Valo 		if (ar->auth_mode == WPA_AUTH)
218bdcd8170SKalle Valo 			ar->auth_mode = WPA_PSK_AUTH;
219bdcd8170SKalle Valo 		else if (ar->auth_mode == WPA2_AUTH)
220bdcd8170SKalle Valo 			ar->auth_mode = WPA2_PSK_AUTH;
221bdcd8170SKalle Valo 	} else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
222bdcd8170SKalle Valo 		ar->auth_mode = NONE_AUTH;
223bdcd8170SKalle Valo 	}
224bdcd8170SKalle Valo }
225bdcd8170SKalle Valo 
226bdcd8170SKalle Valo static bool ath6kl_cfg80211_ready(struct ath6kl *ar)
227bdcd8170SKalle Valo {
228bdcd8170SKalle Valo 	if (!test_bit(WMI_READY, &ar->flag)) {
229bdcd8170SKalle Valo 		ath6kl_err("wmi is not ready\n");
230bdcd8170SKalle Valo 		return false;
231bdcd8170SKalle Valo 	}
232bdcd8170SKalle Valo 
233575b5f34SRaja Mani 	if (!test_bit(WLAN_ENABLED, &ar->flag)) {
234bdcd8170SKalle Valo 		ath6kl_err("wlan disabled\n");
235bdcd8170SKalle Valo 		return false;
236bdcd8170SKalle Valo 	}
237bdcd8170SKalle Valo 
238bdcd8170SKalle Valo 	return true;
239bdcd8170SKalle Valo }
240bdcd8170SKalle Valo 
241bdcd8170SKalle Valo static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
242bdcd8170SKalle Valo 				   struct cfg80211_connect_params *sme)
243bdcd8170SKalle Valo {
244bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(dev);
245bdcd8170SKalle Valo 	int status;
246bdcd8170SKalle Valo 
247bdcd8170SKalle Valo 	ar->sme_state = SME_CONNECTING;
248bdcd8170SKalle Valo 
249bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
250bdcd8170SKalle Valo 		return -EIO;
251bdcd8170SKalle Valo 
252bdcd8170SKalle Valo 	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
253bdcd8170SKalle Valo 		ath6kl_err("destroy in progress\n");
254bdcd8170SKalle Valo 		return -EBUSY;
255bdcd8170SKalle Valo 	}
256bdcd8170SKalle Valo 
257bdcd8170SKalle Valo 	if (test_bit(SKIP_SCAN, &ar->flag) &&
258bdcd8170SKalle Valo 	    ((sme->channel && sme->channel->center_freq == 0) ||
259bdcd8170SKalle Valo 	     (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
260bdcd8170SKalle Valo 		ath6kl_err("SkipScan: channel or bssid invalid\n");
261bdcd8170SKalle Valo 		return -EINVAL;
262bdcd8170SKalle Valo 	}
263bdcd8170SKalle Valo 
264bdcd8170SKalle Valo 	if (down_interruptible(&ar->sem)) {
265bdcd8170SKalle Valo 		ath6kl_err("busy, couldn't get access\n");
266bdcd8170SKalle Valo 		return -ERESTARTSYS;
267bdcd8170SKalle Valo 	}
268bdcd8170SKalle Valo 
269bdcd8170SKalle Valo 	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
270bdcd8170SKalle Valo 		ath6kl_err("busy, destroy in progress\n");
271bdcd8170SKalle Valo 		up(&ar->sem);
272bdcd8170SKalle Valo 		return -EBUSY;
273bdcd8170SKalle Valo 	}
274bdcd8170SKalle Valo 
275bdcd8170SKalle Valo 	if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
276bdcd8170SKalle Valo 		/*
277bdcd8170SKalle Valo 		 * sleep until the command queue drains
278bdcd8170SKalle Valo 		 */
279bdcd8170SKalle Valo 		wait_event_interruptible_timeout(ar->event_wq,
280bdcd8170SKalle Valo 			ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
281bdcd8170SKalle Valo 			WMI_TIMEOUT);
282bdcd8170SKalle Valo 		if (signal_pending(current)) {
283bdcd8170SKalle Valo 			ath6kl_err("cmd queue drain timeout\n");
284bdcd8170SKalle Valo 			up(&ar->sem);
285bdcd8170SKalle Valo 			return -EINTR;
286bdcd8170SKalle Valo 		}
287bdcd8170SKalle Valo 	}
288bdcd8170SKalle Valo 
289bdcd8170SKalle Valo 	if (test_bit(CONNECTED, &ar->flag) &&
290bdcd8170SKalle Valo 	    ar->ssid_len == sme->ssid_len &&
291bdcd8170SKalle Valo 	    !memcmp(ar->ssid, sme->ssid, ar->ssid_len)) {
292bdcd8170SKalle Valo 		ar->reconnect_flag = true;
293bdcd8170SKalle Valo 		status = ath6kl_wmi_reconnect_cmd(ar->wmi, ar->req_bssid,
294bdcd8170SKalle Valo 						  ar->ch_hint);
295bdcd8170SKalle Valo 
296bdcd8170SKalle Valo 		up(&ar->sem);
297bdcd8170SKalle Valo 		if (status) {
298bdcd8170SKalle Valo 			ath6kl_err("wmi_reconnect_cmd failed\n");
299bdcd8170SKalle Valo 			return -EIO;
300bdcd8170SKalle Valo 		}
301bdcd8170SKalle Valo 		return 0;
302bdcd8170SKalle Valo 	} else if (ar->ssid_len == sme->ssid_len &&
303bdcd8170SKalle Valo 		   !memcmp(ar->ssid, sme->ssid, ar->ssid_len)) {
304bdcd8170SKalle Valo 		ath6kl_disconnect(ar);
305bdcd8170SKalle Valo 	}
306bdcd8170SKalle Valo 
307bdcd8170SKalle Valo 	memset(ar->ssid, 0, sizeof(ar->ssid));
308bdcd8170SKalle Valo 	ar->ssid_len = sme->ssid_len;
309bdcd8170SKalle Valo 	memcpy(ar->ssid, sme->ssid, sme->ssid_len);
310bdcd8170SKalle Valo 
311bdcd8170SKalle Valo 	if (sme->channel)
312bdcd8170SKalle Valo 		ar->ch_hint = sme->channel->center_freq;
313bdcd8170SKalle Valo 
314bdcd8170SKalle Valo 	memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
315bdcd8170SKalle Valo 	if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
316bdcd8170SKalle Valo 		memcpy(ar->req_bssid, sme->bssid, sizeof(ar->req_bssid));
317bdcd8170SKalle Valo 
318bdcd8170SKalle Valo 	ath6kl_set_wpa_version(ar, sme->crypto.wpa_versions);
319bdcd8170SKalle Valo 
320bdcd8170SKalle Valo 	status = ath6kl_set_auth_type(ar, sme->auth_type);
321bdcd8170SKalle Valo 	if (status) {
322bdcd8170SKalle Valo 		up(&ar->sem);
323bdcd8170SKalle Valo 		return status;
324bdcd8170SKalle Valo 	}
325bdcd8170SKalle Valo 
326bdcd8170SKalle Valo 	if (sme->crypto.n_ciphers_pairwise)
327bdcd8170SKalle Valo 		ath6kl_set_cipher(ar, sme->crypto.ciphers_pairwise[0], true);
328bdcd8170SKalle Valo 	else
329bdcd8170SKalle Valo 		ath6kl_set_cipher(ar, 0, true);
330bdcd8170SKalle Valo 
331bdcd8170SKalle Valo 	ath6kl_set_cipher(ar, sme->crypto.cipher_group, false);
332bdcd8170SKalle Valo 
333bdcd8170SKalle Valo 	if (sme->crypto.n_akm_suites)
334bdcd8170SKalle Valo 		ath6kl_set_key_mgmt(ar, sme->crypto.akm_suites[0]);
335bdcd8170SKalle Valo 
336bdcd8170SKalle Valo 	if ((sme->key_len) &&
337bdcd8170SKalle Valo 	    (ar->auth_mode == NONE_AUTH) && (ar->prwise_crypto == WEP_CRYPT)) {
338bdcd8170SKalle Valo 		struct ath6kl_key *key = NULL;
339bdcd8170SKalle Valo 
340bdcd8170SKalle Valo 		if (sme->key_idx < WMI_MIN_KEY_INDEX ||
341bdcd8170SKalle Valo 		    sme->key_idx > WMI_MAX_KEY_INDEX) {
342bdcd8170SKalle Valo 			ath6kl_err("key index %d out of bounds\n",
343bdcd8170SKalle Valo 				   sme->key_idx);
344bdcd8170SKalle Valo 			up(&ar->sem);
345bdcd8170SKalle Valo 			return -ENOENT;
346bdcd8170SKalle Valo 		}
347bdcd8170SKalle Valo 
348bdcd8170SKalle Valo 		key = &ar->keys[sme->key_idx];
349bdcd8170SKalle Valo 		key->key_len = sme->key_len;
350bdcd8170SKalle Valo 		memcpy(key->key, sme->key, key->key_len);
351bdcd8170SKalle Valo 		key->cipher = ar->prwise_crypto;
352bdcd8170SKalle Valo 		ar->def_txkey_index = sme->key_idx;
353bdcd8170SKalle Valo 
354bdcd8170SKalle Valo 		ath6kl_wmi_addkey_cmd(ar->wmi, sme->key_idx,
355bdcd8170SKalle Valo 				      ar->prwise_crypto,
356bdcd8170SKalle Valo 				      GROUP_USAGE | TX_USAGE,
357bdcd8170SKalle Valo 				      key->key_len,
358bdcd8170SKalle Valo 				      NULL,
359bdcd8170SKalle Valo 				      key->key, KEY_OP_INIT_VAL, NULL,
360bdcd8170SKalle Valo 				      NO_SYNC_WMIFLAG);
361bdcd8170SKalle Valo 	}
362bdcd8170SKalle Valo 
363bdcd8170SKalle Valo 	if (!ar->usr_bss_filter) {
364bdcd8170SKalle Valo 		if (ath6kl_wmi_bssfilter_cmd(ar->wmi, ALL_BSS_FILTER, 0) != 0) {
365bdcd8170SKalle Valo 			ath6kl_err("couldn't set bss filtering\n");
366bdcd8170SKalle Valo 			up(&ar->sem);
367bdcd8170SKalle Valo 			return -EIO;
368bdcd8170SKalle Valo 		}
369bdcd8170SKalle Valo 	}
370bdcd8170SKalle Valo 
371bdcd8170SKalle Valo 	ar->nw_type = ar->next_mode;
372bdcd8170SKalle Valo 
373bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
374bdcd8170SKalle Valo 		   "%s: connect called with authmode %d dot11 auth %d"
375bdcd8170SKalle Valo 		   " PW crypto %d PW crypto len %d GRP crypto %d"
376bdcd8170SKalle Valo 		   " GRP crypto len %d channel hint %u\n",
377bdcd8170SKalle Valo 		   __func__,
378bdcd8170SKalle Valo 		   ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto,
379bdcd8170SKalle Valo 		   ar->prwise_crypto_len, ar->grp_crypto,
38038acde3cSEdward Lu 		   ar->grp_crypto_len, ar->ch_hint);
381bdcd8170SKalle Valo 
382bdcd8170SKalle Valo 	ar->reconnect_flag = 0;
383bdcd8170SKalle Valo 	status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type,
384bdcd8170SKalle Valo 					ar->dot11_auth_mode, ar->auth_mode,
385bdcd8170SKalle Valo 					ar->prwise_crypto,
386bdcd8170SKalle Valo 					ar->prwise_crypto_len,
38738acde3cSEdward Lu 					ar->grp_crypto, ar->grp_crypto_len,
388bdcd8170SKalle Valo 					ar->ssid_len, ar->ssid,
389bdcd8170SKalle Valo 					ar->req_bssid, ar->ch_hint,
390bdcd8170SKalle Valo 					ar->connect_ctrl_flags);
391bdcd8170SKalle Valo 
392bdcd8170SKalle Valo 	up(&ar->sem);
393bdcd8170SKalle Valo 
394bdcd8170SKalle Valo 	if (status == -EINVAL) {
395bdcd8170SKalle Valo 		memset(ar->ssid, 0, sizeof(ar->ssid));
396bdcd8170SKalle Valo 		ar->ssid_len = 0;
397bdcd8170SKalle Valo 		ath6kl_err("invalid request\n");
398bdcd8170SKalle Valo 		return -ENOENT;
399bdcd8170SKalle Valo 	} else if (status) {
400bdcd8170SKalle Valo 		ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
401bdcd8170SKalle Valo 		return -EIO;
402bdcd8170SKalle Valo 	}
403bdcd8170SKalle Valo 
404bdcd8170SKalle Valo 	if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
405bdcd8170SKalle Valo 	    ((ar->auth_mode == WPA_PSK_AUTH)
406bdcd8170SKalle Valo 	     || (ar->auth_mode == WPA2_PSK_AUTH))) {
407bdcd8170SKalle Valo 		mod_timer(&ar->disconnect_timer,
408bdcd8170SKalle Valo 			  jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
409bdcd8170SKalle Valo 	}
410bdcd8170SKalle Valo 
411bdcd8170SKalle Valo 	ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
412bdcd8170SKalle Valo 	set_bit(CONNECT_PEND, &ar->flag);
413bdcd8170SKalle Valo 
414bdcd8170SKalle Valo 	return 0;
415bdcd8170SKalle Valo }
416bdcd8170SKalle Valo 
417bdcd8170SKalle Valo void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
418bdcd8170SKalle Valo 				   u8 *bssid, u16 listen_intvl,
419bdcd8170SKalle Valo 				   u16 beacon_intvl,
420bdcd8170SKalle Valo 				   enum network_type nw_type,
421bdcd8170SKalle Valo 				   u8 beacon_ie_len, u8 assoc_req_len,
422bdcd8170SKalle Valo 				   u8 assoc_resp_len, u8 *assoc_info)
423bdcd8170SKalle Valo {
424bdcd8170SKalle Valo 	u16 size = 0;
425bdcd8170SKalle Valo 	u16 capability = 0;
426bdcd8170SKalle Valo 	struct cfg80211_bss *bss = NULL;
427bdcd8170SKalle Valo 	struct ieee80211_mgmt *mgmt = NULL;
428bdcd8170SKalle Valo 	struct ieee80211_channel *ibss_ch = NULL;
429bdcd8170SKalle Valo 	s32 signal = 50 * 100;
430bdcd8170SKalle Valo 	u8 ie_buf_len = 0;
431bdcd8170SKalle Valo 	unsigned char ie_buf[256];
432bdcd8170SKalle Valo 	unsigned char *ptr_ie_buf = ie_buf;
433bdcd8170SKalle Valo 	unsigned char *ieeemgmtbuf = NULL;
434bdcd8170SKalle Valo 	u8 source_mac[ETH_ALEN];
435bdcd8170SKalle Valo 
436bdcd8170SKalle Valo 	/* capinfo + listen interval */
437bdcd8170SKalle Valo 	u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
438bdcd8170SKalle Valo 
439bdcd8170SKalle Valo 	/* capinfo + status code +  associd */
440bdcd8170SKalle Valo 	u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
441bdcd8170SKalle Valo 
442bdcd8170SKalle Valo 	u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
443bdcd8170SKalle Valo 	u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
444bdcd8170SKalle Valo 	    assoc_resp_ie_offset;
445bdcd8170SKalle Valo 
446bdcd8170SKalle Valo 	assoc_req_len -= assoc_req_ie_offset;
447bdcd8170SKalle Valo 	assoc_resp_len -= assoc_resp_ie_offset;
448bdcd8170SKalle Valo 
449bdcd8170SKalle Valo 	ar->auto_auth_stage = AUTH_IDLE;
450bdcd8170SKalle Valo 
451bdcd8170SKalle Valo 	if (nw_type & ADHOC_NETWORK) {
452bdcd8170SKalle Valo 		if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) {
453bdcd8170SKalle Valo 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
454bdcd8170SKalle Valo 				   "%s: ath6k not in ibss mode\n", __func__);
455bdcd8170SKalle Valo 			return;
456bdcd8170SKalle Valo 		}
457bdcd8170SKalle Valo 	}
458bdcd8170SKalle Valo 
459bdcd8170SKalle Valo 	if (nw_type & INFRA_NETWORK) {
4606b5e5d25SJouni Malinen 		if (ar->wdev->iftype != NL80211_IFTYPE_STATION &&
4616b5e5d25SJouni Malinen 		    ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
462bdcd8170SKalle Valo 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
463bdcd8170SKalle Valo 				   "%s: ath6k not in station mode\n", __func__);
464bdcd8170SKalle Valo 			return;
465bdcd8170SKalle Valo 		}
466bdcd8170SKalle Valo 	}
467bdcd8170SKalle Valo 
468bdcd8170SKalle Valo 	/*
469bdcd8170SKalle Valo 	 * Earlier we were updating the cfg about bss by making a beacon frame
470bdcd8170SKalle Valo 	 * only if the entry for bss is not there. This can have some issue if
471bdcd8170SKalle Valo 	 * ROAM event is generated and a heavy traffic is ongoing. The ROAM
472bdcd8170SKalle Valo 	 * event is handled through a work queue and by the time it really gets
473bdcd8170SKalle Valo 	 * handled, BSS would have been aged out. So it is better to update the
474bdcd8170SKalle Valo 	 * cfg about BSS irrespective of its entry being present right now or
475bdcd8170SKalle Valo 	 * not.
476bdcd8170SKalle Valo 	 */
477bdcd8170SKalle Valo 
478bdcd8170SKalle Valo 	if (nw_type & ADHOC_NETWORK) {
479bdcd8170SKalle Valo 		/* construct 802.11 mgmt beacon */
480bdcd8170SKalle Valo 		if (ptr_ie_buf) {
481bdcd8170SKalle Valo 			*ptr_ie_buf++ = WLAN_EID_SSID;
482bdcd8170SKalle Valo 			*ptr_ie_buf++ = ar->ssid_len;
483bdcd8170SKalle Valo 			memcpy(ptr_ie_buf, ar->ssid, ar->ssid_len);
484bdcd8170SKalle Valo 			ptr_ie_buf += ar->ssid_len;
485bdcd8170SKalle Valo 
486bdcd8170SKalle Valo 			*ptr_ie_buf++ = WLAN_EID_IBSS_PARAMS;
487bdcd8170SKalle Valo 			*ptr_ie_buf++ = 2;	/* length */
488bdcd8170SKalle Valo 			*ptr_ie_buf++ = 0;	/* ATIM window */
489bdcd8170SKalle Valo 			*ptr_ie_buf++ = 0;	/* ATIM window */
490bdcd8170SKalle Valo 
491bdcd8170SKalle Valo 			/* TODO: update ibss params and include supported rates,
492bdcd8170SKalle Valo 			 * DS param set, extened support rates, wmm. */
493bdcd8170SKalle Valo 
494bdcd8170SKalle Valo 			ie_buf_len = ptr_ie_buf - ie_buf;
495bdcd8170SKalle Valo 		}
496bdcd8170SKalle Valo 
497bdcd8170SKalle Valo 		capability |= WLAN_CAPABILITY_IBSS;
498bdcd8170SKalle Valo 
499bdcd8170SKalle Valo 		if (ar->prwise_crypto == WEP_CRYPT)
500bdcd8170SKalle Valo 			capability |= WLAN_CAPABILITY_PRIVACY;
501bdcd8170SKalle Valo 
502bdcd8170SKalle Valo 		memcpy(source_mac, ar->net_dev->dev_addr, ETH_ALEN);
503bdcd8170SKalle Valo 		ptr_ie_buf = ie_buf;
504bdcd8170SKalle Valo 	} else {
505bdcd8170SKalle Valo 		capability = *(u16 *) (&assoc_info[beacon_ie_len]);
506bdcd8170SKalle Valo 		memcpy(source_mac, bssid, ETH_ALEN);
507bdcd8170SKalle Valo 		ptr_ie_buf = assoc_req_ie;
508bdcd8170SKalle Valo 		ie_buf_len = assoc_req_len;
509bdcd8170SKalle Valo 	}
510bdcd8170SKalle Valo 
511bdcd8170SKalle Valo 	size = offsetof(struct ieee80211_mgmt, u)
512bdcd8170SKalle Valo 	+ sizeof(mgmt->u.beacon)
513bdcd8170SKalle Valo 	+ ie_buf_len;
514bdcd8170SKalle Valo 
515bdcd8170SKalle Valo 	ieeemgmtbuf = kzalloc(size, GFP_ATOMIC);
516bdcd8170SKalle Valo 	if (!ieeemgmtbuf) {
517bdcd8170SKalle Valo 		ath6kl_err("ieee mgmt buf alloc error\n");
518bdcd8170SKalle Valo 		return;
519bdcd8170SKalle Valo 	}
520bdcd8170SKalle Valo 
521bdcd8170SKalle Valo 	mgmt = (struct ieee80211_mgmt *)ieeemgmtbuf;
522bdcd8170SKalle Valo 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
523bdcd8170SKalle Valo 					  IEEE80211_STYPE_BEACON);
524bdcd8170SKalle Valo 	memset(mgmt->da, 0xff, ETH_ALEN);	/* broadcast addr */
525bdcd8170SKalle Valo 	memcpy(mgmt->sa, source_mac, ETH_ALEN);
526bdcd8170SKalle Valo 	memcpy(mgmt->bssid, bssid, ETH_ALEN);
527bdcd8170SKalle Valo 	mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_intvl);
528bdcd8170SKalle Valo 	mgmt->u.beacon.capab_info = cpu_to_le16(capability);
529bdcd8170SKalle Valo 	memcpy(mgmt->u.beacon.variable, ptr_ie_buf, ie_buf_len);
530bdcd8170SKalle Valo 
531bdcd8170SKalle Valo 	ibss_ch = ieee80211_get_channel(ar->wdev->wiphy, (int)channel);
532bdcd8170SKalle Valo 
533bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
534bdcd8170SKalle Valo 		   "%s: inform bss with bssid %pM channel %d beacon_intvl %d capability 0x%x\n",
535bdcd8170SKalle Valo 		   __func__, mgmt->bssid, ibss_ch->hw_value,
536bdcd8170SKalle Valo 		   beacon_intvl, capability);
537bdcd8170SKalle Valo 
538bdcd8170SKalle Valo 	bss = cfg80211_inform_bss_frame(ar->wdev->wiphy,
539bdcd8170SKalle Valo 					ibss_ch, mgmt,
540bdcd8170SKalle Valo 					size, signal, GFP_KERNEL);
541bdcd8170SKalle Valo 	kfree(ieeemgmtbuf);
542bdcd8170SKalle Valo 	cfg80211_put_bss(bss);
543bdcd8170SKalle Valo 
544bdcd8170SKalle Valo 	if (nw_type & ADHOC_NETWORK) {
545bdcd8170SKalle Valo 		cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
546bdcd8170SKalle Valo 		return;
547bdcd8170SKalle Valo 	}
548bdcd8170SKalle Valo 
5499aa60357SRaja Mani 	if (ar->sme_state == SME_CONNECTING) {
550bdcd8170SKalle Valo 		/* inform connect result to cfg80211 */
5519aa60357SRaja Mani 		ar->sme_state = SME_CONNECTED;
552bdcd8170SKalle Valo 		cfg80211_connect_result(ar->net_dev, bssid,
553bdcd8170SKalle Valo 					assoc_req_ie, assoc_req_len,
554bdcd8170SKalle Valo 					assoc_resp_ie, assoc_resp_len,
555bdcd8170SKalle Valo 					WLAN_STATUS_SUCCESS, GFP_KERNEL);
5569aa60357SRaja Mani 	} else if (ar->sme_state == SME_CONNECTED) {
557bdcd8170SKalle Valo 		/* inform roam event to cfg80211 */
558bdcd8170SKalle Valo 		cfg80211_roamed(ar->net_dev, ibss_ch, bssid,
559bdcd8170SKalle Valo 				assoc_req_ie, assoc_req_len,
560bdcd8170SKalle Valo 				assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
561bdcd8170SKalle Valo 	}
562bdcd8170SKalle Valo }
563bdcd8170SKalle Valo 
564bdcd8170SKalle Valo static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
565bdcd8170SKalle Valo 				      struct net_device *dev, u16 reason_code)
566bdcd8170SKalle Valo {
567bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
568bdcd8170SKalle Valo 
569bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
570bdcd8170SKalle Valo 		   reason_code);
571bdcd8170SKalle Valo 
572bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
573bdcd8170SKalle Valo 		return -EIO;
574bdcd8170SKalle Valo 
575bdcd8170SKalle Valo 	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
576bdcd8170SKalle Valo 		ath6kl_err("busy, destroy in progress\n");
577bdcd8170SKalle Valo 		return -EBUSY;
578bdcd8170SKalle Valo 	}
579bdcd8170SKalle Valo 
580bdcd8170SKalle Valo 	if (down_interruptible(&ar->sem)) {
581bdcd8170SKalle Valo 		ath6kl_err("busy, couldn't get access\n");
582bdcd8170SKalle Valo 		return -ERESTARTSYS;
583bdcd8170SKalle Valo 	}
584bdcd8170SKalle Valo 
585bdcd8170SKalle Valo 	ar->reconnect_flag = 0;
586bdcd8170SKalle Valo 	ath6kl_disconnect(ar);
587bdcd8170SKalle Valo 	memset(ar->ssid, 0, sizeof(ar->ssid));
588bdcd8170SKalle Valo 	ar->ssid_len = 0;
589bdcd8170SKalle Valo 
590bdcd8170SKalle Valo 	if (!test_bit(SKIP_SCAN, &ar->flag))
591bdcd8170SKalle Valo 		memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
592bdcd8170SKalle Valo 
593bdcd8170SKalle Valo 	up(&ar->sem);
594bdcd8170SKalle Valo 
595bdcd8170SKalle Valo 	return 0;
596bdcd8170SKalle Valo }
597bdcd8170SKalle Valo 
598bdcd8170SKalle Valo void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
599bdcd8170SKalle Valo 				      u8 *bssid, u8 assoc_resp_len,
600bdcd8170SKalle Valo 				      u8 *assoc_info, u16 proto_reason)
601bdcd8170SKalle Valo {
602bdcd8170SKalle Valo 	struct ath6kl_key *key = NULL;
603bdcd8170SKalle Valo 	u16 status;
604bdcd8170SKalle Valo 
605bdcd8170SKalle Valo 	if (ar->scan_req) {
606bdcd8170SKalle Valo 		cfg80211_scan_done(ar->scan_req, true);
607bdcd8170SKalle Valo 		ar->scan_req = NULL;
608bdcd8170SKalle Valo 	}
609bdcd8170SKalle Valo 
610bdcd8170SKalle Valo 	if (ar->nw_type & ADHOC_NETWORK) {
611bdcd8170SKalle Valo 		if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) {
612bdcd8170SKalle Valo 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
613bdcd8170SKalle Valo 				   "%s: ath6k not in ibss mode\n", __func__);
614bdcd8170SKalle Valo 			return;
615bdcd8170SKalle Valo 		}
616bdcd8170SKalle Valo 		memset(bssid, 0, ETH_ALEN);
617bdcd8170SKalle Valo 		cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
618bdcd8170SKalle Valo 		return;
619bdcd8170SKalle Valo 	}
620bdcd8170SKalle Valo 
621bdcd8170SKalle Valo 	if (ar->nw_type & INFRA_NETWORK) {
6226b5e5d25SJouni Malinen 		if (ar->wdev->iftype != NL80211_IFTYPE_STATION &&
6236b5e5d25SJouni Malinen 		    ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
624bdcd8170SKalle Valo 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
625bdcd8170SKalle Valo 				   "%s: ath6k not in station mode\n", __func__);
626bdcd8170SKalle Valo 			return;
627bdcd8170SKalle Valo 		}
628bdcd8170SKalle Valo 	}
629bdcd8170SKalle Valo 
630bdcd8170SKalle Valo 	if (!test_bit(CONNECT_PEND, &ar->flag)) {
631bdcd8170SKalle Valo 		if (reason != DISCONNECT_CMD)
632bdcd8170SKalle Valo 			ath6kl_wmi_disconnect_cmd(ar->wmi);
633bdcd8170SKalle Valo 
634bdcd8170SKalle Valo 		return;
635bdcd8170SKalle Valo 	}
636bdcd8170SKalle Valo 
637bdcd8170SKalle Valo 	if (reason == NO_NETWORK_AVAIL) {
638bdcd8170SKalle Valo 		/* connect cmd failed */
639bdcd8170SKalle Valo 		ath6kl_wmi_disconnect_cmd(ar->wmi);
640bdcd8170SKalle Valo 		return;
641bdcd8170SKalle Valo 	}
642bdcd8170SKalle Valo 
643bdcd8170SKalle Valo 	if (reason != DISCONNECT_CMD)
644bdcd8170SKalle Valo 		return;
645bdcd8170SKalle Valo 
646bdcd8170SKalle Valo 	if (!ar->auto_auth_stage) {
647bdcd8170SKalle Valo 		clear_bit(CONNECT_PEND, &ar->flag);
648bdcd8170SKalle Valo 
649bdcd8170SKalle Valo 		if (ar->sme_state == SME_CONNECTING) {
650bdcd8170SKalle Valo 			cfg80211_connect_result(ar->net_dev,
651bdcd8170SKalle Valo 						bssid, NULL, 0,
652bdcd8170SKalle Valo 						NULL, 0,
653bdcd8170SKalle Valo 						WLAN_STATUS_UNSPECIFIED_FAILURE,
654bdcd8170SKalle Valo 						GFP_KERNEL);
6550e5cc8e6SRaja Mani 		} else if (ar->sme_state == SME_CONNECTED) {
656bdcd8170SKalle Valo 			cfg80211_disconnected(ar->net_dev, reason,
657bdcd8170SKalle Valo 					      NULL, 0, GFP_KERNEL);
658bdcd8170SKalle Valo 		}
659bdcd8170SKalle Valo 
660bdcd8170SKalle Valo 		ar->sme_state = SME_DISCONNECTED;
661bdcd8170SKalle Valo 		return;
662bdcd8170SKalle Valo 	}
663bdcd8170SKalle Valo 
664bdcd8170SKalle Valo 	if (ar->dot11_auth_mode != OPEN_AUTH)
665bdcd8170SKalle Valo 		return;
666bdcd8170SKalle Valo 
667bdcd8170SKalle Valo 	/*
668bdcd8170SKalle Valo 	 * If the current auth algorithm is open, try shared and
669bdcd8170SKalle Valo 	 * make autoAuthStage idle. We do not make it leap for now
670bdcd8170SKalle Valo 	 * being.
671bdcd8170SKalle Valo 	 */
672bdcd8170SKalle Valo 	key = &ar->keys[ar->def_txkey_index];
673bdcd8170SKalle Valo 	if (down_interruptible(&ar->sem)) {
674bdcd8170SKalle Valo 		ath6kl_err("busy, couldn't get access\n");
675bdcd8170SKalle Valo 		return;
676bdcd8170SKalle Valo 	}
677bdcd8170SKalle Valo 
678bdcd8170SKalle Valo 	ar->dot11_auth_mode = SHARED_AUTH;
679bdcd8170SKalle Valo 	ar->auto_auth_stage = AUTH_IDLE;
680bdcd8170SKalle Valo 
681bdcd8170SKalle Valo 	ath6kl_wmi_addkey_cmd(ar->wmi,
682bdcd8170SKalle Valo 			      ar->def_txkey_index,
683bdcd8170SKalle Valo 			      ar->prwise_crypto,
684bdcd8170SKalle Valo 			      GROUP_USAGE | TX_USAGE,
685bdcd8170SKalle Valo 			      key->key_len, NULL,
686bdcd8170SKalle Valo 			      key->key,
687bdcd8170SKalle Valo 			      KEY_OP_INIT_VAL, NULL,
688bdcd8170SKalle Valo 			      NO_SYNC_WMIFLAG);
689bdcd8170SKalle Valo 
690bdcd8170SKalle Valo 	status = ath6kl_wmi_connect_cmd(ar->wmi,
691bdcd8170SKalle Valo 					ar->nw_type,
692bdcd8170SKalle Valo 					ar->dot11_auth_mode,
693bdcd8170SKalle Valo 					ar->auth_mode,
694bdcd8170SKalle Valo 					ar->prwise_crypto,
695bdcd8170SKalle Valo 					ar->prwise_crypto_len,
696bdcd8170SKalle Valo 					ar->grp_crypto,
69738acde3cSEdward Lu 					ar->grp_crypto_len,
698bdcd8170SKalle Valo 					ar->ssid_len,
699bdcd8170SKalle Valo 					ar->ssid,
700bdcd8170SKalle Valo 					ar->req_bssid,
701bdcd8170SKalle Valo 					ar->ch_hint,
702bdcd8170SKalle Valo 					ar->connect_ctrl_flags);
703bdcd8170SKalle Valo 	up(&ar->sem);
704bdcd8170SKalle Valo }
705bdcd8170SKalle Valo 
706bdcd8170SKalle Valo static inline bool is_ch_11a(u16 ch)
707bdcd8170SKalle Valo {
708bdcd8170SKalle Valo 	return (!((ch >= 2412) && (ch <= 2484)));
709bdcd8170SKalle Valo }
710bdcd8170SKalle Valo 
711cf104c2aSKalle Valo /* struct ath6kl_node_table::nt_nodelock is locked when calling this */
71291db35daSVasanthakumar Thiagarajan void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni)
713bdcd8170SKalle Valo {
714bdcd8170SKalle Valo 	struct ieee80211_mgmt *mgmt;
715bdcd8170SKalle Valo 	struct ieee80211_channel *channel;
716bdcd8170SKalle Valo 	struct ieee80211_supported_band *band;
717bdcd8170SKalle Valo 	struct ath6kl_common_ie *cie;
718bdcd8170SKalle Valo 	s32 signal;
719bdcd8170SKalle Valo 	int freq;
720bdcd8170SKalle Valo 
721bdcd8170SKalle Valo 	cie = &ni->ni_cie;
722bdcd8170SKalle Valo 
723bdcd8170SKalle Valo 	if (is_ch_11a(cie->ie_chan))
724bdcd8170SKalle Valo 		band = wiphy->bands[IEEE80211_BAND_5GHZ]; /* 11a */
725bdcd8170SKalle Valo 	else if ((cie->ie_erp) || (cie->ie_xrates))
726bdcd8170SKalle Valo 		band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11g */
727bdcd8170SKalle Valo 	else
728bdcd8170SKalle Valo 		band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11b */
729bdcd8170SKalle Valo 
730bdcd8170SKalle Valo 	freq = cie->ie_chan;
731bdcd8170SKalle Valo 	channel = ieee80211_get_channel(wiphy, freq);
732bdcd8170SKalle Valo 	signal = ni->ni_snr * 100;
733bdcd8170SKalle Valo 
734bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
735bdcd8170SKalle Valo 		   "%s: bssid %pM ch %d freq %d size %d\n", __func__,
7360ce77920SJouni Malinen 		   ni->ni_macaddr, channel->hw_value, freq, ni->ni_framelen);
7370ce77920SJouni Malinen 	/*
7380ce77920SJouni Malinen 	 * Both Beacon and Probe Response frames have same payload structure,
7390ce77920SJouni Malinen 	 * so it is fine to share the parser for both.
7400ce77920SJouni Malinen 	 */
7410ce77920SJouni Malinen 	if (ni->ni_framelen < 8 + 2 + 2)
7420ce77920SJouni Malinen 		return;
7430ce77920SJouni Malinen 	mgmt = (struct ieee80211_mgmt *) (ni->ni_buf -
7440ce77920SJouni Malinen 					  offsetof(struct ieee80211_mgmt, u));
7450ce77920SJouni Malinen 	cfg80211_inform_bss(wiphy, channel, ni->ni_macaddr,
7460ce77920SJouni Malinen 			    le64_to_cpu(mgmt->u.beacon.timestamp),
7470ce77920SJouni Malinen 			    le16_to_cpu(mgmt->u.beacon.capab_info),
7480ce77920SJouni Malinen 			    le16_to_cpu(mgmt->u.beacon.beacon_int),
7490ce77920SJouni Malinen 			    mgmt->u.beacon.variable,
7500ce77920SJouni Malinen 			    ni->ni_buf + ni->ni_framelen -
7510ce77920SJouni Malinen 			    mgmt->u.beacon.variable,
7520ce77920SJouni Malinen 			    signal, GFP_ATOMIC);
753bdcd8170SKalle Valo }
754bdcd8170SKalle Valo 
755bdcd8170SKalle Valo static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
756bdcd8170SKalle Valo 				struct cfg80211_scan_request *request)
757bdcd8170SKalle Valo {
758bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
7591276c9efSEdward Lu 	s8 n_channels = 0;
7601276c9efSEdward Lu 	u16 *channels = NULL;
761bdcd8170SKalle Valo 	int ret = 0;
762bdcd8170SKalle Valo 
763bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
764bdcd8170SKalle Valo 		return -EIO;
765bdcd8170SKalle Valo 
766bdcd8170SKalle Valo 	if (!ar->usr_bss_filter) {
7671b1e6ee3SJouni Malinen 		ret = ath6kl_wmi_bssfilter_cmd(
7681b1e6ee3SJouni Malinen 			ar->wmi,
769bdcd8170SKalle Valo 			(test_bit(CONNECTED, &ar->flag) ?
7701b1e6ee3SJouni Malinen 			 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
7711b1e6ee3SJouni Malinen 		if (ret) {
772bdcd8170SKalle Valo 			ath6kl_err("couldn't set bss filtering\n");
7731b1e6ee3SJouni Malinen 			return ret;
774bdcd8170SKalle Valo 		}
775bdcd8170SKalle Valo 	}
776bdcd8170SKalle Valo 
777bdcd8170SKalle Valo 	if (request->n_ssids && request->ssids[0].ssid_len) {
778bdcd8170SKalle Valo 		u8 i;
779bdcd8170SKalle Valo 
780bdcd8170SKalle Valo 		if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
781bdcd8170SKalle Valo 			request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
782bdcd8170SKalle Valo 
783bdcd8170SKalle Valo 		for (i = 0; i < request->n_ssids; i++)
784bdcd8170SKalle Valo 			ath6kl_wmi_probedssid_cmd(ar->wmi, i + 1,
785bdcd8170SKalle Valo 						  SPECIFIC_SSID_FLAG,
786bdcd8170SKalle Valo 						  request->ssids[i].ssid_len,
787bdcd8170SKalle Valo 						  request->ssids[i].ssid);
788bdcd8170SKalle Valo 	}
789bdcd8170SKalle Valo 
790b84da8c7SJouni Malinen 	if (request->ie) {
791b84da8c7SJouni Malinen 		ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_REQ,
792b84da8c7SJouni Malinen 					       request->ie, request->ie_len);
793b84da8c7SJouni Malinen 		if (ret) {
794b84da8c7SJouni Malinen 			ath6kl_err("failed to set Probe Request appie for "
795b84da8c7SJouni Malinen 				   "scan");
796b84da8c7SJouni Malinen 			return ret;
797b84da8c7SJouni Malinen 		}
798b84da8c7SJouni Malinen 	}
799b84da8c7SJouni Malinen 
80011869befSJouni Malinen 	/*
80111869befSJouni Malinen 	 * Scan only the requested channels if the request specifies a set of
80211869befSJouni Malinen 	 * channels. If the list is longer than the target supports, do not
80311869befSJouni Malinen 	 * configure the list and instead, scan all available channels.
80411869befSJouni Malinen 	 */
80511869befSJouni Malinen 	if (request->n_channels > 0 &&
80611869befSJouni Malinen 	    request->n_channels <= WMI_MAX_CHANNELS) {
8071276c9efSEdward Lu 		u8 i;
8081276c9efSEdward Lu 
80911869befSJouni Malinen 		n_channels = request->n_channels;
8101276c9efSEdward Lu 
8111276c9efSEdward Lu 		channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
8121276c9efSEdward Lu 		if (channels == NULL) {
8131276c9efSEdward Lu 			ath6kl_warn("failed to set scan channels, "
8141276c9efSEdward Lu 				    "scan all channels");
8151276c9efSEdward Lu 			n_channels = 0;
8161276c9efSEdward Lu 		}
8171276c9efSEdward Lu 
8181276c9efSEdward Lu 		for (i = 0; i < n_channels; i++)
8191276c9efSEdward Lu 			channels[i] = request->channels[i]->center_freq;
8201276c9efSEdward Lu 	}
8211276c9efSEdward Lu 
8221b1e6ee3SJouni Malinen 	ret = ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0,
8231b1e6ee3SJouni Malinen 				       false, 0, 0, n_channels, channels);
8241b1e6ee3SJouni Malinen 	if (ret)
825bdcd8170SKalle Valo 		ath6kl_err("wmi_startscan_cmd failed\n");
82611869befSJouni Malinen 	else
827bdcd8170SKalle Valo 		ar->scan_req = request;
828bdcd8170SKalle Valo 
8291276c9efSEdward Lu 	kfree(channels);
8301276c9efSEdward Lu 
831bdcd8170SKalle Valo 	return ret;
832bdcd8170SKalle Valo }
833bdcd8170SKalle Valo 
834bdcd8170SKalle Valo void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status)
835bdcd8170SKalle Valo {
8366fd1eaceSKalle Valo 	int i;
837bdcd8170SKalle Valo 
838bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status %d\n", __func__, status);
839bdcd8170SKalle Valo 
8406fd1eaceSKalle Valo 	if (!ar->scan_req)
8416fd1eaceSKalle Valo 		return;
8426fd1eaceSKalle Valo 
8436fd1eaceSKalle Valo 	if ((status == -ECANCELED) || (status == -EBUSY)) {
8446fd1eaceSKalle Valo 		cfg80211_scan_done(ar->scan_req, true);
8456fd1eaceSKalle Valo 		goto out;
8466fd1eaceSKalle Valo 	}
8476fd1eaceSKalle Valo 
848bdcd8170SKalle Valo 	/* Translate data to cfg80211 mgmt format */
8498a8bc5a4SVasanthakumar Thiagarajan 	wlan_iterate_nodes(&ar->scan_table, ar->wdev->wiphy);
850bdcd8170SKalle Valo 
8516fd1eaceSKalle Valo 	cfg80211_scan_done(ar->scan_req, false);
852bdcd8170SKalle Valo 
853bdcd8170SKalle Valo 	if (ar->scan_req->n_ssids && ar->scan_req->ssids[0].ssid_len) {
854bdcd8170SKalle Valo 		for (i = 0; i < ar->scan_req->n_ssids; i++) {
855bdcd8170SKalle Valo 			ath6kl_wmi_probedssid_cmd(ar->wmi, i + 1,
856bdcd8170SKalle Valo 						  DISABLE_SSID_FLAG,
857bdcd8170SKalle Valo 						  0, NULL);
858bdcd8170SKalle Valo 		}
859bdcd8170SKalle Valo 	}
8606fd1eaceSKalle Valo 
8616fd1eaceSKalle Valo out:
862bdcd8170SKalle Valo 	ar->scan_req = NULL;
863bdcd8170SKalle Valo }
864bdcd8170SKalle Valo 
865bdcd8170SKalle Valo static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
866bdcd8170SKalle Valo 				   u8 key_index, bool pairwise,
867bdcd8170SKalle Valo 				   const u8 *mac_addr,
868bdcd8170SKalle Valo 				   struct key_params *params)
869bdcd8170SKalle Valo {
870bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
871bdcd8170SKalle Valo 	struct ath6kl_key *key = NULL;
872bdcd8170SKalle Valo 	u8 key_usage;
873bdcd8170SKalle Valo 	u8 key_type;
874bdcd8170SKalle Valo 	int status = 0;
875bdcd8170SKalle Valo 
876bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
877bdcd8170SKalle Valo 		return -EIO;
878bdcd8170SKalle Valo 
879bdcd8170SKalle Valo 	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
880bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
881bdcd8170SKalle Valo 			   "%s: key index %d out of bounds\n", __func__,
882bdcd8170SKalle Valo 			   key_index);
883bdcd8170SKalle Valo 		return -ENOENT;
884bdcd8170SKalle Valo 	}
885bdcd8170SKalle Valo 
886bdcd8170SKalle Valo 	key = &ar->keys[key_index];
887bdcd8170SKalle Valo 	memset(key, 0, sizeof(struct ath6kl_key));
888bdcd8170SKalle Valo 
889bdcd8170SKalle Valo 	if (pairwise)
890bdcd8170SKalle Valo 		key_usage = PAIRWISE_USAGE;
891bdcd8170SKalle Valo 	else
892bdcd8170SKalle Valo 		key_usage = GROUP_USAGE;
893bdcd8170SKalle Valo 
894bdcd8170SKalle Valo 	if (params) {
895bdcd8170SKalle Valo 		if (params->key_len > WLAN_MAX_KEY_LEN ||
896bdcd8170SKalle Valo 		    params->seq_len > sizeof(key->seq))
897bdcd8170SKalle Valo 			return -EINVAL;
898bdcd8170SKalle Valo 
899bdcd8170SKalle Valo 		key->key_len = params->key_len;
900bdcd8170SKalle Valo 		memcpy(key->key, params->key, key->key_len);
901bdcd8170SKalle Valo 		key->seq_len = params->seq_len;
902bdcd8170SKalle Valo 		memcpy(key->seq, params->seq, key->seq_len);
903bdcd8170SKalle Valo 		key->cipher = params->cipher;
904bdcd8170SKalle Valo 	}
905bdcd8170SKalle Valo 
906bdcd8170SKalle Valo 	switch (key->cipher) {
907bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_WEP40:
908bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_WEP104:
909bdcd8170SKalle Valo 		key_type = WEP_CRYPT;
910bdcd8170SKalle Valo 		break;
911bdcd8170SKalle Valo 
912bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_TKIP:
913bdcd8170SKalle Valo 		key_type = TKIP_CRYPT;
914bdcd8170SKalle Valo 		break;
915bdcd8170SKalle Valo 
916bdcd8170SKalle Valo 	case WLAN_CIPHER_SUITE_CCMP:
917bdcd8170SKalle Valo 		key_type = AES_CRYPT;
918bdcd8170SKalle Valo 		break;
919bdcd8170SKalle Valo 
920bdcd8170SKalle Valo 	default:
921bdcd8170SKalle Valo 		return -ENOTSUPP;
922bdcd8170SKalle Valo 	}
923bdcd8170SKalle Valo 
924bdcd8170SKalle Valo 	if (((ar->auth_mode == WPA_PSK_AUTH)
925bdcd8170SKalle Valo 	     || (ar->auth_mode == WPA2_PSK_AUTH))
926bdcd8170SKalle Valo 	    && (key_usage & GROUP_USAGE))
927bdcd8170SKalle Valo 		del_timer(&ar->disconnect_timer);
928bdcd8170SKalle Valo 
929bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
930bdcd8170SKalle Valo 		   "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
931bdcd8170SKalle Valo 		   __func__, key_index, key->key_len, key_type,
932bdcd8170SKalle Valo 		   key_usage, key->seq_len);
933bdcd8170SKalle Valo 
934bdcd8170SKalle Valo 	ar->def_txkey_index = key_index;
9359a5b1318SJouni Malinen 
9369a5b1318SJouni Malinen 	if (ar->nw_type == AP_NETWORK && !pairwise &&
9379a5b1318SJouni Malinen 	    (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
9389a5b1318SJouni Malinen 		ar->ap_mode_bkey.valid = true;
9399a5b1318SJouni Malinen 		ar->ap_mode_bkey.key_index = key_index;
9409a5b1318SJouni Malinen 		ar->ap_mode_bkey.key_type = key_type;
9419a5b1318SJouni Malinen 		ar->ap_mode_bkey.key_len = key->key_len;
9429a5b1318SJouni Malinen 		memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
9439a5b1318SJouni Malinen 		if (!test_bit(CONNECTED, &ar->flag)) {
9449a5b1318SJouni Malinen 			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
9459a5b1318SJouni Malinen 				   "key configuration until AP mode has been "
9469a5b1318SJouni Malinen 				   "started\n");
9479a5b1318SJouni Malinen 			/*
9489a5b1318SJouni Malinen 			 * The key will be set in ath6kl_connect_ap_mode() once
9499a5b1318SJouni Malinen 			 * the connected event is received from the target.
9509a5b1318SJouni Malinen 			 */
9519a5b1318SJouni Malinen 			return 0;
9529a5b1318SJouni Malinen 		}
9539a5b1318SJouni Malinen 	}
9549a5b1318SJouni Malinen 
955bdcd8170SKalle Valo 	status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index,
956bdcd8170SKalle Valo 				       key_type, key_usage, key->key_len,
957bdcd8170SKalle Valo 				       key->seq, key->key, KEY_OP_INIT_VAL,
958bdcd8170SKalle Valo 				       (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
959bdcd8170SKalle Valo 
960bdcd8170SKalle Valo 	if (status)
961bdcd8170SKalle Valo 		return -EIO;
962bdcd8170SKalle Valo 
963bdcd8170SKalle Valo 	return 0;
964bdcd8170SKalle Valo }
965bdcd8170SKalle Valo 
966bdcd8170SKalle Valo static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
967bdcd8170SKalle Valo 				   u8 key_index, bool pairwise,
968bdcd8170SKalle Valo 				   const u8 *mac_addr)
969bdcd8170SKalle Valo {
970bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
971bdcd8170SKalle Valo 
972bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
973bdcd8170SKalle Valo 
974bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
975bdcd8170SKalle Valo 		return -EIO;
976bdcd8170SKalle Valo 
977bdcd8170SKalle Valo 	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
978bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
979bdcd8170SKalle Valo 			   "%s: key index %d out of bounds\n", __func__,
980bdcd8170SKalle Valo 			   key_index);
981bdcd8170SKalle Valo 		return -ENOENT;
982bdcd8170SKalle Valo 	}
983bdcd8170SKalle Valo 
984bdcd8170SKalle Valo 	if (!ar->keys[key_index].key_len) {
985bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
986bdcd8170SKalle Valo 			   "%s: index %d is empty\n", __func__, key_index);
987bdcd8170SKalle Valo 		return 0;
988bdcd8170SKalle Valo 	}
989bdcd8170SKalle Valo 
990bdcd8170SKalle Valo 	ar->keys[key_index].key_len = 0;
991bdcd8170SKalle Valo 
992bdcd8170SKalle Valo 	return ath6kl_wmi_deletekey_cmd(ar->wmi, key_index);
993bdcd8170SKalle Valo }
994bdcd8170SKalle Valo 
995bdcd8170SKalle Valo static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
996bdcd8170SKalle Valo 				   u8 key_index, bool pairwise,
997bdcd8170SKalle Valo 				   const u8 *mac_addr, void *cookie,
998bdcd8170SKalle Valo 				   void (*callback) (void *cookie,
999bdcd8170SKalle Valo 						     struct key_params *))
1000bdcd8170SKalle Valo {
1001bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
1002bdcd8170SKalle Valo 	struct ath6kl_key *key = NULL;
1003bdcd8170SKalle Valo 	struct key_params params;
1004bdcd8170SKalle Valo 
1005bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1006bdcd8170SKalle Valo 
1007bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
1008bdcd8170SKalle Valo 		return -EIO;
1009bdcd8170SKalle Valo 
1010bdcd8170SKalle Valo 	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1011bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1012bdcd8170SKalle Valo 			   "%s: key index %d out of bounds\n", __func__,
1013bdcd8170SKalle Valo 			   key_index);
1014bdcd8170SKalle Valo 		return -ENOENT;
1015bdcd8170SKalle Valo 	}
1016bdcd8170SKalle Valo 
1017bdcd8170SKalle Valo 	key = &ar->keys[key_index];
1018bdcd8170SKalle Valo 	memset(&params, 0, sizeof(params));
1019bdcd8170SKalle Valo 	params.cipher = key->cipher;
1020bdcd8170SKalle Valo 	params.key_len = key->key_len;
1021bdcd8170SKalle Valo 	params.seq_len = key->seq_len;
1022bdcd8170SKalle Valo 	params.seq = key->seq;
1023bdcd8170SKalle Valo 	params.key = key->key;
1024bdcd8170SKalle Valo 
1025bdcd8170SKalle Valo 	callback(cookie, &params);
1026bdcd8170SKalle Valo 
1027bdcd8170SKalle Valo 	return key->key_len ? 0 : -ENOENT;
1028bdcd8170SKalle Valo }
1029bdcd8170SKalle Valo 
1030bdcd8170SKalle Valo static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1031bdcd8170SKalle Valo 					   struct net_device *ndev,
1032bdcd8170SKalle Valo 					   u8 key_index, bool unicast,
1033bdcd8170SKalle Valo 					   bool multicast)
1034bdcd8170SKalle Valo {
1035bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
1036bdcd8170SKalle Valo 	struct ath6kl_key *key = NULL;
1037bdcd8170SKalle Valo 	int status = 0;
1038bdcd8170SKalle Valo 	u8 key_usage;
1039229ed6b5SEdward Lu 	enum crypto_type key_type = NONE_CRYPT;
1040bdcd8170SKalle Valo 
1041bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1042bdcd8170SKalle Valo 
1043bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
1044bdcd8170SKalle Valo 		return -EIO;
1045bdcd8170SKalle Valo 
1046bdcd8170SKalle Valo 	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1047bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1048bdcd8170SKalle Valo 			   "%s: key index %d out of bounds\n",
1049bdcd8170SKalle Valo 			   __func__, key_index);
1050bdcd8170SKalle Valo 		return -ENOENT;
1051bdcd8170SKalle Valo 	}
1052bdcd8170SKalle Valo 
1053bdcd8170SKalle Valo 	if (!ar->keys[key_index].key_len) {
1054bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1055bdcd8170SKalle Valo 			   __func__, key_index);
1056bdcd8170SKalle Valo 		return -EINVAL;
1057bdcd8170SKalle Valo 	}
1058bdcd8170SKalle Valo 
1059bdcd8170SKalle Valo 	ar->def_txkey_index = key_index;
1060bdcd8170SKalle Valo 	key = &ar->keys[ar->def_txkey_index];
1061bdcd8170SKalle Valo 	key_usage = GROUP_USAGE;
1062bdcd8170SKalle Valo 	if (ar->prwise_crypto == WEP_CRYPT)
1063bdcd8170SKalle Valo 		key_usage |= TX_USAGE;
1064229ed6b5SEdward Lu 	if (unicast)
1065229ed6b5SEdward Lu 		key_type = ar->prwise_crypto;
1066229ed6b5SEdward Lu 	if (multicast)
1067229ed6b5SEdward Lu 		key_type = ar->grp_crypto;
1068bdcd8170SKalle Valo 
10699a5b1318SJouni Malinen 	if (ar->nw_type == AP_NETWORK && !test_bit(CONNECTED, &ar->flag))
10709a5b1318SJouni Malinen 		return 0; /* Delay until AP mode has been started */
10719a5b1318SJouni Malinen 
1072bdcd8170SKalle Valo 	status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index,
1073229ed6b5SEdward Lu 				       key_type, key_usage,
1074bdcd8170SKalle Valo 				       key->key_len, key->seq, key->key,
1075bdcd8170SKalle Valo 				       KEY_OP_INIT_VAL, NULL,
1076bdcd8170SKalle Valo 				       SYNC_BOTH_WMIFLAG);
1077bdcd8170SKalle Valo 	if (status)
1078bdcd8170SKalle Valo 		return -EIO;
1079bdcd8170SKalle Valo 
1080bdcd8170SKalle Valo 	return 0;
1081bdcd8170SKalle Valo }
1082bdcd8170SKalle Valo 
1083bdcd8170SKalle Valo void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl *ar, u8 keyid,
1084bdcd8170SKalle Valo 				       bool ismcast)
1085bdcd8170SKalle Valo {
1086bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1087bdcd8170SKalle Valo 		   "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1088bdcd8170SKalle Valo 
1089bdcd8170SKalle Valo 	cfg80211_michael_mic_failure(ar->net_dev, ar->bssid,
1090bdcd8170SKalle Valo 				     (ismcast ? NL80211_KEYTYPE_GROUP :
1091bdcd8170SKalle Valo 				      NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1092bdcd8170SKalle Valo 				     GFP_KERNEL);
1093bdcd8170SKalle Valo }
1094bdcd8170SKalle Valo 
1095bdcd8170SKalle Valo static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1096bdcd8170SKalle Valo {
1097bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
1098bdcd8170SKalle Valo 	int ret;
1099bdcd8170SKalle Valo 
1100bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1101bdcd8170SKalle Valo 		   changed);
1102bdcd8170SKalle Valo 
1103bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
1104bdcd8170SKalle Valo 		return -EIO;
1105bdcd8170SKalle Valo 
1106bdcd8170SKalle Valo 	if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1107bdcd8170SKalle Valo 		ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1108bdcd8170SKalle Valo 		if (ret != 0) {
1109bdcd8170SKalle Valo 			ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1110bdcd8170SKalle Valo 			return -EIO;
1111bdcd8170SKalle Valo 		}
1112bdcd8170SKalle Valo 	}
1113bdcd8170SKalle Valo 
1114bdcd8170SKalle Valo 	return 0;
1115bdcd8170SKalle Valo }
1116bdcd8170SKalle Valo 
1117bdcd8170SKalle Valo /*
1118bdcd8170SKalle Valo  * The type nl80211_tx_power_setting replaces the following
1119bdcd8170SKalle Valo  * data type from 2.6.36 onwards
1120bdcd8170SKalle Valo */
1121bdcd8170SKalle Valo static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1122bdcd8170SKalle Valo 				       enum nl80211_tx_power_setting type,
1123bdcd8170SKalle Valo 				       int dbm)
1124bdcd8170SKalle Valo {
1125bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
1126bdcd8170SKalle Valo 	u8 ath6kl_dbm;
1127bdcd8170SKalle Valo 
1128bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1129bdcd8170SKalle Valo 		   type, dbm);
1130bdcd8170SKalle Valo 
1131bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
1132bdcd8170SKalle Valo 		return -EIO;
1133bdcd8170SKalle Valo 
1134bdcd8170SKalle Valo 	switch (type) {
1135bdcd8170SKalle Valo 	case NL80211_TX_POWER_AUTOMATIC:
1136bdcd8170SKalle Valo 		return 0;
1137bdcd8170SKalle Valo 	case NL80211_TX_POWER_LIMITED:
1138bdcd8170SKalle Valo 		ar->tx_pwr = ath6kl_dbm = dbm;
1139bdcd8170SKalle Valo 		break;
1140bdcd8170SKalle Valo 	default:
1141bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1142bdcd8170SKalle Valo 			   __func__, type);
1143bdcd8170SKalle Valo 		return -EOPNOTSUPP;
1144bdcd8170SKalle Valo 	}
1145bdcd8170SKalle Valo 
1146bdcd8170SKalle Valo 	ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, ath6kl_dbm);
1147bdcd8170SKalle Valo 
1148bdcd8170SKalle Valo 	return 0;
1149bdcd8170SKalle Valo }
1150bdcd8170SKalle Valo 
1151bdcd8170SKalle Valo static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1152bdcd8170SKalle Valo {
1153bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
1154bdcd8170SKalle Valo 
1155bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
1156bdcd8170SKalle Valo 		return -EIO;
1157bdcd8170SKalle Valo 
1158bdcd8170SKalle Valo 	if (test_bit(CONNECTED, &ar->flag)) {
1159bdcd8170SKalle Valo 		ar->tx_pwr = 0;
1160bdcd8170SKalle Valo 
1161bdcd8170SKalle Valo 		if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi) != 0) {
1162bdcd8170SKalle Valo 			ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1163bdcd8170SKalle Valo 			return -EIO;
1164bdcd8170SKalle Valo 		}
1165bdcd8170SKalle Valo 
1166bdcd8170SKalle Valo 		wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1167bdcd8170SKalle Valo 						 5 * HZ);
1168bdcd8170SKalle Valo 
1169bdcd8170SKalle Valo 		if (signal_pending(current)) {
1170bdcd8170SKalle Valo 			ath6kl_err("target did not respond\n");
1171bdcd8170SKalle Valo 			return -EINTR;
1172bdcd8170SKalle Valo 		}
1173bdcd8170SKalle Valo 	}
1174bdcd8170SKalle Valo 
1175bdcd8170SKalle Valo 	*dbm = ar->tx_pwr;
1176bdcd8170SKalle Valo 	return 0;
1177bdcd8170SKalle Valo }
1178bdcd8170SKalle Valo 
1179bdcd8170SKalle Valo static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1180bdcd8170SKalle Valo 					  struct net_device *dev,
1181bdcd8170SKalle Valo 					  bool pmgmt, int timeout)
1182bdcd8170SKalle Valo {
1183bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(dev);
1184bdcd8170SKalle Valo 	struct wmi_power_mode_cmd mode;
1185bdcd8170SKalle Valo 
1186bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1187bdcd8170SKalle Valo 		   __func__, pmgmt, timeout);
1188bdcd8170SKalle Valo 
1189bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
1190bdcd8170SKalle Valo 		return -EIO;
1191bdcd8170SKalle Valo 
1192bdcd8170SKalle Valo 	if (pmgmt) {
1193bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1194bdcd8170SKalle Valo 		mode.pwr_mode = REC_POWER;
1195bdcd8170SKalle Valo 	} else {
1196bdcd8170SKalle Valo 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1197bdcd8170SKalle Valo 		mode.pwr_mode = MAX_PERF_POWER;
1198bdcd8170SKalle Valo 	}
1199bdcd8170SKalle Valo 
1200bdcd8170SKalle Valo 	if (ath6kl_wmi_powermode_cmd(ar->wmi, mode.pwr_mode) != 0) {
1201bdcd8170SKalle Valo 		ath6kl_err("wmi_powermode_cmd failed\n");
1202bdcd8170SKalle Valo 		return -EIO;
1203bdcd8170SKalle Valo 	}
1204bdcd8170SKalle Valo 
1205bdcd8170SKalle Valo 	return 0;
1206bdcd8170SKalle Valo }
1207bdcd8170SKalle Valo 
1208bdcd8170SKalle Valo static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1209bdcd8170SKalle Valo 					struct net_device *ndev,
1210bdcd8170SKalle Valo 					enum nl80211_iftype type, u32 *flags,
1211bdcd8170SKalle Valo 					struct vif_params *params)
1212bdcd8170SKalle Valo {
1213bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(ndev);
1214bdcd8170SKalle Valo 	struct wireless_dev *wdev = ar->wdev;
1215bdcd8170SKalle Valo 
1216bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1217bdcd8170SKalle Valo 
1218bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
1219bdcd8170SKalle Valo 		return -EIO;
1220bdcd8170SKalle Valo 
1221bdcd8170SKalle Valo 	switch (type) {
1222bdcd8170SKalle Valo 	case NL80211_IFTYPE_STATION:
1223bdcd8170SKalle Valo 		ar->next_mode = INFRA_NETWORK;
1224bdcd8170SKalle Valo 		break;
1225bdcd8170SKalle Valo 	case NL80211_IFTYPE_ADHOC:
1226bdcd8170SKalle Valo 		ar->next_mode = ADHOC_NETWORK;
1227bdcd8170SKalle Valo 		break;
12286e4604c8SJouni Malinen 	case NL80211_IFTYPE_AP:
12296e4604c8SJouni Malinen 		ar->next_mode = AP_NETWORK;
12306e4604c8SJouni Malinen 		break;
12316b5e5d25SJouni Malinen 	case NL80211_IFTYPE_P2P_CLIENT:
12326b5e5d25SJouni Malinen 		ar->next_mode = INFRA_NETWORK;
12336b5e5d25SJouni Malinen 		break;
12346b5e5d25SJouni Malinen 	case NL80211_IFTYPE_P2P_GO:
12356b5e5d25SJouni Malinen 		ar->next_mode = AP_NETWORK;
12366b5e5d25SJouni Malinen 		break;
1237bdcd8170SKalle Valo 	default:
1238bdcd8170SKalle Valo 		ath6kl_err("invalid interface type %u\n", type);
1239bdcd8170SKalle Valo 		return -EOPNOTSUPP;
1240bdcd8170SKalle Valo 	}
1241bdcd8170SKalle Valo 
1242bdcd8170SKalle Valo 	wdev->iftype = type;
1243bdcd8170SKalle Valo 
1244bdcd8170SKalle Valo 	return 0;
1245bdcd8170SKalle Valo }
1246bdcd8170SKalle Valo 
1247bdcd8170SKalle Valo static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1248bdcd8170SKalle Valo 				     struct net_device *dev,
1249bdcd8170SKalle Valo 				     struct cfg80211_ibss_params *ibss_param)
1250bdcd8170SKalle Valo {
1251bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(dev);
1252bdcd8170SKalle Valo 	int status;
1253bdcd8170SKalle Valo 
1254bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
1255bdcd8170SKalle Valo 		return -EIO;
1256bdcd8170SKalle Valo 
1257bdcd8170SKalle Valo 	ar->ssid_len = ibss_param->ssid_len;
1258bdcd8170SKalle Valo 	memcpy(ar->ssid, ibss_param->ssid, ar->ssid_len);
1259bdcd8170SKalle Valo 
1260bdcd8170SKalle Valo 	if (ibss_param->channel)
1261bdcd8170SKalle Valo 		ar->ch_hint = ibss_param->channel->center_freq;
1262bdcd8170SKalle Valo 
1263bdcd8170SKalle Valo 	if (ibss_param->channel_fixed) {
1264bdcd8170SKalle Valo 		/*
1265bdcd8170SKalle Valo 		 * TODO: channel_fixed: The channel should be fixed, do not
1266bdcd8170SKalle Valo 		 * search for IBSSs to join on other channels. Target
1267bdcd8170SKalle Valo 		 * firmware does not support this feature, needs to be
1268bdcd8170SKalle Valo 		 * updated.
1269bdcd8170SKalle Valo 		 */
1270bdcd8170SKalle Valo 		return -EOPNOTSUPP;
1271bdcd8170SKalle Valo 	}
1272bdcd8170SKalle Valo 
1273bdcd8170SKalle Valo 	memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
1274bdcd8170SKalle Valo 	if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
1275bdcd8170SKalle Valo 		memcpy(ar->req_bssid, ibss_param->bssid, sizeof(ar->req_bssid));
1276bdcd8170SKalle Valo 
1277bdcd8170SKalle Valo 	ath6kl_set_wpa_version(ar, 0);
1278bdcd8170SKalle Valo 
1279bdcd8170SKalle Valo 	status = ath6kl_set_auth_type(ar, NL80211_AUTHTYPE_OPEN_SYSTEM);
1280bdcd8170SKalle Valo 	if (status)
1281bdcd8170SKalle Valo 		return status;
1282bdcd8170SKalle Valo 
1283bdcd8170SKalle Valo 	if (ibss_param->privacy) {
1284bdcd8170SKalle Valo 		ath6kl_set_cipher(ar, WLAN_CIPHER_SUITE_WEP40, true);
1285bdcd8170SKalle Valo 		ath6kl_set_cipher(ar, WLAN_CIPHER_SUITE_WEP40, false);
1286bdcd8170SKalle Valo 	} else {
1287bdcd8170SKalle Valo 		ath6kl_set_cipher(ar, 0, true);
1288bdcd8170SKalle Valo 		ath6kl_set_cipher(ar, 0, false);
1289bdcd8170SKalle Valo 	}
1290bdcd8170SKalle Valo 
1291bdcd8170SKalle Valo 	ar->nw_type = ar->next_mode;
1292bdcd8170SKalle Valo 
1293bdcd8170SKalle Valo 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1294bdcd8170SKalle Valo 		   "%s: connect called with authmode %d dot11 auth %d"
1295bdcd8170SKalle Valo 		   " PW crypto %d PW crypto len %d GRP crypto %d"
1296bdcd8170SKalle Valo 		   " GRP crypto len %d channel hint %u\n",
1297bdcd8170SKalle Valo 		   __func__,
1298bdcd8170SKalle Valo 		   ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto,
1299bdcd8170SKalle Valo 		   ar->prwise_crypto_len, ar->grp_crypto,
130038acde3cSEdward Lu 		   ar->grp_crypto_len, ar->ch_hint);
1301bdcd8170SKalle Valo 
1302bdcd8170SKalle Valo 	status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type,
1303bdcd8170SKalle Valo 					ar->dot11_auth_mode, ar->auth_mode,
1304bdcd8170SKalle Valo 					ar->prwise_crypto,
1305bdcd8170SKalle Valo 					ar->prwise_crypto_len,
130638acde3cSEdward Lu 					ar->grp_crypto, ar->grp_crypto_len,
1307bdcd8170SKalle Valo 					ar->ssid_len, ar->ssid,
1308bdcd8170SKalle Valo 					ar->req_bssid, ar->ch_hint,
1309bdcd8170SKalle Valo 					ar->connect_ctrl_flags);
1310bdcd8170SKalle Valo 	set_bit(CONNECT_PEND, &ar->flag);
1311bdcd8170SKalle Valo 
1312bdcd8170SKalle Valo 	return 0;
1313bdcd8170SKalle Valo }
1314bdcd8170SKalle Valo 
1315bdcd8170SKalle Valo static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1316bdcd8170SKalle Valo 				      struct net_device *dev)
1317bdcd8170SKalle Valo {
1318bdcd8170SKalle Valo 	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
1319bdcd8170SKalle Valo 
1320bdcd8170SKalle Valo 	if (!ath6kl_cfg80211_ready(ar))
1321bdcd8170SKalle Valo 		return -EIO;
1322bdcd8170SKalle Valo 
1323bdcd8170SKalle Valo 	ath6kl_disconnect(ar);
1324bdcd8170SKalle Valo 	memset(ar->ssid, 0, sizeof(ar->ssid));
1325bdcd8170SKalle Valo 	ar->ssid_len = 0;
1326bdcd8170SKalle Valo 
1327bdcd8170SKalle Valo 	return 0;
1328bdcd8170SKalle Valo }
1329bdcd8170SKalle Valo 
1330bdcd8170SKalle Valo static const u32 cipher_suites[] = {
1331bdcd8170SKalle Valo 	WLAN_CIPHER_SUITE_WEP40,
1332bdcd8170SKalle Valo 	WLAN_CIPHER_SUITE_WEP104,
1333bdcd8170SKalle Valo 	WLAN_CIPHER_SUITE_TKIP,
1334bdcd8170SKalle Valo 	WLAN_CIPHER_SUITE_CCMP,
1335bdcd8170SKalle Valo };
1336bdcd8170SKalle Valo 
1337bdcd8170SKalle Valo static bool is_rate_legacy(s32 rate)
1338bdcd8170SKalle Valo {
1339bdcd8170SKalle Valo 	static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1340bdcd8170SKalle Valo 		6000, 9000, 12000, 18000, 24000,
1341bdcd8170SKalle Valo 		36000, 48000, 54000
1342bdcd8170SKalle Valo 	};
1343bdcd8170SKalle Valo 	u8 i;
1344bdcd8170SKalle Valo 
1345bdcd8170SKalle Valo 	for (i = 0; i < ARRAY_SIZE(legacy); i++)
1346bdcd8170SKalle Valo 		if (rate == legacy[i])
1347bdcd8170SKalle Valo 			return true;
1348bdcd8170SKalle Valo 
1349bdcd8170SKalle Valo 	return false;
1350bdcd8170SKalle Valo }
1351bdcd8170SKalle Valo 
1352bdcd8170SKalle Valo static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1353bdcd8170SKalle Valo {
1354bdcd8170SKalle Valo 	static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1355bdcd8170SKalle Valo 		52000, 58500, 65000, 72200
1356bdcd8170SKalle Valo 	};
1357bdcd8170SKalle Valo 	u8 i;
1358bdcd8170SKalle Valo 
1359bdcd8170SKalle Valo 	for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1360bdcd8170SKalle Valo 		if (rate == ht20[i]) {
1361bdcd8170SKalle Valo 			if (i == ARRAY_SIZE(ht20) - 1)
1362bdcd8170SKalle Valo 				/* last rate uses sgi */
1363bdcd8170SKalle Valo 				*sgi = true;
1364bdcd8170SKalle Valo 			else
1365bdcd8170SKalle Valo 				*sgi = false;
1366bdcd8170SKalle Valo 
1367bdcd8170SKalle Valo 			*mcs = i;
1368bdcd8170SKalle Valo 			return true;
1369bdcd8170SKalle Valo 		}
1370bdcd8170SKalle Valo 	}
1371bdcd8170SKalle Valo 	return false;
1372bdcd8170SKalle Valo }
1373bdcd8170SKalle Valo 
1374bdcd8170SKalle Valo static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1375bdcd8170SKalle Valo {
1376bdcd8170SKalle Valo 	static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1377bdcd8170SKalle Valo 		81000, 108000, 121500, 135000,
1378bdcd8170SKalle Valo 		150000
1379bdcd8170SKalle Valo 	};
1380bdcd8170SKalle Valo 	u8 i;
1381bdcd8170SKalle Valo 
1382bdcd8170SKalle Valo 	for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1383bdcd8170SKalle Valo 		if (rate == ht40[i]) {
1384bdcd8170SKalle Valo 			if (i == ARRAY_SIZE(ht40) - 1)
1385bdcd8170SKalle Valo 				/* last rate uses sgi */
1386bdcd8170SKalle Valo 				*sgi = true;
1387bdcd8170SKalle Valo 			else
1388bdcd8170SKalle Valo 				*sgi = false;
1389bdcd8170SKalle Valo 
1390bdcd8170SKalle Valo 			*mcs = i;
1391bdcd8170SKalle Valo 			return true;
1392bdcd8170SKalle Valo 		}
1393bdcd8170SKalle Valo 	}
1394bdcd8170SKalle Valo 
1395bdcd8170SKalle Valo 	return false;
1396bdcd8170SKalle Valo }
1397bdcd8170SKalle Valo 
1398bdcd8170SKalle Valo static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1399bdcd8170SKalle Valo 			      u8 *mac, struct station_info *sinfo)
1400bdcd8170SKalle Valo {
1401bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(dev);
1402bdcd8170SKalle Valo 	long left;
1403bdcd8170SKalle Valo 	bool sgi;
1404bdcd8170SKalle Valo 	s32 rate;
1405bdcd8170SKalle Valo 	int ret;
1406bdcd8170SKalle Valo 	u8 mcs;
1407bdcd8170SKalle Valo 
1408bdcd8170SKalle Valo 	if (memcmp(mac, ar->bssid, ETH_ALEN) != 0)
1409bdcd8170SKalle Valo 		return -ENOENT;
1410bdcd8170SKalle Valo 
1411bdcd8170SKalle Valo 	if (down_interruptible(&ar->sem))
1412bdcd8170SKalle Valo 		return -EBUSY;
1413bdcd8170SKalle Valo 
1414bdcd8170SKalle Valo 	set_bit(STATS_UPDATE_PEND, &ar->flag);
1415bdcd8170SKalle Valo 
1416bdcd8170SKalle Valo 	ret = ath6kl_wmi_get_stats_cmd(ar->wmi);
1417bdcd8170SKalle Valo 
1418bdcd8170SKalle Valo 	if (ret != 0) {
1419bdcd8170SKalle Valo 		up(&ar->sem);
1420bdcd8170SKalle Valo 		return -EIO;
1421bdcd8170SKalle Valo 	}
1422bdcd8170SKalle Valo 
1423bdcd8170SKalle Valo 	left = wait_event_interruptible_timeout(ar->event_wq,
1424bdcd8170SKalle Valo 						!test_bit(STATS_UPDATE_PEND,
1425bdcd8170SKalle Valo 							  &ar->flag),
1426bdcd8170SKalle Valo 						WMI_TIMEOUT);
1427bdcd8170SKalle Valo 
1428bdcd8170SKalle Valo 	up(&ar->sem);
1429bdcd8170SKalle Valo 
1430bdcd8170SKalle Valo 	if (left == 0)
1431bdcd8170SKalle Valo 		return -ETIMEDOUT;
1432bdcd8170SKalle Valo 	else if (left < 0)
1433bdcd8170SKalle Valo 		return left;
1434bdcd8170SKalle Valo 
1435bdcd8170SKalle Valo 	if (ar->target_stats.rx_byte) {
1436bdcd8170SKalle Valo 		sinfo->rx_bytes = ar->target_stats.rx_byte;
1437bdcd8170SKalle Valo 		sinfo->filled |= STATION_INFO_RX_BYTES;
1438bdcd8170SKalle Valo 		sinfo->rx_packets = ar->target_stats.rx_pkt;
1439bdcd8170SKalle Valo 		sinfo->filled |= STATION_INFO_RX_PACKETS;
1440bdcd8170SKalle Valo 	}
1441bdcd8170SKalle Valo 
1442bdcd8170SKalle Valo 	if (ar->target_stats.tx_byte) {
1443bdcd8170SKalle Valo 		sinfo->tx_bytes = ar->target_stats.tx_byte;
1444bdcd8170SKalle Valo 		sinfo->filled |= STATION_INFO_TX_BYTES;
1445bdcd8170SKalle Valo 		sinfo->tx_packets = ar->target_stats.tx_pkt;
1446bdcd8170SKalle Valo 		sinfo->filled |= STATION_INFO_TX_PACKETS;
1447bdcd8170SKalle Valo 	}
1448bdcd8170SKalle Valo 
1449bdcd8170SKalle Valo 	sinfo->signal = ar->target_stats.cs_rssi;
1450bdcd8170SKalle Valo 	sinfo->filled |= STATION_INFO_SIGNAL;
1451bdcd8170SKalle Valo 
1452bdcd8170SKalle Valo 	rate = ar->target_stats.tx_ucast_rate;
1453bdcd8170SKalle Valo 
1454bdcd8170SKalle Valo 	if (is_rate_legacy(rate)) {
1455bdcd8170SKalle Valo 		sinfo->txrate.legacy = rate / 100;
1456bdcd8170SKalle Valo 	} else if (is_rate_ht20(rate, &mcs, &sgi)) {
1457bdcd8170SKalle Valo 		if (sgi) {
1458bdcd8170SKalle Valo 			sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1459bdcd8170SKalle Valo 			sinfo->txrate.mcs = mcs - 1;
1460bdcd8170SKalle Valo 		} else {
1461bdcd8170SKalle Valo 			sinfo->txrate.mcs = mcs;
1462bdcd8170SKalle Valo 		}
1463bdcd8170SKalle Valo 
1464bdcd8170SKalle Valo 		sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1465bdcd8170SKalle Valo 	} else if (is_rate_ht40(rate, &mcs, &sgi)) {
1466bdcd8170SKalle Valo 		if (sgi) {
1467bdcd8170SKalle Valo 			sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1468bdcd8170SKalle Valo 			sinfo->txrate.mcs = mcs - 1;
1469bdcd8170SKalle Valo 		} else {
1470bdcd8170SKalle Valo 			sinfo->txrate.mcs = mcs;
1471bdcd8170SKalle Valo 		}
1472bdcd8170SKalle Valo 
1473bdcd8170SKalle Valo 		sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1474bdcd8170SKalle Valo 		sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1475bdcd8170SKalle Valo 	} else {
1476bdcd8170SKalle Valo 		ath6kl_warn("invalid rate: %d\n", rate);
1477bdcd8170SKalle Valo 		return 0;
1478bdcd8170SKalle Valo 	}
1479bdcd8170SKalle Valo 
1480bdcd8170SKalle Valo 	sinfo->filled |= STATION_INFO_TX_BITRATE;
1481bdcd8170SKalle Valo 
1482bdcd8170SKalle Valo 	return 0;
1483bdcd8170SKalle Valo }
1484bdcd8170SKalle Valo 
1485bdcd8170SKalle Valo static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1486bdcd8170SKalle Valo 			    struct cfg80211_pmksa *pmksa)
1487bdcd8170SKalle Valo {
1488bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(netdev);
1489bdcd8170SKalle Valo 	return ath6kl_wmi_setpmkid_cmd(ar->wmi, pmksa->bssid,
1490bdcd8170SKalle Valo 				       pmksa->pmkid, true);
1491bdcd8170SKalle Valo }
1492bdcd8170SKalle Valo 
1493bdcd8170SKalle Valo static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1494bdcd8170SKalle Valo 			    struct cfg80211_pmksa *pmksa)
1495bdcd8170SKalle Valo {
1496bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(netdev);
1497bdcd8170SKalle Valo 	return ath6kl_wmi_setpmkid_cmd(ar->wmi, pmksa->bssid,
1498bdcd8170SKalle Valo 				       pmksa->pmkid, false);
1499bdcd8170SKalle Valo }
1500bdcd8170SKalle Valo 
1501bdcd8170SKalle Valo static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1502bdcd8170SKalle Valo {
1503bdcd8170SKalle Valo 	struct ath6kl *ar = ath6kl_priv(netdev);
1504bdcd8170SKalle Valo 	if (test_bit(CONNECTED, &ar->flag))
1505bdcd8170SKalle Valo 		return ath6kl_wmi_setpmkid_cmd(ar->wmi, ar->bssid, NULL, false);
1506bdcd8170SKalle Valo 	return 0;
1507bdcd8170SKalle Valo }
1508bdcd8170SKalle Valo 
1509abcb344bSKalle Valo #ifdef CONFIG_PM
1510abcb344bSKalle Valo static int ar6k_cfg80211_suspend(struct wiphy *wiphy,
1511abcb344bSKalle Valo 				 struct cfg80211_wowlan *wow)
1512abcb344bSKalle Valo {
1513abcb344bSKalle Valo 	struct ath6kl *ar = wiphy_priv(wiphy);
1514abcb344bSKalle Valo 
1515abcb344bSKalle Valo 	return ath6kl_hif_suspend(ar);
1516abcb344bSKalle Valo }
1517abcb344bSKalle Valo #endif
1518abcb344bSKalle Valo 
15196a7c9badSJouni Malinen static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
15206a7c9badSJouni Malinen 			      struct ieee80211_channel *chan,
15216a7c9badSJouni Malinen 			      enum nl80211_channel_type channel_type)
15226a7c9badSJouni Malinen {
15236a7c9badSJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
15246a7c9badSJouni Malinen 
15256a7c9badSJouni Malinen 	if (!ath6kl_cfg80211_ready(ar))
15266a7c9badSJouni Malinen 		return -EIO;
15276a7c9badSJouni Malinen 
15286a7c9badSJouni Malinen 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
15296a7c9badSJouni Malinen 		   __func__, chan->center_freq, chan->hw_value);
15306a7c9badSJouni Malinen 	ar->next_chan = chan->center_freq;
15316a7c9badSJouni Malinen 
15326a7c9badSJouni Malinen 	return 0;
15336a7c9badSJouni Malinen }
15346a7c9badSJouni Malinen 
15358bdfbf40SJouni Malinen static bool ath6kl_is_p2p_ie(const u8 *pos)
15368bdfbf40SJouni Malinen {
15378bdfbf40SJouni Malinen 	return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
15388bdfbf40SJouni Malinen 		pos[2] == 0x50 && pos[3] == 0x6f &&
15398bdfbf40SJouni Malinen 		pos[4] == 0x9a && pos[5] == 0x09;
15408bdfbf40SJouni Malinen }
15418bdfbf40SJouni Malinen 
15428bdfbf40SJouni Malinen static int ath6kl_set_ap_probe_resp_ies(struct ath6kl *ar, const u8 *ies,
15438bdfbf40SJouni Malinen 					size_t ies_len)
15448bdfbf40SJouni Malinen {
15458bdfbf40SJouni Malinen 	const u8 *pos;
15468bdfbf40SJouni Malinen 	u8 *buf = NULL;
15478bdfbf40SJouni Malinen 	size_t len = 0;
15488bdfbf40SJouni Malinen 	int ret;
15498bdfbf40SJouni Malinen 
15508bdfbf40SJouni Malinen 	/*
15518bdfbf40SJouni Malinen 	 * Filter out P2P IE(s) since they will be included depending on
15528bdfbf40SJouni Malinen 	 * the Probe Request frame in ath6kl_send_go_probe_resp().
15538bdfbf40SJouni Malinen 	 */
15548bdfbf40SJouni Malinen 
15558bdfbf40SJouni Malinen 	if (ies && ies_len) {
15568bdfbf40SJouni Malinen 		buf = kmalloc(ies_len, GFP_KERNEL);
15578bdfbf40SJouni Malinen 		if (buf == NULL)
15588bdfbf40SJouni Malinen 			return -ENOMEM;
15598bdfbf40SJouni Malinen 		pos = ies;
15608bdfbf40SJouni Malinen 		while (pos + 1 < ies + ies_len) {
15618bdfbf40SJouni Malinen 			if (pos + 2 + pos[1] > ies + ies_len)
15628bdfbf40SJouni Malinen 				break;
15638bdfbf40SJouni Malinen 			if (!ath6kl_is_p2p_ie(pos)) {
15648bdfbf40SJouni Malinen 				memcpy(buf + len, pos, 2 + pos[1]);
15658bdfbf40SJouni Malinen 				len += 2 + pos[1];
15668bdfbf40SJouni Malinen 			}
15678bdfbf40SJouni Malinen 			pos += 2 + pos[1];
15688bdfbf40SJouni Malinen 		}
15698bdfbf40SJouni Malinen 	}
15708bdfbf40SJouni Malinen 
15718bdfbf40SJouni Malinen 	ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_RESP,
15728bdfbf40SJouni Malinen 				       buf, len);
15738bdfbf40SJouni Malinen 	kfree(buf);
15748bdfbf40SJouni Malinen 	return ret;
15758bdfbf40SJouni Malinen }
15768bdfbf40SJouni Malinen 
15776a7c9badSJouni Malinen static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
15786a7c9badSJouni Malinen 			    struct beacon_parameters *info, bool add)
15796a7c9badSJouni Malinen {
15806a7c9badSJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
15816a7c9badSJouni Malinen 	struct ieee80211_mgmt *mgmt;
15826a7c9badSJouni Malinen 	u8 *ies;
15836a7c9badSJouni Malinen 	int ies_len;
15846a7c9badSJouni Malinen 	struct wmi_connect_cmd p;
15856a7c9badSJouni Malinen 	int res;
15866a7c9badSJouni Malinen 	int i;
15876a7c9badSJouni Malinen 
15886a7c9badSJouni Malinen 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
15896a7c9badSJouni Malinen 
15906a7c9badSJouni Malinen 	if (!ath6kl_cfg80211_ready(ar))
15916a7c9badSJouni Malinen 		return -EIO;
15926a7c9badSJouni Malinen 
15936a7c9badSJouni Malinen 	if (ar->next_mode != AP_NETWORK)
15946a7c9badSJouni Malinen 		return -EOPNOTSUPP;
15956a7c9badSJouni Malinen 
15966a7c9badSJouni Malinen 	if (info->beacon_ies) {
15976a7c9badSJouni Malinen 		res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_BEACON,
15986a7c9badSJouni Malinen 					       info->beacon_ies,
15996a7c9badSJouni Malinen 					       info->beacon_ies_len);
16006a7c9badSJouni Malinen 		if (res)
16016a7c9badSJouni Malinen 			return res;
16026a7c9badSJouni Malinen 	}
16036a7c9badSJouni Malinen 	if (info->proberesp_ies) {
16048bdfbf40SJouni Malinen 		res = ath6kl_set_ap_probe_resp_ies(ar, info->proberesp_ies,
16056a7c9badSJouni Malinen 						   info->proberesp_ies_len);
16066a7c9badSJouni Malinen 		if (res)
16076a7c9badSJouni Malinen 			return res;
16086a7c9badSJouni Malinen 	}
16096a7c9badSJouni Malinen 	if (info->assocresp_ies) {
16106a7c9badSJouni Malinen 		res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_ASSOC_RESP,
16116a7c9badSJouni Malinen 					       info->assocresp_ies,
16126a7c9badSJouni Malinen 					       info->assocresp_ies_len);
16136a7c9badSJouni Malinen 		if (res)
16146a7c9badSJouni Malinen 			return res;
16156a7c9badSJouni Malinen 	}
16166a7c9badSJouni Malinen 
16176a7c9badSJouni Malinen 	if (!add)
16186a7c9badSJouni Malinen 		return 0;
16196a7c9badSJouni Malinen 
16209a5b1318SJouni Malinen 	ar->ap_mode_bkey.valid = false;
16219a5b1318SJouni Malinen 
16226a7c9badSJouni Malinen 	/* TODO:
16236a7c9badSJouni Malinen 	 * info->interval
16246a7c9badSJouni Malinen 	 * info->dtim_period
16256a7c9badSJouni Malinen 	 */
16266a7c9badSJouni Malinen 
16276a7c9badSJouni Malinen 	if (info->head == NULL)
16286a7c9badSJouni Malinen 		return -EINVAL;
16296a7c9badSJouni Malinen 	mgmt = (struct ieee80211_mgmt *) info->head;
16306a7c9badSJouni Malinen 	ies = mgmt->u.beacon.variable;
16316a7c9badSJouni Malinen 	if (ies > info->head + info->head_len)
16326a7c9badSJouni Malinen 		return -EINVAL;
16336a7c9badSJouni Malinen 	ies_len = info->head + info->head_len - ies;
16346a7c9badSJouni Malinen 
16356a7c9badSJouni Malinen 	if (info->ssid == NULL)
16366a7c9badSJouni Malinen 		return -EINVAL;
16376a7c9badSJouni Malinen 	memcpy(ar->ssid, info->ssid, info->ssid_len);
16386a7c9badSJouni Malinen 	ar->ssid_len = info->ssid_len;
16396a7c9badSJouni Malinen 	if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
16406a7c9badSJouni Malinen 		return -EOPNOTSUPP; /* TODO */
16416a7c9badSJouni Malinen 
16426a7c9badSJouni Malinen 	ar->dot11_auth_mode = OPEN_AUTH;
16436a7c9badSJouni Malinen 
16446a7c9badSJouni Malinen 	memset(&p, 0, sizeof(p));
16456a7c9badSJouni Malinen 
16466a7c9badSJouni Malinen 	for (i = 0; i < info->crypto.n_akm_suites; i++) {
16476a7c9badSJouni Malinen 		switch (info->crypto.akm_suites[i]) {
16486a7c9badSJouni Malinen 		case WLAN_AKM_SUITE_8021X:
16496a7c9badSJouni Malinen 			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
16506a7c9badSJouni Malinen 				p.auth_mode |= WPA_AUTH;
16516a7c9badSJouni Malinen 			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
16526a7c9badSJouni Malinen 				p.auth_mode |= WPA2_AUTH;
16536a7c9badSJouni Malinen 			break;
16546a7c9badSJouni Malinen 		case WLAN_AKM_SUITE_PSK:
16556a7c9badSJouni Malinen 			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
16566a7c9badSJouni Malinen 				p.auth_mode |= WPA_PSK_AUTH;
16576a7c9badSJouni Malinen 			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
16586a7c9badSJouni Malinen 				p.auth_mode |= WPA2_PSK_AUTH;
16596a7c9badSJouni Malinen 			break;
16606a7c9badSJouni Malinen 		}
16616a7c9badSJouni Malinen 	}
16626a7c9badSJouni Malinen 	if (p.auth_mode == 0)
16636a7c9badSJouni Malinen 		p.auth_mode = NONE_AUTH;
16646a7c9badSJouni Malinen 	ar->auth_mode = p.auth_mode;
16656a7c9badSJouni Malinen 
16666a7c9badSJouni Malinen 	for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
16676a7c9badSJouni Malinen 		switch (info->crypto.ciphers_pairwise[i]) {
16686a7c9badSJouni Malinen 		case WLAN_CIPHER_SUITE_WEP40:
16696a7c9badSJouni Malinen 		case WLAN_CIPHER_SUITE_WEP104:
16706a7c9badSJouni Malinen 			p.prwise_crypto_type |= WEP_CRYPT;
16716a7c9badSJouni Malinen 			break;
16726a7c9badSJouni Malinen 		case WLAN_CIPHER_SUITE_TKIP:
16736a7c9badSJouni Malinen 			p.prwise_crypto_type |= TKIP_CRYPT;
16746a7c9badSJouni Malinen 			break;
16756a7c9badSJouni Malinen 		case WLAN_CIPHER_SUITE_CCMP:
16766a7c9badSJouni Malinen 			p.prwise_crypto_type |= AES_CRYPT;
16776a7c9badSJouni Malinen 			break;
16786a7c9badSJouni Malinen 		}
16796a7c9badSJouni Malinen 	}
1680229ed6b5SEdward Lu 	if (p.prwise_crypto_type == 0) {
16816a7c9badSJouni Malinen 		p.prwise_crypto_type = NONE_CRYPT;
1682229ed6b5SEdward Lu 		ath6kl_set_cipher(ar, 0, true);
1683229ed6b5SEdward Lu 	} else if (info->crypto.n_ciphers_pairwise == 1)
1684229ed6b5SEdward Lu 		ath6kl_set_cipher(ar, info->crypto.ciphers_pairwise[0], true);
16856a7c9badSJouni Malinen 
16866a7c9badSJouni Malinen 	switch (info->crypto.cipher_group) {
16876a7c9badSJouni Malinen 	case WLAN_CIPHER_SUITE_WEP40:
16886a7c9badSJouni Malinen 	case WLAN_CIPHER_SUITE_WEP104:
16896a7c9badSJouni Malinen 		p.grp_crypto_type = WEP_CRYPT;
16906a7c9badSJouni Malinen 		break;
16916a7c9badSJouni Malinen 	case WLAN_CIPHER_SUITE_TKIP:
16926a7c9badSJouni Malinen 		p.grp_crypto_type = TKIP_CRYPT;
16936a7c9badSJouni Malinen 		break;
16946a7c9badSJouni Malinen 	case WLAN_CIPHER_SUITE_CCMP:
16956a7c9badSJouni Malinen 		p.grp_crypto_type = AES_CRYPT;
16966a7c9badSJouni Malinen 		break;
16976a7c9badSJouni Malinen 	default:
16986a7c9badSJouni Malinen 		p.grp_crypto_type = NONE_CRYPT;
16996a7c9badSJouni Malinen 		break;
17006a7c9badSJouni Malinen 	}
1701229ed6b5SEdward Lu 	ath6kl_set_cipher(ar, info->crypto.cipher_group, false);
17026a7c9badSJouni Malinen 
17036a7c9badSJouni Malinen 	p.nw_type = AP_NETWORK;
17046a7c9badSJouni Malinen 	ar->nw_type = ar->next_mode;
17056a7c9badSJouni Malinen 
17066a7c9badSJouni Malinen 	p.ssid_len = ar->ssid_len;
17076a7c9badSJouni Malinen 	memcpy(p.ssid, ar->ssid, ar->ssid_len);
17086a7c9badSJouni Malinen 	p.dot11_auth_mode = ar->dot11_auth_mode;
17096a7c9badSJouni Malinen 	p.ch = cpu_to_le16(ar->next_chan);
17106a7c9badSJouni Malinen 
17119a5b1318SJouni Malinen 	res = ath6kl_wmi_ap_profile_commit(ar->wmi, &p);
17129a5b1318SJouni Malinen 	if (res < 0)
17139a5b1318SJouni Malinen 		return res;
17149a5b1318SJouni Malinen 
17159a5b1318SJouni Malinen 	return 0;
17166a7c9badSJouni Malinen }
17176a7c9badSJouni Malinen 
17186a7c9badSJouni Malinen static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
17196a7c9badSJouni Malinen 			     struct beacon_parameters *info)
17206a7c9badSJouni Malinen {
17216a7c9badSJouni Malinen 	return ath6kl_ap_beacon(wiphy, dev, info, true);
17226a7c9badSJouni Malinen }
17236a7c9badSJouni Malinen 
17246a7c9badSJouni Malinen static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
17256a7c9badSJouni Malinen 			     struct beacon_parameters *info)
17266a7c9badSJouni Malinen {
17276a7c9badSJouni Malinen 	return ath6kl_ap_beacon(wiphy, dev, info, false);
17286a7c9badSJouni Malinen }
17296a7c9badSJouni Malinen 
17306a7c9badSJouni Malinen static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
17316a7c9badSJouni Malinen {
17326a7c9badSJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
17336a7c9badSJouni Malinen 
17346a7c9badSJouni Malinen 	if (ar->nw_type != AP_NETWORK)
17356a7c9badSJouni Malinen 		return -EOPNOTSUPP;
17366a7c9badSJouni Malinen 	if (!test_bit(CONNECTED, &ar->flag))
17376a7c9badSJouni Malinen 		return -ENOTCONN;
17386a7c9badSJouni Malinen 
17396a7c9badSJouni Malinen 	ath6kl_wmi_disconnect_cmd(ar->wmi);
17406a7c9badSJouni Malinen 	clear_bit(CONNECTED, &ar->flag);
17416a7c9badSJouni Malinen 
17426a7c9badSJouni Malinen 	return 0;
17436a7c9badSJouni Malinen }
17446a7c9badSJouni Malinen 
174523875136SJouni Malinen static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
174623875136SJouni Malinen 				 u8 *mac, struct station_parameters *params)
174723875136SJouni Malinen {
174823875136SJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
174923875136SJouni Malinen 
175023875136SJouni Malinen 	if (ar->nw_type != AP_NETWORK)
175123875136SJouni Malinen 		return -EOPNOTSUPP;
175223875136SJouni Malinen 
175323875136SJouni Malinen 	/* Use this only for authorizing/unauthorizing a station */
175423875136SJouni Malinen 	if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
175523875136SJouni Malinen 		return -EOPNOTSUPP;
175623875136SJouni Malinen 
175723875136SJouni Malinen 	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
175823875136SJouni Malinen 		return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_AUTHORIZE,
175923875136SJouni Malinen 					      mac, 0);
176023875136SJouni Malinen 	return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_UNAUTHORIZE, mac,
176123875136SJouni Malinen 				      0);
176223875136SJouni Malinen }
176323875136SJouni Malinen 
176463fa1e0cSJouni Malinen static int ath6kl_remain_on_channel(struct wiphy *wiphy,
176563fa1e0cSJouni Malinen 				    struct net_device *dev,
176663fa1e0cSJouni Malinen 				    struct ieee80211_channel *chan,
176763fa1e0cSJouni Malinen 				    enum nl80211_channel_type channel_type,
176863fa1e0cSJouni Malinen 				    unsigned int duration,
176963fa1e0cSJouni Malinen 				    u64 *cookie)
177063fa1e0cSJouni Malinen {
177163fa1e0cSJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
177263fa1e0cSJouni Malinen 
177363fa1e0cSJouni Malinen 	/* TODO: if already pending or ongoing remain-on-channel,
177463fa1e0cSJouni Malinen 	 * return -EBUSY */
177563fa1e0cSJouni Malinen 	*cookie = 1; /* only a single pending request is supported */
177663fa1e0cSJouni Malinen 
177763fa1e0cSJouni Malinen 	return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, chan->center_freq,
177863fa1e0cSJouni Malinen 					     duration);
177963fa1e0cSJouni Malinen }
178063fa1e0cSJouni Malinen 
178163fa1e0cSJouni Malinen static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
178263fa1e0cSJouni Malinen 					   struct net_device *dev,
178363fa1e0cSJouni Malinen 					   u64 cookie)
178463fa1e0cSJouni Malinen {
178563fa1e0cSJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
178663fa1e0cSJouni Malinen 
178763fa1e0cSJouni Malinen 	if (cookie != 1)
178863fa1e0cSJouni Malinen 		return -ENOENT;
178963fa1e0cSJouni Malinen 
179063fa1e0cSJouni Malinen 	return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi);
179163fa1e0cSJouni Malinen }
179263fa1e0cSJouni Malinen 
17938bdfbf40SJouni Malinen static int ath6kl_send_go_probe_resp(struct ath6kl *ar, const u8 *buf,
17948bdfbf40SJouni Malinen 				     size_t len, unsigned int freq)
17958bdfbf40SJouni Malinen {
17968bdfbf40SJouni Malinen 	const u8 *pos;
17978bdfbf40SJouni Malinen 	u8 *p2p;
17988bdfbf40SJouni Malinen 	int p2p_len;
17998bdfbf40SJouni Malinen 	int ret;
18008bdfbf40SJouni Malinen 	const struct ieee80211_mgmt *mgmt;
18018bdfbf40SJouni Malinen 
18028bdfbf40SJouni Malinen 	mgmt = (const struct ieee80211_mgmt *) buf;
18038bdfbf40SJouni Malinen 
18048bdfbf40SJouni Malinen 	/* Include P2P IE(s) from the frame generated in user space. */
18058bdfbf40SJouni Malinen 
18068bdfbf40SJouni Malinen 	p2p = kmalloc(len, GFP_KERNEL);
18078bdfbf40SJouni Malinen 	if (p2p == NULL)
18088bdfbf40SJouni Malinen 		return -ENOMEM;
18098bdfbf40SJouni Malinen 	p2p_len = 0;
18108bdfbf40SJouni Malinen 
18118bdfbf40SJouni Malinen 	pos = mgmt->u.probe_resp.variable;
18128bdfbf40SJouni Malinen 	while (pos + 1 < buf + len) {
18138bdfbf40SJouni Malinen 		if (pos + 2 + pos[1] > buf + len)
18148bdfbf40SJouni Malinen 			break;
18158bdfbf40SJouni Malinen 		if (ath6kl_is_p2p_ie(pos)) {
18168bdfbf40SJouni Malinen 			memcpy(p2p + p2p_len, pos, 2 + pos[1]);
18178bdfbf40SJouni Malinen 			p2p_len += 2 + pos[1];
18188bdfbf40SJouni Malinen 		}
18198bdfbf40SJouni Malinen 		pos += 2 + pos[1];
18208bdfbf40SJouni Malinen 	}
18218bdfbf40SJouni Malinen 
18228bdfbf40SJouni Malinen 	ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, freq, mgmt->da,
18238bdfbf40SJouni Malinen 						 p2p, p2p_len);
18248bdfbf40SJouni Malinen 	kfree(p2p);
18258bdfbf40SJouni Malinen 	return ret;
18268bdfbf40SJouni Malinen }
18278bdfbf40SJouni Malinen 
18288a6c8060SJouni Malinen static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
18298a6c8060SJouni Malinen 			  struct ieee80211_channel *chan, bool offchan,
18308a6c8060SJouni Malinen 			  enum nl80211_channel_type channel_type,
18318a6c8060SJouni Malinen 			  bool channel_type_valid, unsigned int wait,
18328a6c8060SJouni Malinen 			  const u8 *buf, size_t len, u64 *cookie)
18338a6c8060SJouni Malinen {
18348a6c8060SJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
18358a6c8060SJouni Malinen 	u32 id;
18368bdfbf40SJouni Malinen 	const struct ieee80211_mgmt *mgmt;
18378bdfbf40SJouni Malinen 
18388bdfbf40SJouni Malinen 	mgmt = (const struct ieee80211_mgmt *) buf;
18398bdfbf40SJouni Malinen 	if (buf + len >= mgmt->u.probe_resp.variable &&
18408bdfbf40SJouni Malinen 	    ar->nw_type == AP_NETWORK && test_bit(CONNECTED, &ar->flag) &&
18418bdfbf40SJouni Malinen 	    ieee80211_is_probe_resp(mgmt->frame_control)) {
18428bdfbf40SJouni Malinen 		/*
18438bdfbf40SJouni Malinen 		 * Send Probe Response frame in AP mode using a separate WMI
18448bdfbf40SJouni Malinen 		 * command to allow the target to fill in the generic IEs.
18458bdfbf40SJouni Malinen 		 */
18468bdfbf40SJouni Malinen 		*cookie = 0; /* TX status not supported */
18478bdfbf40SJouni Malinen 		return ath6kl_send_go_probe_resp(ar, buf, len,
18488bdfbf40SJouni Malinen 						 chan->center_freq);
18498bdfbf40SJouni Malinen 	}
18508a6c8060SJouni Malinen 
18518a6c8060SJouni Malinen 	id = ar->send_action_id++;
18528a6c8060SJouni Malinen 	if (id == 0) {
18538a6c8060SJouni Malinen 		/*
18548a6c8060SJouni Malinen 		 * 0 is a reserved value in the WMI command and shall not be
18558a6c8060SJouni Malinen 		 * used for the command.
18568a6c8060SJouni Malinen 		 */
18578a6c8060SJouni Malinen 		id = ar->send_action_id++;
18588a6c8060SJouni Malinen 	}
18598a6c8060SJouni Malinen 
18608a6c8060SJouni Malinen 	*cookie = id;
18618a6c8060SJouni Malinen 	return ath6kl_wmi_send_action_cmd(ar->wmi, id, chan->center_freq, wait,
18628a6c8060SJouni Malinen 					  buf, len);
18638a6c8060SJouni Malinen }
18648a6c8060SJouni Malinen 
1865ae32c30aSJouni Malinen static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
1866ae32c30aSJouni Malinen 				       struct net_device *dev,
1867ae32c30aSJouni Malinen 				       u16 frame_type, bool reg)
1868ae32c30aSJouni Malinen {
1869ae32c30aSJouni Malinen 	struct ath6kl *ar = ath6kl_priv(dev);
1870ae32c30aSJouni Malinen 
1871ae32c30aSJouni Malinen 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
1872ae32c30aSJouni Malinen 		   __func__, frame_type, reg);
1873ae32c30aSJouni Malinen 	if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
1874ae32c30aSJouni Malinen 		/*
1875ae32c30aSJouni Malinen 		 * Note: This notification callback is not allowed to sleep, so
1876ae32c30aSJouni Malinen 		 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
1877ae32c30aSJouni Malinen 		 * hardcode target to report Probe Request frames all the time.
1878ae32c30aSJouni Malinen 		 */
1879ae32c30aSJouni Malinen 		ar->probe_req_report = reg;
1880ae32c30aSJouni Malinen 	}
1881ae32c30aSJouni Malinen }
1882ae32c30aSJouni Malinen 
1883f80574aeSJouni Malinen static const struct ieee80211_txrx_stypes
1884f80574aeSJouni Malinen ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
1885f80574aeSJouni Malinen 	[NL80211_IFTYPE_STATION] = {
1886f80574aeSJouni Malinen 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
1887f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
1888f80574aeSJouni Malinen 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
1889f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
1890f80574aeSJouni Malinen 	},
1891f80574aeSJouni Malinen 	[NL80211_IFTYPE_P2P_CLIENT] = {
1892f80574aeSJouni Malinen 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
1893f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
1894f80574aeSJouni Malinen 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
1895f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
1896f80574aeSJouni Malinen 	},
1897f80574aeSJouni Malinen 	[NL80211_IFTYPE_P2P_GO] = {
1898f80574aeSJouni Malinen 		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
1899f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
1900f80574aeSJouni Malinen 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
1901f80574aeSJouni Malinen 		BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
1902f80574aeSJouni Malinen 	},
1903f80574aeSJouni Malinen };
1904f80574aeSJouni Malinen 
1905bdcd8170SKalle Valo static struct cfg80211_ops ath6kl_cfg80211_ops = {
1906bdcd8170SKalle Valo 	.change_virtual_intf = ath6kl_cfg80211_change_iface,
1907bdcd8170SKalle Valo 	.scan = ath6kl_cfg80211_scan,
1908bdcd8170SKalle Valo 	.connect = ath6kl_cfg80211_connect,
1909bdcd8170SKalle Valo 	.disconnect = ath6kl_cfg80211_disconnect,
1910bdcd8170SKalle Valo 	.add_key = ath6kl_cfg80211_add_key,
1911bdcd8170SKalle Valo 	.get_key = ath6kl_cfg80211_get_key,
1912bdcd8170SKalle Valo 	.del_key = ath6kl_cfg80211_del_key,
1913bdcd8170SKalle Valo 	.set_default_key = ath6kl_cfg80211_set_default_key,
1914bdcd8170SKalle Valo 	.set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
1915bdcd8170SKalle Valo 	.set_tx_power = ath6kl_cfg80211_set_txpower,
1916bdcd8170SKalle Valo 	.get_tx_power = ath6kl_cfg80211_get_txpower,
1917bdcd8170SKalle Valo 	.set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
1918bdcd8170SKalle Valo 	.join_ibss = ath6kl_cfg80211_join_ibss,
1919bdcd8170SKalle Valo 	.leave_ibss = ath6kl_cfg80211_leave_ibss,
1920bdcd8170SKalle Valo 	.get_station = ath6kl_get_station,
1921bdcd8170SKalle Valo 	.set_pmksa = ath6kl_set_pmksa,
1922bdcd8170SKalle Valo 	.del_pmksa = ath6kl_del_pmksa,
1923bdcd8170SKalle Valo 	.flush_pmksa = ath6kl_flush_pmksa,
1924003353b0SKalle Valo 	CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
1925abcb344bSKalle Valo #ifdef CONFIG_PM
1926abcb344bSKalle Valo 	.suspend = ar6k_cfg80211_suspend,
1927abcb344bSKalle Valo #endif
19286a7c9badSJouni Malinen 	.set_channel = ath6kl_set_channel,
19296a7c9badSJouni Malinen 	.add_beacon = ath6kl_add_beacon,
19306a7c9badSJouni Malinen 	.set_beacon = ath6kl_set_beacon,
19316a7c9badSJouni Malinen 	.del_beacon = ath6kl_del_beacon,
193223875136SJouni Malinen 	.change_station = ath6kl_change_station,
193363fa1e0cSJouni Malinen 	.remain_on_channel = ath6kl_remain_on_channel,
193463fa1e0cSJouni Malinen 	.cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
19358a6c8060SJouni Malinen 	.mgmt_tx = ath6kl_mgmt_tx,
1936ae32c30aSJouni Malinen 	.mgmt_frame_register = ath6kl_mgmt_frame_register,
1937bdcd8170SKalle Valo };
1938bdcd8170SKalle Valo 
1939bdcd8170SKalle Valo struct wireless_dev *ath6kl_cfg80211_init(struct device *dev)
1940bdcd8170SKalle Valo {
1941bdcd8170SKalle Valo 	int ret = 0;
1942bdcd8170SKalle Valo 	struct wireless_dev *wdev;
19436bbc7c35SJouni Malinen 	struct ath6kl *ar;
1944bdcd8170SKalle Valo 
1945bdcd8170SKalle Valo 	wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
1946bdcd8170SKalle Valo 	if (!wdev) {
1947bdcd8170SKalle Valo 		ath6kl_err("couldn't allocate wireless device\n");
1948bdcd8170SKalle Valo 		return NULL;
1949bdcd8170SKalle Valo 	}
1950bdcd8170SKalle Valo 
1951bdcd8170SKalle Valo 	/* create a new wiphy for use with cfg80211 */
1952bdcd8170SKalle Valo 	wdev->wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
1953bdcd8170SKalle Valo 	if (!wdev->wiphy) {
1954bdcd8170SKalle Valo 		ath6kl_err("couldn't allocate wiphy device\n");
1955bdcd8170SKalle Valo 		kfree(wdev);
1956bdcd8170SKalle Valo 		return NULL;
1957bdcd8170SKalle Valo 	}
1958bdcd8170SKalle Valo 
19596bbc7c35SJouni Malinen 	ar = wiphy_priv(wdev->wiphy);
19606bbc7c35SJouni Malinen 	ar->p2p = !!ath6kl_p2p;
19616bbc7c35SJouni Malinen 
1962f80574aeSJouni Malinen 	wdev->wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
1963f80574aeSJouni Malinen 
196463fa1e0cSJouni Malinen 	wdev->wiphy->max_remain_on_channel_duration = 5000;
196563fa1e0cSJouni Malinen 
1966bdcd8170SKalle Valo 	/* set device pointer for wiphy */
1967bdcd8170SKalle Valo 	set_wiphy_dev(wdev->wiphy, dev);
1968bdcd8170SKalle Valo 
1969bdcd8170SKalle Valo 	wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
19706e4604c8SJouni Malinen 		BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
19716bbc7c35SJouni Malinen 	if (ar->p2p) {
19726bbc7c35SJouni Malinen 		wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
19736bbc7c35SJouni Malinen 			BIT(NL80211_IFTYPE_P2P_CLIENT);
19746bbc7c35SJouni Malinen 	}
1975bdcd8170SKalle Valo 	/* max num of ssids that can be probed during scanning */
1976bdcd8170SKalle Valo 	wdev->wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
1977b84da8c7SJouni Malinen 	wdev->wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
1978bdcd8170SKalle Valo 	wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
1979bdcd8170SKalle Valo 	wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
1980bdcd8170SKalle Valo 	wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
1981bdcd8170SKalle Valo 
1982bdcd8170SKalle Valo 	wdev->wiphy->cipher_suites = cipher_suites;
1983bdcd8170SKalle Valo 	wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
1984bdcd8170SKalle Valo 
1985bdcd8170SKalle Valo 	ret = wiphy_register(wdev->wiphy);
1986bdcd8170SKalle Valo 	if (ret < 0) {
1987bdcd8170SKalle Valo 		ath6kl_err("couldn't register wiphy device\n");
1988bdcd8170SKalle Valo 		wiphy_free(wdev->wiphy);
1989bdcd8170SKalle Valo 		kfree(wdev);
1990bdcd8170SKalle Valo 		return NULL;
1991bdcd8170SKalle Valo 	}
1992bdcd8170SKalle Valo 
1993bdcd8170SKalle Valo 	return wdev;
1994bdcd8170SKalle Valo }
1995bdcd8170SKalle Valo 
1996bdcd8170SKalle Valo void ath6kl_cfg80211_deinit(struct ath6kl *ar)
1997bdcd8170SKalle Valo {
1998bdcd8170SKalle Valo 	struct wireless_dev *wdev = ar->wdev;
1999bdcd8170SKalle Valo 
2000bdcd8170SKalle Valo 	if (ar->scan_req) {
2001bdcd8170SKalle Valo 		cfg80211_scan_done(ar->scan_req, true);
2002bdcd8170SKalle Valo 		ar->scan_req = NULL;
2003bdcd8170SKalle Valo 	}
2004bdcd8170SKalle Valo 
2005bdcd8170SKalle Valo 	if (!wdev)
2006bdcd8170SKalle Valo 		return;
2007bdcd8170SKalle Valo 
2008bdcd8170SKalle Valo 	wiphy_unregister(wdev->wiphy);
2009bdcd8170SKalle Valo 	wiphy_free(wdev->wiphy);
2010bdcd8170SKalle Valo 	kfree(wdev);
2011bdcd8170SKalle Valo }
2012