cfg80211.c 76.3 KB
Newer Older
Kalle Valo's avatar
Kalle Valo committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
 * Copyright (c) 2004-2011 Atheros Communications Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

17
#include <linux/moduleparam.h>
18
#include <linux/inetdevice.h>
Kalle Valo's avatar
Kalle Valo committed
19
#include <linux/export.h>
20

Kalle Valo's avatar
Kalle Valo committed
21
22
23
#include "core.h"
#include "cfg80211.h"
#include "debug.h"
Kalle Valo's avatar
Kalle Valo committed
24
#include "hif-ops.h"
Kalle Valo's avatar
Kalle Valo committed
25
#include "testmode.h"
Kalle Valo's avatar
Kalle Valo committed
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

#define RATETAB_ENT(_rate, _rateid, _flags) {   \
	.bitrate    = (_rate),                  \
	.flags      = (_flags),                 \
	.hw_value   = (_rateid),                \
}

#define CHAN2G(_channel, _freq, _flags) {   \
	.band           = IEEE80211_BAND_2GHZ,  \
	.hw_value       = (_channel),           \
	.center_freq    = (_freq),              \
	.flags          = (_flags),             \
	.max_antenna_gain   = 0,                \
	.max_power      = 30,                   \
}

#define CHAN5G(_channel, _flags) {		    \
	.band           = IEEE80211_BAND_5GHZ,      \
	.hw_value       = (_channel),               \
	.center_freq    = 5000 + (5 * (_channel)),  \
	.flags          = (_flags),                 \
	.max_antenna_gain   = 0,                    \
	.max_power      = 30,                       \
}

static struct ieee80211_rate ath6kl_rates[] = {
	RATETAB_ENT(10, 0x1, 0),
	RATETAB_ENT(20, 0x2, 0),
	RATETAB_ENT(55, 0x4, 0),
	RATETAB_ENT(110, 0x8, 0),
	RATETAB_ENT(60, 0x10, 0),
	RATETAB_ENT(90, 0x20, 0),
	RATETAB_ENT(120, 0x40, 0),
	RATETAB_ENT(180, 0x80, 0),
	RATETAB_ENT(240, 0x100, 0),
	RATETAB_ENT(360, 0x200, 0),
	RATETAB_ENT(480, 0x400, 0),
	RATETAB_ENT(540, 0x800, 0),
};

#define ath6kl_a_rates     (ath6kl_rates + 4)
#define ath6kl_a_rates_size    8
#define ath6kl_g_rates     (ath6kl_rates + 0)
#define ath6kl_g_rates_size    12

static struct ieee80211_channel ath6kl_2ghz_channels[] = {
	CHAN2G(1, 2412, 0),
	CHAN2G(2, 2417, 0),
	CHAN2G(3, 2422, 0),
	CHAN2G(4, 2427, 0),
	CHAN2G(5, 2432, 0),
	CHAN2G(6, 2437, 0),
	CHAN2G(7, 2442, 0),
	CHAN2G(8, 2447, 0),
	CHAN2G(9, 2452, 0),
	CHAN2G(10, 2457, 0),
	CHAN2G(11, 2462, 0),
	CHAN2G(12, 2467, 0),
	CHAN2G(13, 2472, 0),
	CHAN2G(14, 2484, 0),
};

static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
	CHAN5G(34, 0), CHAN5G(36, 0),
	CHAN5G(38, 0), CHAN5G(40, 0),
	CHAN5G(42, 0), CHAN5G(44, 0),
	CHAN5G(46, 0), CHAN5G(48, 0),
	CHAN5G(52, 0), CHAN5G(56, 0),
	CHAN5G(60, 0), CHAN5G(64, 0),
	CHAN5G(100, 0), CHAN5G(104, 0),
	CHAN5G(108, 0), CHAN5G(112, 0),
	CHAN5G(116, 0), CHAN5G(120, 0),
	CHAN5G(124, 0), CHAN5G(128, 0),
	CHAN5G(132, 0), CHAN5G(136, 0),
	CHAN5G(140, 0), CHAN5G(149, 0),
	CHAN5G(153, 0), CHAN5G(157, 0),
	CHAN5G(161, 0), CHAN5G(165, 0),
	CHAN5G(184, 0), CHAN5G(188, 0),
	CHAN5G(192, 0), CHAN5G(196, 0),
	CHAN5G(200, 0), CHAN5G(204, 0),
	CHAN5G(208, 0), CHAN5G(212, 0),
	CHAN5G(216, 0),
};

static struct ieee80211_supported_band ath6kl_band_2ghz = {
	.n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
	.channels = ath6kl_2ghz_channels,
	.n_bitrates = ath6kl_g_rates_size,
	.bitrates = ath6kl_g_rates,
};

static struct ieee80211_supported_band ath6kl_band_5ghz = {
	.n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
	.channels = ath6kl_5ghz_a_channels,
	.n_bitrates = ath6kl_a_rates_size,
	.bitrates = ath6kl_a_rates,
};

124
125
#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */

Kalle Valo's avatar
Kalle Valo committed
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* returns true if scheduled scan was stopped */
static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
{
	struct ath6kl *ar = vif->ar;

	if (ar->state != ATH6KL_STATE_SCHED_SCAN)
		return false;

	del_timer_sync(&vif->sched_scan_timer);

	ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
					   ATH6KL_HOST_MODE_AWAKE);

	ar->state = ATH6KL_STATE_ON;

	return true;
}

static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
{
	struct ath6kl *ar = vif->ar;
	bool stopped;

	stopped = __ath6kl_cfg80211_sscan_stop(vif);

	if (!stopped)
		return;

	cfg80211_sched_scan_stopped(ar->wiphy);
}

157
static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valo's avatar
Kalle Valo committed
158
159
160
161
162
				  enum nl80211_wpa_versions wpa_version)
{
	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);

	if (!wpa_version) {
163
		vif->auth_mode = NONE_AUTH;
Kalle Valo's avatar
Kalle Valo committed
164
	} else if (wpa_version & NL80211_WPA_VERSION_2) {
165
		vif->auth_mode = WPA2_AUTH;
Kalle Valo's avatar
Kalle Valo committed
166
	} else if (wpa_version & NL80211_WPA_VERSION_1) {
167
		vif->auth_mode = WPA_AUTH;
Kalle Valo's avatar
Kalle Valo committed
168
169
170
171
172
173
174
175
	} else {
		ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
		return -ENOTSUPP;
	}

	return 0;
}

176
static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valo's avatar
Kalle Valo committed
177
178
179
180
181
182
				enum nl80211_auth_type auth_type)
{
	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);

	switch (auth_type) {
	case NL80211_AUTHTYPE_OPEN_SYSTEM:
183
		vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valo's avatar
Kalle Valo committed
184
185
		break;
	case NL80211_AUTHTYPE_SHARED_KEY:
186
		vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valo's avatar
Kalle Valo committed
187
188
		break;
	case NL80211_AUTHTYPE_NETWORK_EAP:
189
		vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valo's avatar
Kalle Valo committed
190
191
192
		break;

	case NL80211_AUTHTYPE_AUTOMATIC:
193
		vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valo's avatar
Kalle Valo committed
194
195
196
		break;

	default:
Masanari Iida's avatar
Masanari Iida committed
197
		ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
Kalle Valo's avatar
Kalle Valo committed
198
199
200
201
202
203
		return -ENOTSUPP;
	}

	return 0;
}

204
static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valo's avatar
Kalle Valo committed
205
{
206
207
208
	u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
	u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
		&vif->grp_crypto_len;
Kalle Valo's avatar
Kalle Valo committed
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234

	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
		   __func__, cipher, ucast);

	switch (cipher) {
	case 0:
		/* our own hack to use value 0 as no crypto used */
		*ar_cipher = NONE_CRYPT;
		*ar_cipher_len = 0;
		break;
	case WLAN_CIPHER_SUITE_WEP40:
		*ar_cipher = WEP_CRYPT;
		*ar_cipher_len = 5;
		break;
	case WLAN_CIPHER_SUITE_WEP104:
		*ar_cipher = WEP_CRYPT;
		*ar_cipher_len = 13;
		break;
	case WLAN_CIPHER_SUITE_TKIP:
		*ar_cipher = TKIP_CRYPT;
		*ar_cipher_len = 0;
		break;
	case WLAN_CIPHER_SUITE_CCMP:
		*ar_cipher = AES_CRYPT;
		*ar_cipher_len = 0;
		break;
235
236
237
238
	case WLAN_CIPHER_SUITE_SMS4:
		*ar_cipher = WAPI_CRYPT;
		*ar_cipher_len = 0;
		break;
Kalle Valo's avatar
Kalle Valo committed
239
240
241
242
243
244
245
246
	default:
		ath6kl_err("cipher 0x%x not supported\n", cipher);
		return -ENOTSUPP;
	}

	return 0;
}

247
static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valo's avatar
Kalle Valo committed
248
249
250
251
{
	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);

	if (key_mgmt == WLAN_AKM_SUITE_PSK) {
252
253
254
255
		if (vif->auth_mode == WPA_AUTH)
			vif->auth_mode = WPA_PSK_AUTH;
		else if (vif->auth_mode == WPA2_AUTH)
			vif->auth_mode = WPA2_PSK_AUTH;
256
	} else if (key_mgmt == 0x00409600) {
257
258
259
260
		if (vif->auth_mode == WPA_AUTH)
			vif->auth_mode = WPA_AUTH_CCKM;
		else if (vif->auth_mode == WPA2_AUTH)
			vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valo's avatar
Kalle Valo committed
261
	} else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
262
		vif->auth_mode = NONE_AUTH;
Kalle Valo's avatar
Kalle Valo committed
263
264
265
	}
}

266
static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valo's avatar
Kalle Valo committed
267
{
268
	struct ath6kl *ar = vif->ar;
269

Kalle Valo's avatar
Kalle Valo committed
270
271
272
273
274
	if (!test_bit(WMI_READY, &ar->flag)) {
		ath6kl_err("wmi is not ready\n");
		return false;
	}

275
	if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valo's avatar
Kalle Valo committed
276
277
278
279
280
281
282
		ath6kl_err("wlan disabled\n");
		return false;
	}

	return true;
}

283
284
285
286
287
288
289
290
291
292
293
294
static bool ath6kl_is_wpa_ie(const u8 *pos)
{
	return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
		pos[2] == 0x00 && pos[3] == 0x50 &&
		pos[4] == 0xf2 && pos[5] == 0x01;
}

static bool ath6kl_is_rsn_ie(const u8 *pos)
{
	return pos[0] == WLAN_EID_RSN;
}

295
296
297
298
299
300
301
302
static bool ath6kl_is_wps_ie(const u8 *pos)
{
	return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
		pos[1] >= 4 &&
		pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
		pos[5] == 0x04);
}

303
304
static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
				    size_t ies_len)
305
{
306
	struct ath6kl *ar = vif->ar;
307
308
309
310
311
	const u8 *pos;
	u8 *buf = NULL;
	size_t len = 0;
	int ret;

312
313
314
315
316
317
	/*
	 * Clear previously set flag
	 */

	ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
	/*
	 * Filter out RSN/WPA IE(s)
	 */

	if (ies && ies_len) {
		buf = kmalloc(ies_len, GFP_KERNEL);
		if (buf == NULL)
			return -ENOMEM;
		pos = ies;

		while (pos + 1 < ies + ies_len) {
			if (pos + 2 + pos[1] > ies + ies_len)
				break;
			if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
				memcpy(buf + len, pos, 2 + pos[1]);
				len += 2 + pos[1];
			}
335
336
337
338

			if (ath6kl_is_wps_ie(pos))
				ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;

339
340
341
342
			pos += 2 + pos[1];
		}
	}

343
344
	ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
				       WMI_FRAME_ASSOC_REQ, buf, len);
345
346
347
348
	kfree(buf);
	return ret;
}

349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
{
	switch (type) {
	case NL80211_IFTYPE_STATION:
		*nw_type = INFRA_NETWORK;
		break;
	case NL80211_IFTYPE_ADHOC:
		*nw_type = ADHOC_NETWORK;
		break;
	case NL80211_IFTYPE_AP:
		*nw_type = AP_NETWORK;
		break;
	case NL80211_IFTYPE_P2P_CLIENT:
		*nw_type = INFRA_NETWORK;
		break;
	case NL80211_IFTYPE_P2P_GO:
		*nw_type = AP_NETWORK;
		break;
	default:
		ath6kl_err("invalid interface type %u\n", type);
		return -ENOTSUPP;
	}

	return 0;
}

static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
				   u8 *if_idx, u8 *nw_type)
{
	int i;

	if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
		return false;

	if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
	    ar->num_vif))
		return false;

	if (type == NL80211_IFTYPE_STATION ||
	    type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
389
		for (i = 0; i < ar->vif_max; i++) {
390
391
392
393
394
395
396
			if ((ar->avail_idx_map >> i) & BIT(0)) {
				*if_idx = i;
				return true;
			}
		}
	}

397
398
	if (type == NL80211_IFTYPE_P2P_CLIENT ||
	    type == NL80211_IFTYPE_P2P_GO) {
399
		for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
400
401
402
403
404
405
406
			if ((ar->avail_idx_map >> i) & BIT(0)) {
				*if_idx = i;
				return true;
			}
		}
	}

407
408
409
	return false;
}

Kalle Valo's avatar
Kalle Valo committed
410
411
412
413
static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
				   struct cfg80211_connect_params *sme)
{
	struct ath6kl *ar = ath6kl_priv(dev);
414
	struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valo's avatar
Kalle Valo committed
415
	int status;
416
	u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Kalle Valo's avatar
Kalle Valo committed
417

Kalle Valo's avatar
Kalle Valo committed
418
419
	ath6kl_cfg80211_sscan_disable(vif);

420
	vif->sme_state = SME_CONNECTING;
Kalle Valo's avatar
Kalle Valo committed
421

422
	if (!ath6kl_cfg80211_ready(vif))
Kalle Valo's avatar
Kalle Valo committed
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
		return -EIO;

	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
		ath6kl_err("destroy in progress\n");
		return -EBUSY;
	}

	if (test_bit(SKIP_SCAN, &ar->flag) &&
	    ((sme->channel && sme->channel->center_freq == 0) ||
	     (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
		ath6kl_err("SkipScan: channel or bssid invalid\n");
		return -EINVAL;
	}

	if (down_interruptible(&ar->sem)) {
		ath6kl_err("busy, couldn't get access\n");
		return -ERESTARTSYS;
	}

	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
		ath6kl_err("busy, destroy in progress\n");
		up(&ar->sem);
		return -EBUSY;
	}

	if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
		/*
		 * sleep until the command queue drains
		 */
		wait_event_interruptible_timeout(ar->event_wq,
			ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
			WMI_TIMEOUT);
		if (signal_pending(current)) {
			ath6kl_err("cmd queue drain timeout\n");
			up(&ar->sem);
			return -EINTR;
		}
	}

462
463
464
465
466
467
468
	status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
	if (status) {
		up(&ar->sem);
		return status;
	}

	if (sme->ie == NULL || sme->ie_len == 0)
469
		ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
470

471
	if (test_bit(CONNECTED, &vif->flags) &&
472
473
	    vif->ssid_len == sme->ssid_len &&
	    !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
474
		vif->reconnect_flag = true;
475
476
		status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
						  vif->req_bssid,
477
						  vif->ch_hint);
Kalle Valo's avatar
Kalle Valo committed
478
479
480
481
482
483
484

		up(&ar->sem);
		if (status) {
			ath6kl_err("wmi_reconnect_cmd failed\n");
			return -EIO;
		}
		return 0;
485
486
	} else if (vif->ssid_len == sme->ssid_len &&
		   !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
487
		ath6kl_disconnect(vif);
Kalle Valo's avatar
Kalle Valo committed
488
489
	}

490
491
492
	memset(vif->ssid, 0, sizeof(vif->ssid));
	vif->ssid_len = sme->ssid_len;
	memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valo's avatar
Kalle Valo committed
493
494

	if (sme->channel)
495
		vif->ch_hint = sme->channel->center_freq;
Kalle Valo's avatar
Kalle Valo committed
496

497
	memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valo's avatar
Kalle Valo committed
498
	if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
499
		memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valo's avatar
Kalle Valo committed
500

501
	ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valo's avatar
Kalle Valo committed
502

503
	status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valo's avatar
Kalle Valo committed
504
505
506
507
508
509
	if (status) {
		up(&ar->sem);
		return status;
	}

	if (sme->crypto.n_ciphers_pairwise)
510
		ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valo's avatar
Kalle Valo committed
511
	else
512
		ath6kl_set_cipher(vif, 0, true);
Kalle Valo's avatar
Kalle Valo committed
513

514
	ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valo's avatar
Kalle Valo committed
515
516

	if (sme->crypto.n_akm_suites)
517
		ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valo's avatar
Kalle Valo committed
518
519

	if ((sme->key_len) &&
520
521
	    (vif->auth_mode == NONE_AUTH) &&
	    (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valo's avatar
Kalle Valo committed
522
523
		struct ath6kl_key *key = NULL;

524
		if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valo's avatar
Kalle Valo committed
525
526
527
528
529
530
			ath6kl_err("key index %d out of bounds\n",
				   sme->key_idx);
			up(&ar->sem);
			return -ENOENT;
		}

531
		key = &vif->keys[sme->key_idx];
Kalle Valo's avatar
Kalle Valo committed
532
533
		key->key_len = sme->key_len;
		memcpy(key->key, sme->key, key->key_len);
534
535
		key->cipher = vif->prwise_crypto;
		vif->def_txkey_index = sme->key_idx;
Kalle Valo's avatar
Kalle Valo committed
536

537
		ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
538
				      vif->prwise_crypto,
Kalle Valo's avatar
Kalle Valo committed
539
540
				      GROUP_USAGE | TX_USAGE,
				      key->key_len,
541
				      NULL, 0,
Kalle Valo's avatar
Kalle Valo committed
542
543
544
545
546
				      key->key, KEY_OP_INIT_VAL, NULL,
				      NO_SYNC_WMIFLAG);
	}

	if (!ar->usr_bss_filter) {
547
		clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
548
549
		if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
		    ALL_BSS_FILTER, 0) != 0) {
Kalle Valo's avatar
Kalle Valo committed
550
551
552
553
554
555
			ath6kl_err("couldn't set bss filtering\n");
			up(&ar->sem);
			return -EIO;
		}
	}

556
	vif->nw_type = vif->next_mode;
Kalle Valo's avatar
Kalle Valo committed
557

558
559
560
	if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
		nw_subtype = SUBTYPE_P2PCLIENT;

Kalle Valo's avatar
Kalle Valo committed
561
562
563
564
565
	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
		   "%s: connect called with authmode %d dot11 auth %d"
		   " PW crypto %d PW crypto len %d GRP crypto %d"
		   " GRP crypto len %d channel hint %u\n",
		   __func__,
566
567
		   vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
		   vif->prwise_crypto_len, vif->grp_crypto,
568
		   vif->grp_crypto_len, vif->ch_hint);
Kalle Valo's avatar
Kalle Valo committed
569

570
	vif->reconnect_flag = 0;
571
	status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
572
573
574
575
576
					vif->dot11_auth_mode, vif->auth_mode,
					vif->prwise_crypto,
					vif->prwise_crypto_len,
					vif->grp_crypto, vif->grp_crypto_len,
					vif->ssid_len, vif->ssid,
577
					vif->req_bssid, vif->ch_hint,
578
					ar->connect_ctrl_flags, nw_subtype);
Kalle Valo's avatar
Kalle Valo committed
579
580
581
582

	up(&ar->sem);

	if (status == -EINVAL) {
583
584
		memset(vif->ssid, 0, sizeof(vif->ssid));
		vif->ssid_len = 0;
Kalle Valo's avatar
Kalle Valo committed
585
586
587
588
589
590
591
592
		ath6kl_err("invalid request\n");
		return -ENOENT;
	} else if (status) {
		ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
		return -EIO;
	}

	if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
593
594
	    ((vif->auth_mode == WPA_PSK_AUTH)
	     || (vif->auth_mode == WPA2_PSK_AUTH))) {
595
		mod_timer(&vif->disconnect_timer,
Kalle Valo's avatar
Kalle Valo committed
596
597
598
599
			  jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
	}

	ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
600
	set_bit(CONNECT_PEND, &vif->flags);
Kalle Valo's avatar
Kalle Valo committed
601
602
603
604

	return 0;
}

605
606
607
608
609
610
611
static struct cfg80211_bss *
ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
			 enum network_type nw_type,
			 const u8 *bssid,
			 struct ieee80211_channel *chan,
			 const u8 *beacon_ie,
			 size_t beacon_ie_len)
612
{
613
	struct ath6kl *ar = vif->ar;
614
	struct cfg80211_bss *bss;
615
	u16 cap_mask, cap_val;
616
617
	u8 *ie;

618
619
620
621
622
623
624
625
	if (nw_type & ADHOC_NETWORK) {
		cap_mask = WLAN_CAPABILITY_IBSS;
		cap_val = WLAN_CAPABILITY_IBSS;
	} else {
		cap_mask = WLAN_CAPABILITY_ESS;
		cap_val = WLAN_CAPABILITY_ESS;
	}

626
	bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
627
628
			       vif->ssid, vif->ssid_len,
			       cap_mask, cap_val);
629
630
631
632
633
634
635
636
637
	if (bss == NULL) {
		/*
		 * Since cfg80211 may not yet know about the BSS,
		 * generate a partial entry until the first BSS info
		 * event becomes available.
		 *
		 * Prepend SSID element since it is not included in the Beacon
		 * IEs from the target.
		 */
638
		ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
639
		if (ie == NULL)
640
			return NULL;
641
		ie[0] = WLAN_EID_SSID;
642
643
644
		ie[1] = vif->ssid_len;
		memcpy(ie + 2, vif->ssid, vif->ssid_len);
		memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
645
		bss = cfg80211_inform_bss(ar->wiphy, chan,
646
					  bssid, 0, cap_val, 100,
647
					  ie, 2 + vif->ssid_len + beacon_ie_len,
648
649
					  0, GFP_KERNEL);
		if (bss)
650
651
			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
				   "cfg80211\n", bssid);
652
653
		kfree(ie);
	} else
654
		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
655

656
	return bss;
657
658
}

659
void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valo's avatar
Kalle Valo committed
660
661
662
663
664
665
				   u8 *bssid, u16 listen_intvl,
				   u16 beacon_intvl,
				   enum network_type nw_type,
				   u8 beacon_ie_len, u8 assoc_req_len,
				   u8 assoc_resp_len, u8 *assoc_info)
{
666
	struct ieee80211_channel *chan;
667
	struct ath6kl *ar = vif->ar;
668
	struct cfg80211_bss *bss;
Kalle Valo's avatar
Kalle Valo committed
669
670
671
672
673
674
675
676
677
678
679
680
681
682

	/* capinfo + listen interval */
	u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);

	/* capinfo + status code +  associd */
	u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);

	u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
	u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
	    assoc_resp_ie_offset;

	assoc_req_len -= assoc_req_ie_offset;
	assoc_resp_len -= assoc_resp_ie_offset;

683
684
685
686
	/*
	 * Store Beacon interval here; DTIM period will be available only once
	 * a Beacon frame from the AP is seen.
	 */
687
	vif->assoc_bss_beacon_int = beacon_intvl;
688
	clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
689

Kalle Valo's avatar
Kalle Valo committed
690
	if (nw_type & ADHOC_NETWORK) {
691
		if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valo's avatar
Kalle Valo committed
692
693
694
695
696
697
698
			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
				   "%s: ath6k not in ibss mode\n", __func__);
			return;
		}
	}

	if (nw_type & INFRA_NETWORK) {
699
700
		if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
		    vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valo's avatar
Kalle Valo committed
701
702
703
704
705
706
			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
				   "%s: ath6k not in station mode\n", __func__);
			return;
		}
	}

707
	chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valo's avatar
Kalle Valo committed
708

709
710
711
	bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
				       assoc_info, beacon_ie_len);
	if (!bss) {
712
		ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valo's avatar
Kalle Valo committed
713
714
715
		return;
	}

716
717
718
719
	if (nw_type & ADHOC_NETWORK) {
		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
			   nw_type & ADHOC_CREATOR ? "creator" : "joiner");
		cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
720
		cfg80211_put_bss(bss);
Kalle Valo's avatar
Kalle Valo committed
721
722
723
		return;
	}

724
	if (vif->sme_state == SME_CONNECTING) {
Kalle Valo's avatar
Kalle Valo committed
725
		/* inform connect result to cfg80211 */
726
		vif->sme_state = SME_CONNECTED;
727
		cfg80211_connect_result(vif->ndev, bssid,
Kalle Valo's avatar
Kalle Valo committed
728
729
730
					assoc_req_ie, assoc_req_len,
					assoc_resp_ie, assoc_resp_len,
					WLAN_STATUS_SUCCESS, GFP_KERNEL);
731
		cfg80211_put_bss(bss);
732
	} else if (vif->sme_state == SME_CONNECTED) {
Kalle Valo's avatar
Kalle Valo committed
733
		/* inform roam event to cfg80211 */
734
735
		cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
				    assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valo's avatar
Kalle Valo committed
736
737
738
739
740
741
	}
}

static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
				      struct net_device *dev, u16 reason_code)
{
Kalle Valo's avatar
Kalle Valo committed
742
	struct ath6kl *ar = ath6kl_priv(dev);
743
	struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valo's avatar
Kalle Valo committed
744
745
746
747

	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
		   reason_code);

Kalle Valo's avatar
Kalle Valo committed
748
749
	ath6kl_cfg80211_sscan_disable(vif);

750
	if (!ath6kl_cfg80211_ready(vif))
Kalle Valo's avatar
Kalle Valo committed
751
752
753
754
755
756
757
758
759
760
761
762
		return -EIO;

	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
		ath6kl_err("busy, destroy in progress\n");
		return -EBUSY;
	}

	if (down_interruptible(&ar->sem)) {
		ath6kl_err("busy, couldn't get access\n");
		return -ERESTARTSYS;
	}

763
	vif->reconnect_flag = 0;
764
	ath6kl_disconnect(vif);
765
766
	memset(vif->ssid, 0, sizeof(vif->ssid));
	vif->ssid_len = 0;
Kalle Valo's avatar
Kalle Valo committed
767
768

	if (!test_bit(SKIP_SCAN, &ar->flag))
769
		memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valo's avatar
Kalle Valo committed
770
771
772

	up(&ar->sem);

773
	vif->sme_state = SME_DISCONNECTED;
774

Kalle Valo's avatar
Kalle Valo committed
775
776
777
	return 0;
}

778
void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valo's avatar
Kalle Valo committed
779
780
781
				      u8 *bssid, u8 assoc_resp_len,
				      u8 *assoc_info, u16 proto_reason)
{
782
	struct ath6kl *ar = vif->ar;
783

784
785
786
	if (vif->scan_req) {
		cfg80211_scan_done(vif->scan_req, true);
		vif->scan_req = NULL;
Kalle Valo's avatar
Kalle Valo committed
787
788
	}

789
	if (vif->nw_type & ADHOC_NETWORK) {
790
		if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valo's avatar
Kalle Valo committed
791
792
793
794
795
			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
				   "%s: ath6k not in ibss mode\n", __func__);
			return;
		}
		memset(bssid, 0, ETH_ALEN);
796
		cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valo's avatar
Kalle Valo committed
797
798
799
		return;
	}

800
	if (vif->nw_type & INFRA_NETWORK) {
801
802
		if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
		    vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valo's avatar
Kalle Valo committed
803
804
805
806
807
808
			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
				   "%s: ath6k not in station mode\n", __func__);
			return;
		}
	}

809
810
811
812
813
814
815
816
	/*
	 * Send a disconnect command to target when a disconnect event is
	 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
	 * request from host) to make the firmware stop trying to connect even
	 * after giving disconnect event. There will be one more disconnect
	 * event for this disconnect command with reason code DISCONNECT_CMD
	 * which will be notified to cfg80211.
	 */
Kalle Valo's avatar
Kalle Valo committed
817

818
	if (reason != DISCONNECT_CMD) {
819
		ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valo's avatar
Kalle Valo committed
820
821
822
		return;
	}

823
	clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valo's avatar
Kalle Valo committed
824

825
	if (vif->sme_state == SME_CONNECTING) {
826
		cfg80211_connect_result(vif->ndev,
827
828
829
830
				bssid, NULL, 0,
				NULL, 0,
				WLAN_STATUS_UNSPECIFIED_FAILURE,
				GFP_KERNEL);
831
	} else if (vif->sme_state == SME_CONNECTED) {
832
		cfg80211_disconnected(vif->ndev, reason,
833
				NULL, 0, GFP_KERNEL);
Kalle Valo's avatar
Kalle Valo committed
834
835
	}

836
	vif->sme_state = SME_DISCONNECTED;
Kalle Valo's avatar
Kalle Valo committed
837
838
839
840
841
}

static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
				struct cfg80211_scan_request *request)
{
Kalle Valo's avatar
Kalle Valo committed
842
	struct ath6kl *ar = ath6kl_priv(ndev);
843
	struct ath6kl_vif *vif = netdev_priv(ndev);
844
845
	s8 n_channels = 0;
	u16 *channels = NULL;
Kalle Valo's avatar
Kalle Valo committed
846
	int ret = 0;
847
	u32 force_fg_scan = 0;
Kalle Valo's avatar
Kalle Valo committed
848

849
	if (!ath6kl_cfg80211_ready(vif))
Kalle Valo's avatar
Kalle Valo committed
850
851
		return -EIO;

Kalle Valo's avatar
Kalle Valo committed
852
853
	ath6kl_cfg80211_sscan_disable(vif);

Kalle Valo's avatar
Kalle Valo committed
854
	if (!ar->usr_bss_filter) {
855
		clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
856
		ret = ath6kl_wmi_bssfilter_cmd(
857
			ar->wmi, vif->fw_vif_idx,
858
			(test_bit(CONNECTED, &vif->flags) ?
859
860
			 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
		if (ret) {
Kalle Valo's avatar
Kalle Valo committed
861
			ath6kl_err("couldn't set bss filtering\n");
862
			return ret;
Kalle Valo's avatar
Kalle Valo committed
863
864
865
866
867
868
869
870
871
872
		}
	}

	if (request->n_ssids && request->ssids[0].ssid_len) {
		u8 i;

		if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
			request->n_ssids = MAX_PROBED_SSID_INDEX - 1;

		for (i = 0; i < request->n_ssids; i++)
873
874
			ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
						  i + 1, SPECIFIC_SSID_FLAG,
Kalle Valo's avatar
Kalle Valo committed
875
876
877
878
						  request->ssids[i].ssid_len,
						  request->ssids[i].ssid);
	}

Kalle Valo's avatar
Kalle Valo committed
879
880
881
882
	/*
	 * FIXME: we should clear the IE in fw if it's not set so just
	 * remove the check altogether
	 */
883
	if (request->ie) {
884
885
		ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
					       WMI_FRAME_PROBE_REQ,
886
887
888
889
890
891
892
893
					       request->ie, request->ie_len);
		if (ret) {
			ath6kl_err("failed to set Probe Request appie for "
				   "scan");
			return ret;
		}
	}

894
895
896
897
898
899
900
	/*
	 * Scan only the requested channels if the request specifies a set of
	 * channels. If the list is longer than the target supports, do not
	 * configure the list and instead, scan all available channels.
	 */
	if (request->n_channels > 0 &&
	    request->n_channels <= WMI_MAX_CHANNELS) {
901
902
		u8 i;

903
		n_channels = request->n_channels;
904
905
906
907
908
909
910
911
912
913
914
915

		channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
		if (channels == NULL) {
			ath6kl_warn("failed to set scan channels, "
				    "scan all channels");
			n_channels = 0;
		}

		for (i = 0; i < n_channels; i++)
			channels[i] = request->channels[i]->center_freq;
	}

916
	if (test_bit(CONNECTED, &vif->flags))
917
918
		force_fg_scan = 1;

919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
	if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
		    ar->fw_capabilities)) {
		/*
		 * If capable of doing P2P mgmt operations using
		 * station interface, send additional information like
		 * supported rates to advertise and xmit rates for
		 * probe requests
		 */
		ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
						WMI_LONG_SCAN, force_fg_scan,
						false, 0, 0, n_channels,
						channels, request->no_cck,
						request->rates);
	} else {
		ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
						WMI_LONG_SCAN, force_fg_scan,
						false, 0, 0, n_channels,
						channels);
	}
938
	if (ret)
Kalle Valo's avatar
Kalle Valo committed
939
		ath6kl_err("wmi_startscan_cmd failed\n");
940
	else
941
		vif->scan_req = request;
Kalle Valo's avatar
Kalle Valo committed
942

943
944
	kfree(channels);

Kalle Valo's avatar
Kalle Valo committed
945
946
947
	return ret;
}

948
void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valo's avatar
Kalle Valo committed
949
{
950
	struct ath6kl *ar = vif->ar;
951
	int i;
Kalle Valo's avatar
Kalle Valo committed
952

953
954
	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
		   aborted ? " aborted" : "");
Kalle Valo's avatar
Kalle Valo committed
955

956
	if (!vif->scan_req)
957
958
		return;

959
	if (aborted)
960
961
		goto out;

962
963
	if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
		for (i = 0; i < vif->scan_req->n_ssids; i++) {
964
965
			ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
						  i + 1, DISABLE_SSID_FLAG,
966
						  0, NULL);
Kalle Valo's avatar
Kalle Valo committed
967
968
		}
	}
969
970

out:
971
	cfg80211_scan_done(vif->scan_req, aborted);
972
	vif->scan_req = NULL;
Kalle Valo's avatar
Kalle Valo committed
973
974
975
976
977
978
979
}

static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
				   u8 key_index, bool pairwise,
				   const u8 *mac_addr,
				   struct key_params *params)
{
Kalle Valo's avatar
Kalle Valo committed
980
	struct ath6kl *ar = ath6kl_priv(ndev);
981
	struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valo's avatar
Kalle Valo committed
982
	struct ath6kl_key *key = NULL;
983
	int seq_len;
Kalle Valo's avatar
Kalle Valo committed
984
985
986
	u8 key_usage;
	u8 key_type;

987
	if (!ath6kl_cfg80211_ready(vif))
Kalle Valo's avatar
Kalle Valo committed
988
989
		return -EIO;

990
991
992
	if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
		if (params->key_len != WMI_KRK_LEN)
			return -EINVAL;
993
994
		return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
					      params->key);
995
996
	}

997
	if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valo's avatar
Kalle Valo committed
998
999
1000
1001
1002
1003
		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
			   "%s: key index %d out of bounds\n", __func__,
			   key_index);
		return -ENOENT;
	}

1004
	key = &vif->keys[key_index];
Kalle Valo's avatar
Kalle Valo committed
1005
1006
1007
1008
1009
1010
1011
	memset(key, 0, sizeof(struct ath6kl_key));

	if (pairwise)
		key_usage = PAIRWISE_USAGE;
	else
		key_usage = GROUP_USAGE;

1012
1013
1014
1015
1016
	seq_len = params->seq_len;
	if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
	    seq_len > ATH6KL_KEY_SEQ_LEN) {
		/* Only first half of the WPI PN is configured */
		seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valo's avatar
Kalle Valo committed
1017
	}
1018
1019
1020
1021
1022
1023
1024
1025
1026
	if (params->key_len > WLAN_MAX_KEY_LEN ||
	    seq_len > sizeof(key->seq))
		return -EINVAL;

	key->key_len = params->key_len;
	memcpy(key->key, params->key, key->key_len);
	key->seq_len = seq_len;
	memcpy(key->seq, params->seq, key->seq_len);
	key->cipher = params->cipher;
Kalle Valo's avatar
Kalle Valo committed
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040

	switch (key->cipher) {
	case WLAN_CIPHER_SUITE_WEP40:
	case WLAN_CIPHER_SUITE_WEP104:
		key_type = WEP_CRYPT;
		break;

	case WLAN_CIPHER_SUITE_TKIP:
		key_type = TKIP_CRYPT;
		break;

	case WLAN_CIPHER_SUITE_CCMP:
		key_type = AES_CRYPT;
		break;
1041
1042
1043
	case WLAN_CIPHER_SUITE_SMS4:
		key_type = WAPI_CRYPT;
		break;
Kalle Valo's avatar
Kalle Valo committed
1044
1045
1046
1047
1048

	default:
		return -ENOTSUPP;
	}

1049
1050
	if (((vif->auth_mode == WPA_PSK_AUTH)
	     || (vif->auth_mode == WPA2_PSK_AUTH))
Kalle Valo's avatar
Kalle Valo committed
1051
	    && (key_usage & GROUP_USAGE))
1052
		del_timer(&vif->disconnect_timer);
Kalle Valo's avatar
Kalle Valo committed
1053
1054
1055
1056
1057
1058

	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
		   "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
		   __func__, key_index, key->key_len, key_type,
		   key_usage, key->seq_len);

1059
	if (vif->nw_type == AP_NETWORK && !pairwise &&
1060
1061
	    (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
	     key_type == WAPI_CRYPT) && params) {
1062
1063
1064
1065
1066
		ar->ap_mode_bkey.valid = true;
		ar->ap_mode_bkey.key_index = key_index;
		ar->ap_mode_bkey.key_type = key_type;
		ar->ap_mode_bkey.key_len = key->key_len;
		memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
1067
		if (!test_bit(CONNECTED, &vif->flags)) {
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
				   "key configuration until AP mode has been "
				   "started\n");
			/*
			 * The key will be set in ath6kl_connect_ap_mode() once
			 * the connected event is received from the target.
			 */
			return 0;
		}
	}

1079
	if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
1080
	    !test_bit(CONNECTED, &vif->flags)) {
1081
1082
1083
1084
1085
1086
1087
		/*
		 * Store the key locally so that it can be re-configured after
		 * the AP mode has properly started
		 * (ath6kl_install_statioc_wep_keys).
		 */
		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
			   "until AP mode has been started\n");
1088
1089
1090
		vif->wep_key_list[key_index].key_len = key->key_len;
		memcpy(vif->wep_key_list[key_index].key, key->key,
		       key->key_len);
1091
1092
1093
		return 0;
	}

1094
	return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
1095
1096
1097
1098
				     key_type, key_usage, key->key_len,
				     key->seq, key->seq_len, key->key,
				     KEY_OP_INIT_VAL,
				     (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valo's avatar
Kalle Valo committed
1099
1100
1101
1102
1103
1104
}

static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
				   u8 key_index, bool pairwise,
				   const u8 *mac_addr)
{
Kalle Valo's avatar
Kalle Valo committed
1105
	struct ath6kl *ar = ath6kl_priv(ndev);
1106
	struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valo's avatar
Kalle Valo committed
1107
1108
1109

	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);

1110
	if (!ath6kl_cfg80211_ready(vif))
Kalle Valo's avatar
Kalle Valo committed
1111
1112
		return -EIO;

1113
	if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valo's avatar
Kalle Valo committed
1114
1115
1116
1117
1118
1119
		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
			   "%s: key index %d out of bounds\n", __func__,
			   key_index);
		return -ENOENT;
	}

1120
	if (!vif->keys[key_index].key_len) {
Kalle Valo's avatar
Kalle Valo committed
1121
1122
1123
1124
1125
		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
			   "%s: index %d is empty\n", __func__, key_index);
		return 0;
	}

1126
	vif->keys[key_index].key_len = 0;
Kalle Valo's avatar
Kalle Valo committed
1127

1128
	return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valo's avatar
Kalle Valo committed
1129
1130
1131
1132
1133
1134
1135
1136
}

static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
				   u8 key_index, bool pairwise,
				   const u8 *mac_addr, void *cookie,
				   void (*callback) (void *cookie,
						     struct key_params *))
{
1137
	struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valo's avatar
Kalle Valo committed
1138
1139
1140
1141
1142
	struct ath6kl_key *key = NULL;
	struct key_params params;

	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);

1143
	if (!ath6kl_cfg80211_ready(vif))
Kalle Valo's avatar
Kalle Valo committed
1144
1145
		return -EIO;

1146
	if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valo's avatar
Kalle Valo committed
1147
1148
1149
1150
1151
1152
		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
			   "%s: key index %d out of bounds\n", __func__,
			   key_index);
		return -ENOENT;
	}

1153
	key = &vif->keys[key_index];
Kalle Valo's avatar
Kalle Valo committed
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
	memset(&params, 0, sizeof(params));
	params.cipher = key->cipher;
	params.key_len = key->key_len;
	params.seq_len = key->seq_len;
	params.seq = key->seq;
	params.key = key->key;

	callback(cookie, &params);

	return key->key_len ? 0 : -ENOENT;
}

static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
					   struct net_device *ndev,
					   u8 key_index, bool unicast,
					   bool multicast)
{
Kalle Valo's avatar
Kalle Valo committed
1171
	struct ath6kl *ar = ath6kl_priv(ndev);
1172
	struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valo's avatar
Kalle Valo committed
1173
1174
	struct ath6kl_key *key = NULL;
	u8 key_usage;
1175
	enum crypto_type key_type = NONE_CRYPT;
Kalle Valo's avatar
Kalle Valo committed
1176
1177
1178

	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);

1179
	if (!ath6kl_cfg80211_ready(vif))
Kalle Valo's avatar
Kalle Valo committed
1180
1181
		return -EIO;

1182
	if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valo's avatar
Kalle Valo committed
1183
1184
1185
1186
1187
1188
		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
			   "%s: key index %d out of bounds\n",
			   __func__, key_index);
		return -ENOENT;
	}

1189
	if (!vif->keys[key_index].key_len) {
Kalle Valo's avatar
Kalle Valo committed
1190
1191
1192
1193
1194
		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
			   __func__, key_index);
		return -EINVAL;
	}

1195
	vif->def_txkey_index = key_index;
1196
	key = &vif->keys[vif->def_txkey_index];
Kalle Valo's avatar
Kalle Valo committed
1197
	key_usage = GROUP_USAGE;
1198
	if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valo's avatar
Kalle Valo committed
1199
		key_usage |= TX_USAGE;