assoc.c 20.2 KB
Newer Older
1 2 3 4
/* Copyright (C) 2006, Red Hat, Inc. */

#include <linux/bitops.h>
#include <net/ieee80211.h>
5
#include <linux/etherdevice.h>
6 7 8 9 10 11 12 13 14 15 16

#include "assoc.h"
#include "join.h"
#include "decl.h"
#include "hostcmd.h"
#include "host.h"


static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

17 18
static void print_assoc_req(const char * extra, struct assoc_request * assoc_req)
{
19
	DECLARE_MAC_BUF(mac);
20 21 22 23 24 25 26
	lbs_deb_assoc(
	       "#### Association Request: %s\n"
	       "       flags:      0x%08lX\n"
	       "       SSID:       '%s'\n"
	       "       channel:    %d\n"
	       "       band:       %d\n"
	       "       mode:       %d\n"
27
	       "       BSSID:      %s\n"
28 29
	       "       Encryption:%s%s%s\n"
	       "       auth:       %d\n",
30
	       extra, assoc_req->flags,
31
	       escape_essid(assoc_req->ssid, assoc_req->ssid_len),
32
	       assoc_req->channel, assoc_req->band, assoc_req->mode,
33
	       print_mac(mac, assoc_req->bssid),
34 35 36 37
	       assoc_req->secinfo.WPAenabled ? " WPA" : "",
	       assoc_req->secinfo.WPA2enabled ? " WPA2" : "",
	       assoc_req->secinfo.wep_enabled ? " WEP" : "",
	       assoc_req->secinfo.auth_mode);
38 39 40
}


41
static int assoc_helper_essid(lbs_private *priv,
42 43
                              struct assoc_request * assoc_req)
{
44
	lbs_adapter *adapter = priv->adapter;
45
	int ret = 0;
46
	struct bss_descriptor * bss;
47
	int channel = -1;
48

49
	lbs_deb_enter(LBS_DEB_ASSOC);
50

51 52 53 54
	/* FIXME: take channel into account when picking SSIDs if a channel
	 * is set.
	 */

55 56 57
	if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
		channel = assoc_req->channel;

58 59
	lbs_deb_assoc("New SSID requested: '%s'\n",
	              escape_essid(assoc_req->ssid, assoc_req->ssid_len));
60
	if (assoc_req->mode == IW_MODE_INFRA) {
61
		lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
62
			assoc_req->ssid_len, 0);
63

64
		bss = lbs_find_ssid_in_list(adapter, assoc_req->ssid,
65
				assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel);
66 67
		if (bss != NULL) {
			lbs_deb_assoc("SSID found in scan list, associating\n");
68
			memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
69
			ret = lbs_associate(priv, assoc_req);
70
		} else {
71
			lbs_deb_assoc("SSID not found; cannot associate\n");
72
		}
73
	} else if (assoc_req->mode == IW_MODE_ADHOC) {
74 75 76
		/* Scan for the network, do not save previous results.  Stale
		 *   scan data will cause us to join a non-existant adhoc network
		 */
77
		lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
78
			assoc_req->ssid_len, 1);
79 80

		/* Search for the requested SSID in the scan table */
81
		bss = lbs_find_ssid_in_list(adapter, assoc_req->ssid,
82
				assoc_req->ssid_len, NULL, IW_MODE_ADHOC, channel);
83
		if (bss != NULL) {
84
			lbs_deb_assoc("SSID found, will join\n");
85
			memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
86
			lbs_join_adhoc_network(priv, assoc_req);
87 88
		} else {
			/* else send START command */
89
			lbs_deb_assoc("SSID not found, creating adhoc network\n");
90
			memcpy(&assoc_req->bss.ssid, &assoc_req->ssid,
91 92
				IW_ESSID_MAX_SIZE);
			assoc_req->bss.ssid_len = assoc_req->ssid_len;
93
			lbs_start_adhoc_network(priv, assoc_req);
94 95 96
		}
	}

97
	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
98 99 100 101
	return ret;
}


102
static int assoc_helper_bssid(lbs_private *priv,
103 104
                              struct assoc_request * assoc_req)
{
105
	lbs_adapter *adapter = priv->adapter;
106 107
	int ret = 0;
	struct bss_descriptor * bss;
108
	DECLARE_MAC_BUF(mac);
109

110 111
	lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %s",
		print_mac(mac, assoc_req->bssid));
112 113

	/* Search for index position in list for requested MAC */
114
	bss = lbs_find_bssid_in_list(adapter, assoc_req->bssid,
115
			    assoc_req->mode);
116
	if (bss == NULL) {
117 118
		lbs_deb_assoc("ASSOC: WAP: BSSID %s not found, "
			"cannot associate.\n", print_mac(mac, assoc_req->bssid));
119 120 121
		goto out;
	}

122
	memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
123
	if (assoc_req->mode == IW_MODE_INFRA) {
124 125
		ret = lbs_associate(priv, assoc_req);
		lbs_deb_assoc("ASSOC: lbs_associate(bssid) returned %d\n", ret);
126
	} else if (assoc_req->mode == IW_MODE_ADHOC) {
127
		lbs_join_adhoc_network(priv, assoc_req);
128 129 130
	}

out:
131
	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
132 133 134 135
	return ret;
}


136
static int assoc_helper_associate(lbs_private *priv,
137 138 139 140 141 142 143
                                  struct assoc_request * assoc_req)
{
	int ret = 0, done = 0;

	/* If we're given and 'any' BSSID, try associating based on SSID */

	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
144 145
		if (compare_ether_addr(bssid_any, assoc_req->bssid)
		    && compare_ether_addr(bssid_off, assoc_req->bssid)) {
146 147 148
			ret = assoc_helper_bssid(priv, assoc_req);
			done = 1;
			if (ret) {
149
				lbs_deb_assoc("ASSOC: bssid: ret = %d\n", ret);
150 151 152 153 154 155 156
			}
		}
	}

	if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
		ret = assoc_helper_essid(priv, assoc_req);
		if (ret) {
157
			lbs_deb_assoc("ASSOC: bssid: ret = %d\n", ret);
158 159 160 161 162 163 164
		}
	}

	return ret;
}


165
static int assoc_helper_mode(lbs_private *priv,
166 167
                             struct assoc_request * assoc_req)
{
168
	lbs_adapter *adapter = priv->adapter;
169 170
	int ret = 0;

171
	lbs_deb_enter(LBS_DEB_ASSOC);
172

173 174
	if (assoc_req->mode == adapter->mode)
		goto done;
175

176
	if (assoc_req->mode == IW_MODE_INFRA) {
177
		if (adapter->psstate != PS_STATE_FULL_POWER)
178 179
			lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
		adapter->psmode = LBS802_11POWERMODECAM;
180 181
	}

182
	adapter->mode = assoc_req->mode;
183
	ret = lbs_prepare_and_send_command(priv,
184 185
				    CMD_802_11_SNMP_MIB,
				    0, CMD_OPTION_WAITFORRSP,
186
				    OID_802_11_INFRASTRUCTURE_MODE,
187
		/* Shoot me now */  (void *) (size_t) assoc_req->mode);
188

189 190
done:
	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
191 192 193 194
	return ret;
}


195
static int update_channel(lbs_private * priv)
196 197
{
	/* the channel in f/w could be out of sync, get the current channel */
198
	return lbs_prepare_and_send_command(priv, CMD_802_11_RF_CHANNEL,
199 200
				    CMD_OPT_802_11_RF_CHANNEL_GET,
				    CMD_OPTION_WAITFORRSP, 0, NULL);
201 202
}

203
void lbs_sync_channel(struct work_struct *work)
204
{
205
	lbs_private *priv = container_of(work, lbs_private, sync_channel);
206 207 208 209 210

	if (update_channel(priv) != 0)
		lbs_pr_info("Channel synchronization failed.");
}

211
static int assoc_helper_channel(lbs_private *priv,
212 213
                                struct assoc_request * assoc_req)
{
214
	lbs_adapter *adapter = priv->adapter;
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
	int ret = 0;

	lbs_deb_enter(LBS_DEB_ASSOC);

	ret = update_channel(priv);
	if (ret < 0) {
		lbs_deb_assoc("ASSOC: channel: error getting channel.");
	}

	if (assoc_req->channel == adapter->curbssparams.channel)
		goto done;

	lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
	       adapter->curbssparams.channel, assoc_req->channel);

230
	ret = lbs_prepare_and_send_command(priv, CMD_802_11_RF_CHANNEL,
231 232
				CMD_OPT_802_11_RF_CHANNEL_SET,
				CMD_OPTION_WAITFORRSP, 0, &assoc_req->channel);
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
	if (ret < 0) {
		lbs_deb_assoc("ASSOC: channel: error setting channel.");
	}

	ret = update_channel(priv);
	if (ret < 0) {
		lbs_deb_assoc("ASSOC: channel: error getting channel.");
	}

	if (assoc_req->channel != adapter->curbssparams.channel) {
		lbs_deb_assoc("ASSOC: channel: failed to update channel to %d",
		              assoc_req->channel);
		goto done;
	}

	if (   assoc_req->secinfo.wep_enabled
	    &&   (assoc_req->wep_keys[0].len
	       || assoc_req->wep_keys[1].len
	       || assoc_req->wep_keys[2].len
	       || assoc_req->wep_keys[3].len)) {
		/* Make sure WEP keys are re-sent to firmware */
		set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
	}

	/* Must restart/rejoin adhoc networks after channel change */
	set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);

done:
	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
	return ret;
}


266
static int assoc_helper_wep_keys(lbs_private *priv,
267 268
                                 struct assoc_request * assoc_req)
{
269
	lbs_adapter *adapter = priv->adapter;
270 271 272
	int i;
	int ret = 0;

273
	lbs_deb_enter(LBS_DEB_ASSOC);
274 275 276 277 278 279

	/* Set or remove WEP keys */
	if (   assoc_req->wep_keys[0].len
	    || assoc_req->wep_keys[1].len
	    || assoc_req->wep_keys[2].len
	    || assoc_req->wep_keys[3].len) {
280
		ret = lbs_prepare_and_send_command(priv,
281 282 283
					    CMD_802_11_SET_WEP,
					    CMD_ACT_ADD,
					    CMD_OPTION_WAITFORRSP,
284 285
					    0, assoc_req);
	} else {
286
		ret = lbs_prepare_and_send_command(priv,
287 288 289
					    CMD_802_11_SET_WEP,
					    CMD_ACT_REMOVE,
					    CMD_OPTION_WAITFORRSP,
290 291 292 293 294 295 296
					    0, NULL);
	}

	if (ret)
		goto out;

	/* enable/disable the MAC's WEP packet filter */
297
	if (assoc_req->secinfo.wep_enabled)
298
		adapter->currentpacketfilter |= CMD_ACT_MAC_WEP_ENABLE;
299
	else
300
		adapter->currentpacketfilter &= ~CMD_ACT_MAC_WEP_ENABLE;
301
	ret = lbs_set_mac_packet_filter(priv);
302 303 304 305 306 307 308 309
	if (ret)
		goto out;

	mutex_lock(&adapter->lock);

	/* Copy WEP keys into adapter wep key fields */
	for (i = 0; i < 4; i++) {
		memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i],
310
			sizeof(struct enc_key));
311 312 313 314 315 316
	}
	adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx;

	mutex_unlock(&adapter->lock);

out:
317
	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
318 319 320
	return ret;
}

321
static int assoc_helper_secinfo(lbs_private *priv,
322 323
                                struct assoc_request * assoc_req)
{
324
	lbs_adapter *adapter = priv->adapter;
325
	int ret = 0;
326 327
	u32 do_wpa;
	u32 rsn = 0;
328

329
	lbs_deb_enter(LBS_DEB_ASSOC);
330 331

	memcpy(&adapter->secinfo, &assoc_req->secinfo,
332
		sizeof(struct lbs_802_11_security));
333

334
	ret = lbs_set_mac_packet_filter(priv);
335 336
	if (ret)
		goto out;
337

338 339 340 341 342 343
	/* If RSN is already enabled, don't try to enable it again, since
	 * ENABLE_RSN resets internal state machines and will clobber the
	 * 4-way WPA handshake.
	 */

	/* Get RSN enabled/disabled */
344
	ret = lbs_prepare_and_send_command(priv,
345 346 347
				    CMD_802_11_ENABLE_RSN,
				    CMD_ACT_GET,
				    CMD_OPTION_WAITFORRSP,
348 349 350 351 352 353 354 355 356 357 358 359 360
				    0, &rsn);
	if (ret) {
		lbs_deb_assoc("Failed to get RSN status: %d", ret);
		goto out;
	}

	/* Don't re-enable RSN if it's already enabled */
	do_wpa = (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled);
	if (do_wpa == rsn)
		goto out;

	/* Set RSN enabled/disabled */
	rsn = do_wpa;
361
	ret = lbs_prepare_and_send_command(priv,
362 363 364
				    CMD_802_11_ENABLE_RSN,
				    CMD_ACT_SET,
				    CMD_OPTION_WAITFORRSP,
365
				    0, &rsn);
366 367

out:
368
	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
369 370 371 372
	return ret;
}


373
static int assoc_helper_wpa_keys(lbs_private *priv,
374 375 376
                                 struct assoc_request * assoc_req)
{
	int ret = 0;
377
	unsigned int flags = assoc_req->flags;
378

379
	lbs_deb_enter(LBS_DEB_ASSOC);
380

381 382 383 384
	/* Work around older firmware bug where WPA unicast and multicast
	 * keys must be set independently.  Seen in SDIO parts with firmware
	 * version 5.0.11p0.
	 */
385

386 387
	if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
		clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
388
		ret = lbs_prepare_and_send_command(priv,
389 390 391 392 393 394 395 396 397 398 399 400 401
					CMD_802_11_KEY_MATERIAL,
					CMD_ACT_SET,
					CMD_OPTION_WAITFORRSP,
					0, assoc_req);
		assoc_req->flags = flags;
	}

	if (ret)
		goto out;

	if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
		clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);

402
		ret = lbs_prepare_and_send_command(priv,
403 404 405 406 407 408 409 410
					CMD_802_11_KEY_MATERIAL,
					CMD_ACT_SET,
					CMD_OPTION_WAITFORRSP,
					0, assoc_req);
		assoc_req->flags = flags;
	}

out:
411
	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
412 413 414 415
	return ret;
}


416
static int assoc_helper_wpa_ie(lbs_private *priv,
417 418
                               struct assoc_request * assoc_req)
{
419
	lbs_adapter *adapter = priv->adapter;
420 421
	int ret = 0;

422
	lbs_deb_enter(LBS_DEB_ASSOC);
423 424 425 426 427 428 429 430 431

	if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
		memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
		adapter->wpa_ie_len = assoc_req->wpa_ie_len;
	} else {
		memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN);
		adapter->wpa_ie_len = 0;
	}

432
	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
433 434 435 436
	return ret;
}


437
static int should_deauth_infrastructure(lbs_adapter *adapter,
438 439
                                        struct assoc_request * assoc_req)
{
440
	if (adapter->connect_status != LBS_CONNECTED)
441 442 443
		return 0;

	if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
444
		lbs_deb_assoc("Deauthenticating due to new SSID in "
445 446 447 448 449
			" configuration request.\n");
		return 1;
	}

	if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
450
		if (adapter->secinfo.auth_mode != assoc_req->secinfo.auth_mode) {
451
			lbs_deb_assoc("Deauthenticating due to updated security "
452 453 454 455 456 457
				"info in configuration request.\n");
			return 1;
		}
	}

	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
458
		lbs_deb_assoc("Deauthenticating due to new BSSID in "
459 460 461 462
			" configuration request.\n");
		return 1;
	}

463 464 465 466 467
	if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
		lbs_deb_assoc("Deauthenticating due to channel switch.\n");
		return 1;
	}

468 469
	/* FIXME: deal with 'auto' mode somehow */
	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
470
		if (assoc_req->mode != IW_MODE_INFRA)
471 472 473 474 475 476 477
			return 1;
	}

	return 0;
}


478
static int should_stop_adhoc(lbs_adapter *adapter,
479 480
                             struct assoc_request * assoc_req)
{
481
	if (adapter->connect_status != LBS_CONNECTED)
482 483
		return 0;

484
	if (lbs_ssid_cmp(adapter->curbssparams.ssid,
485 486
	                      adapter->curbssparams.ssid_len,
	                      assoc_req->ssid, assoc_req->ssid_len) != 0)
487 488 489 490
		return 1;

	/* FIXME: deal with 'auto' mode somehow */
	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
491
		if (assoc_req->mode != IW_MODE_ADHOC)
492 493 494
			return 1;
	}

495 496 497 498 499
	if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
		if (assoc_req->channel != adapter->curbssparams.channel)
			return 1;
	}

500 501 502 503
	return 0;
}


504
void lbs_association_worker(struct work_struct *work)
505
{
506 507
	lbs_private *priv = container_of(work, lbs_private, assoc_work.work);
	lbs_adapter *adapter = priv->adapter;
508 509 510
	struct assoc_request * assoc_req = NULL;
	int ret = 0;
	int find_any_ssid = 0;
511
	DECLARE_MAC_BUF(mac);
512

513
	lbs_deb_enter(LBS_DEB_ASSOC);
514 515

	mutex_lock(&adapter->lock);
516 517 518
	assoc_req = adapter->pending_assoc_req;
	adapter->pending_assoc_req = NULL;
	adapter->in_progress_assoc_req = assoc_req;
519 520
	mutex_unlock(&adapter->lock);

521 522
	if (!assoc_req)
		goto done;
523

524
	print_assoc_req(__func__, assoc_req);
525 526 527

	/* If 'any' SSID was specified, find an SSID to associate with */
	if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
528
	    && !assoc_req->ssid_len)
529 530 531 532
		find_any_ssid = 1;

	/* But don't use 'any' SSID if there's a valid locked BSSID to use */
	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
533 534
		if (compare_ether_addr(assoc_req->bssid, bssid_any)
		    && compare_ether_addr(assoc_req->bssid, bssid_off))
535 536 537 538
			find_any_ssid = 0;
	}

	if (find_any_ssid) {
539
		u8 new_mode;
540

541
		ret = lbs_find_best_network_ssid(priv, assoc_req->ssid,
542
				&assoc_req->ssid_len, assoc_req->mode, &new_mode);
543
		if (ret) {
544
			lbs_deb_assoc("Could not find best network\n");
545 546 547 548 549
			ret = -ENETUNREACH;
			goto out;
		}

		/* Ensure we switch to the mode of the AP */
550
		if (assoc_req->mode == IW_MODE_AUTO) {
551 552 553 554 555 556 557 558 559
			set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
			assoc_req->mode = new_mode;
		}
	}

	/*
	 * Check if the attributes being changing require deauthentication
	 * from the currently associated infrastructure access point.
	 */
560
	if (adapter->mode == IW_MODE_INFRA) {
561
		if (should_deauth_infrastructure(adapter, assoc_req)) {
562
			ret = lbs_send_deauthentication(priv);
563
			if (ret) {
564
				lbs_deb_assoc("Deauthentication due to new "
565 566 567 568
					"configuration request failed: %d\n",
					ret);
			}
		}
569
	} else if (adapter->mode == IW_MODE_ADHOC) {
570
		if (should_stop_adhoc(adapter, assoc_req)) {
571
			ret = lbs_stop_adhoc_network(priv);
572
			if (ret) {
573
				lbs_deb_assoc("Teardown of AdHoc network due to "
574 575 576 577 578 579 580 581 582 583 584
					"new configuration request failed: %d\n",
					ret);
			}

		}
	}

	/* Send the various configuration bits to the firmware */
	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
		ret = assoc_helper_mode(priv, assoc_req);
		if (ret) {
585 586
			lbs_deb_assoc("ASSOC(:%d) mode: ret = %d\n",
			              __LINE__, ret);
587 588 589 590
			goto out;
		}
	}

591 592 593 594 595 596 597 598 599
	if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
		ret = assoc_helper_channel(priv, assoc_req);
		if (ret) {
			lbs_deb_assoc("ASSOC(:%d) channel: ret = %d\n",
			              __LINE__, ret);
			goto out;
		}
	}

600 601 602 603
	if (   test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)
	    || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) {
		ret = assoc_helper_wep_keys(priv, assoc_req);
		if (ret) {
604 605
			lbs_deb_assoc("ASSOC(:%d) wep_keys: ret = %d\n",
			              __LINE__, ret);
606 607 608 609 610 611 612
			goto out;
		}
	}

	if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
		ret = assoc_helper_secinfo(priv, assoc_req);
		if (ret) {
613 614
			lbs_deb_assoc("ASSOC(:%d) secinfo: ret = %d\n",
			              __LINE__, ret);
615 616 617 618 619 620 621
			goto out;
		}
	}

	if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
		ret = assoc_helper_wpa_ie(priv, assoc_req);
		if (ret) {
622 623
			lbs_deb_assoc("ASSOC(:%d) wpa_ie: ret = %d\n",
			              __LINE__, ret);
624 625 626 627 628 629 630 631
			goto out;
		}
	}

	if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)
	    || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
		ret = assoc_helper_wpa_keys(priv, assoc_req);
		if (ret) {
632 633
			lbs_deb_assoc("ASSOC(:%d) wpa_keys: ret = %d\n",
			              __LINE__, ret);
634 635 636 637 638 639 640 641 642 643 644 645 646
			goto out;
		}
	}

	/* SSID/BSSID should be the _last_ config option set, because they
	 * trigger the association attempt.
	 */
	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)
	    || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
		int success = 1;

		ret = assoc_helper_associate(priv, assoc_req);
		if (ret) {
647
			lbs_deb_assoc("ASSOC: association attempt unsuccessful: %d\n",
648 649 650 651
				ret);
			success = 0;
		}

652
		if (adapter->connect_status != LBS_CONNECTED) {
653
			lbs_deb_assoc("ASSOC: association attempt unsuccessful, "
654 655 656 657 658
				"not connected.\n");
			success = 0;
		}

		if (success) {
659
			lbs_deb_assoc("ASSOC: association attempt successful. "
660
				"Associated to '%s' (%s)\n",
661 662
				escape_essid(adapter->curbssparams.ssid,
				             adapter->curbssparams.ssid_len),
663
				print_mac(mac, adapter->curbssparams.bssid));
664
			lbs_prepare_and_send_command(priv,
665 666
				CMD_802_11_RSSI,
				0, CMD_OPTION_WAITFORRSP, 0, NULL);
667

668
			lbs_prepare_and_send_command(priv,
669 670
				CMD_802_11_GET_LOG,
				0, CMD_OPTION_WAITFORRSP, 0, NULL);
671 672 673 674 675 676 677
		} else {
			ret = -1;
		}
	}

out:
	if (ret) {
678
		lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n",
679 680
			ret);
	}
681 682 683 684

	mutex_lock(&adapter->lock);
	adapter->in_progress_assoc_req = NULL;
	mutex_unlock(&adapter->lock);
685
	kfree(assoc_req);
686 687 688

done:
	lbs_deb_leave(LBS_DEB_ASSOC);
689 690 691 692 693 694
}


/*
 * Caller MUST hold any necessary locks
 */
695
struct assoc_request *lbs_get_association_request(lbs_adapter *adapter)
696 697 698
{
	struct assoc_request * assoc_req;

699 700 701 702
	if (!adapter->pending_assoc_req) {
		adapter->pending_assoc_req = kzalloc(sizeof(struct assoc_request),
		                                     GFP_KERNEL);
		if (!adapter->pending_assoc_req) {
703 704 705 706 707 708 709 710 711
			lbs_pr_info("Not enough memory to allocate association"
				" request!\n");
			return NULL;
		}
	}

	/* Copy current configuration attributes to the association request,
	 * but don't overwrite any that are already set.
	 */
712
	assoc_req = adapter->pending_assoc_req;
713
	if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
714
		memcpy(&assoc_req->ssid, &adapter->curbssparams.ssid,
715 716
		       IW_ESSID_MAX_SIZE);
		assoc_req->ssid_len = adapter->curbssparams.ssid_len;
717 718 719 720 721
	}

	if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
		assoc_req->channel = adapter->curbssparams.channel;

722 723 724
	if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags))
		assoc_req->band = adapter->curbssparams.band;

725
	if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
726
		assoc_req->mode = adapter->mode;
727 728 729 730 731 732 733 734 735 736

	if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
		memcpy(&assoc_req->bssid, adapter->curbssparams.bssid,
			ETH_ALEN);
	}

	if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) {
		int i;
		for (i = 0; i < 4; i++) {
			memcpy(&assoc_req->wep_keys[i], &adapter->wep_keys[i],
737
				sizeof(struct enc_key));
738 739 740 741 742 743 744 745
		}
	}

	if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags))
		assoc_req->wep_tx_keyidx = adapter->wep_tx_keyidx;

	if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
		memcpy(&assoc_req->wpa_mcast_key, &adapter->wpa_mcast_key,
746
			sizeof(struct enc_key));
747 748 749 750
	}

	if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
		memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key,
751
			sizeof(struct enc_key));
752 753 754 755
	}

	if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
		memcpy(&assoc_req->secinfo, &adapter->secinfo,
756
			sizeof(struct lbs_802_11_security));
757 758 759 760 761 762 763 764
	}

	if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
		memcpy(&assoc_req->wpa_ie, &adapter->wpa_ie,
			MAX_WPA_IE_LEN);
		assoc_req->wpa_ie_len = adapter->wpa_ie_len;
	}

765 766
	print_assoc_req(__func__, assoc_req);

767 768
	return assoc_req;
}