libertas: convert to deferred association

Dan Williams dcbw at localhost.unroutablelocaldomain
Sat Nov 11 20:18:34 EST 2006


Commit:     4daea8dc46cab7db7ec1d253068e1b361e886208
Parent:     e37aa4d8558921a916eafc4efe649bd7764a32b8
commit 4daea8dc46cab7db7ec1d253068e1b361e886208
Author:     Dan Williams <dcbw at localhost.localdomain>
AuthorDate: Sat Nov 11 20:01:21 2006 -0500
Commit:     Dan Williams <dcbw at localhost.localdomain>
CommitDate: Sat Nov 11 20:01:21 2006 -0500

    libertas: convert to deferred association
    
    Instead of doing one-shot associations and changes to card configuration all over
    the place, consolidate association and WE related configuration setting into
    one place, wlan_assoc.c.  Each WE call updates an association request structure,
    and when all WE commands are finished being sent to the driver (after a short
    timeout) the driver sets the whole configuration in one go.  This allows
    commands to be bundled together and avoids the previous behavior of eccentric
    WE command ordering necessary to associate.
---
 drivers/net/wireless/libertas/Makefile     |    2 
 drivers/net/wireless/libertas/wlan_assoc.c |  597 ++++++++++++++++++++++++++
 drivers/net/wireless/libertas/wlan_assoc.h |   43 ++
 drivers/net/wireless/libertas/wlan_cmd.c   |   23 +
 drivers/net/wireless/libertas/wlan_decl.h  |    4 
 drivers/net/wireless/libertas/wlan_dev.h   |   43 ++
 drivers/net/wireless/libertas/wlan_fw.c    |    2 
 drivers/net/wireless/libertas/wlan_join.c  |  275 ------------
 drivers/net/wireless/libertas/wlan_join.h  |    6 
 drivers/net/wireless/libertas/wlan_main.c  |    3 
 drivers/net/wireless/libertas/wlan_scan.c  |   44 +-
 drivers/net/wireless/libertas/wlan_scan.h  |    8 
 drivers/net/wireless/libertas/wlan_wext.c  |  631 ++++++++++++++++------------
 13 files changed, 1093 insertions(+), 588 deletions(-)

diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile
index 05d2a05..d8375cc 100644
--- a/drivers/net/wireless/libertas/Makefile
+++ b/drivers/net/wireless/libertas/Makefile
@@ -5,7 +5,7 @@ usb8xxx-objs := wlan_main.o wlan_fw.o wl
 		wlan_cmdresp.o wlan_scan.o	  \
 		wlan_join.o wlan_11d.o 		  \
 		wlan_ioctl.o wlan_debugfs.o	  \
-		wlan_ethtool.o
+		wlan_ethtool.o wlan_assoc.o
 
 EXTRA_CFLAGS += -DDEBUG_LEVEL2
 ifeq ($(CONFIG_LIBERTAS_USB_DEBUG), y)
diff --git a/drivers/net/wireless/libertas/wlan_assoc.c b/drivers/net/wireless/libertas/wlan_assoc.c
new file mode 100644
index 0000000..0d04dc1
--- /dev/null
+++ b/drivers/net/wireless/libertas/wlan_assoc.c
@@ -0,0 +1,597 @@
+/* Copyright (C) 2006, Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/bitops.h>
+#include <net/ieee80211.h>
+
+#include "wlan_assoc.h"
+#include "wlan_join.h"
+#include "wlan_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 };
+
+static int assoc_helper_essid(wlan_private *priv,
+                              struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	int i;
+
+	ENTER();
+
+	PRINTM(INFO, "New SSID requested: %s\n", assoc_req->ssid.Ssid);
+	if (assoc_req->mode == Wlan802_11Infrastructure) {
+		if (adapter->Prescan) {
+			libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 1);
+		}
+
+		i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid,
+				NULL, Wlan802_11Infrastructure);
+		if (i >= 0) {
+			PRINTM(INFO,
+			       "SSID found in scan list ... associating...\n");
+
+			ret = wlan_associate(priv, &adapter->ScanTable[i]);
+			if (ret == 0) {
+				memcpy(&assoc_req->bssid,
+				       &adapter->ScanTable[i].MacAddress,
+				       ETH_ALEN);
+			}
+		} else {
+			PRINTM(INFO, "SSID '%s' not found; cannot associate\n",
+				assoc_req->ssid.Ssid);
+		}
+	} else if (assoc_req->mode == Wlan802_11IBSS) {
+		/* Scan for the network, do not save previous results.  Stale
+		 *   scan data will cause us to join a non-existant adhoc network
+		 */
+		libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 0);
+
+		/* Search for the requested SSID in the scan table */
+		i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, NULL,
+				Wlan802_11IBSS);
+		if (i >= 0) {
+			PRINTM(INFO, "SSID found at %d in List, so join\n", ret);
+			libertas_join_adhoc_network(priv, &adapter->ScanTable[i]);
+		} else {
+			/* else send START command */
+			PRINTM(INFO, "SSID not found in list, so creating adhoc"
+				" with SSID '%s'\n", assoc_req->ssid.Ssid);
+			libertas_start_adhoc_network(priv, &assoc_req->ssid);
+		}
+		memcpy(&assoc_req->bssid, &adapter->CurrentAddr, ETH_ALEN);
+	}
+
+	LEAVE();
+	return ret;
+}
+
+
+static int assoc_helper_bssid(wlan_private *priv,
+                              struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int i, ret = 0;
+
+	ENTER();
+
+	PRINTM(INFO, "ASSOC: WAP: BSSID = " MAC_FMT "\n",
+		MAC_ARG(assoc_req->bssid));
+
+	/* Search for index position in list for requested MAC */
+	i = libertas_find_BSSID_in_list(adapter, assoc_req->bssid,
+			    assoc_req->mode);
+	if (i < 0) {
+		PRINTM(INFO, "ASSOC: WAP: BSSID " MAC_FMT " not found, "
+			"cannot associate.\n", MAC_ARG(assoc_req->bssid));
+		goto out;
+	}
+
+	if (assoc_req->mode == Wlan802_11Infrastructure) {
+		ret = wlan_associate(priv, &adapter->ScanTable[i]);
+		PRINTM(INFO, "ASSOC: return from wlan_associate(bssd) was %d\n", ret);
+	} else if (assoc_req->mode == Wlan802_11IBSS) {
+		libertas_join_adhoc_network(priv, &adapter->ScanTable[i]);
+	}
+	memcpy(&assoc_req->ssid, &adapter->ScanTable[i].Ssid,
+		sizeof(struct WLAN_802_11_SSID));
+
+out:
+	LEAVE();
+	return ret;
+}
+
+
+static int assoc_helper_associate(wlan_private *priv,
+                                  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)) {
+		if (memcmp(bssid_any, assoc_req->bssid, ETH_ALEN)
+		    && memcmp(bssid_off, assoc_req->bssid, ETH_ALEN)) {
+			ret = assoc_helper_bssid(priv, assoc_req);
+			done = 1;
+			if (ret) {
+				PRINTM(INFO, "ASSOC: bssid: ret = %d\n", ret);
+			}
+		}
+	}
+
+	if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		ret = assoc_helper_essid(priv, assoc_req);
+		if (ret) {
+			PRINTM(INFO, "ASSOC: bssid: ret = %d\n", ret);
+		}
+	}
+
+	return ret;
+}
+
+
+static int assoc_helper_mode(wlan_private *priv,
+                             struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	ENTER();
+
+	if (assoc_req->mode == adapter->InfrastructureMode) {
+		LEAVE();
+		return 0;
+	}
+
+	if (assoc_req->mode == Wlan802_11Infrastructure) {
+		if (adapter->PSState != PS_STATE_FULL_POWER)
+			libertas_ps_wakeup(priv, HostCmd_OPTION_WAITFORRSP);
+		adapter->PSMode = Wlan802_11PowerModeCAM;
+	}
+
+	adapter->InfrastructureMode = assoc_req->mode;
+	ret = libertas_prepare_and_send_command(priv,
+				    HostCmd_CMD_802_11_SNMP_MIB,
+				    0, HostCmd_OPTION_WAITFORRSP,
+				    OID_802_11_INFRASTRUCTURE_MODE,
+				    (void *) assoc_req->mode);
+
+	LEAVE();
+	return ret;
+}
+
+
+static int assoc_helper_wep_keys(wlan_private *priv,
+                                 struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int i;
+	int ret = 0;
+
+	ENTER();
+
+	/* 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) {
+		ret = libertas_prepare_and_send_command(priv,
+					    HostCmd_CMD_802_11_SET_WEP,
+					    HostCmd_ACT_ADD,
+					    HostCmd_OPTION_WAITFORRSP,
+					    0, assoc_req);
+	} else {
+		ret = libertas_prepare_and_send_command(priv,
+					    HostCmd_CMD_802_11_SET_WEP,
+					    HostCmd_ACT_REMOVE,
+					    HostCmd_OPTION_WAITFORRSP,
+					    0, NULL);
+	}
+
+	if (ret)
+		goto out;
+
+	/* Enable/disable the MAC's WEP packet filter */
+	if (assoc_req->secinfo.WEPStatus == Wlan802_11WEPEnabled)
+		adapter->CurrentPacketFilter |= HostCmd_ACT_MAC_WEP_ENABLE;
+	else
+		adapter->CurrentPacketFilter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
+	ret = libertas_set_mac_packet_filter(priv);
+	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],
+			sizeof(struct WLAN_802_11_KEY));
+	}
+	adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
+
+	mutex_unlock(&adapter->lock);
+
+out:
+	LEAVE();
+	return ret;
+}
+
+static int assoc_helper_secinfo(wlan_private *priv,
+                                struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	ENTER();
+
+	memcpy(&adapter->SecInfo, &assoc_req->secinfo,
+		sizeof(struct wlan_802_11_security));
+
+	ret = libertas_set_mac_packet_filter(priv);
+
+	LEAVE();
+	return ret;
+}
+
+
+static int assoc_helper_wpa_keys(wlan_private *priv,
+                                 struct assoc_request * assoc_req)
+{
+	int ret = 0;
+
+	ENTER();
+
+	/* Enable/Disable RSN */
+	ret = libertas_prepare_and_send_command(priv,
+				    HostCmd_CMD_802_11_ENABLE_RSN,
+				    HostCmd_ACT_SET,
+				    HostCmd_OPTION_WAITFORRSP,
+				    0, assoc_req);
+	if (ret)
+		goto out;
+
+	ret = libertas_prepare_and_send_command(priv,
+				    HostCmd_CMD_802_11_KEY_MATERIAL,
+				    HostCmd_ACT_SET,
+				    HostCmd_OPTION_WAITFORRSP,
+				    0, assoc_req);
+
+out:
+	LEAVE();
+	return ret;
+}
+
+
+static int assoc_helper_wpa_ie(wlan_private *priv,
+                               struct assoc_request * assoc_req)
+{
+	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+
+	ENTER();
+
+	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;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+
+static int should_deauth_infrastructure(wlan_adapter *adapter,
+                                        struct assoc_request * assoc_req)
+{
+	if (adapter->MediaConnectStatus != WlanMediaStateConnected)
+		return 0;
+
+	if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		PRINTM(INFO, "Deauthenticating due to new SSID in "
+			" configuration request.\n");
+		return 1;
+	}
+
+	if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+		if (adapter->SecInfo.AuthenticationMode != 
+		    assoc_req->secinfo.AuthenticationMode) {
+			PRINTM(INFO, "Deauthenticating due to updated security "
+				"info in configuration request.\n");
+			return 1;
+		}
+	}
+
+	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+		PRINTM(INFO, "Deauthenticating due to new BSSID in "
+			" configuration request.\n");
+		return 1;
+	}
+
+	/* FIXME: deal with 'auto' mode somehow */
+	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
+		if (assoc_req->mode != Wlan802_11Infrastructure)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int should_stop_adhoc(wlan_adapter *adapter,
+                             struct assoc_request * assoc_req)
+{
+	if (adapter->MediaConnectStatus != WlanMediaStateConnected)
+		return 0;
+
+	if (adapter->CurBssParams.ssid.SsidLength != assoc_req->ssid.SsidLength)
+		return 1;
+	if (memcmp(adapter->CurBssParams.ssid.Ssid, assoc_req->ssid.Ssid,
+			sizeof(struct WLAN_802_11_SSID)))
+		return 1;
+
+	/* FIXME: deal with 'auto' mode somehow */
+	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
+		if (assoc_req->mode != Wlan802_11IBSS)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+void wlan_association_worker(void *user_data)
+{
+	struct net_device *dev = (struct net_device *) user_data;
+	wlan_private *priv = dev->priv;
+	wlan_adapter *adapter = priv->adapter;
+	struct assoc_request * assoc_req = NULL;
+	int ret = 0;
+	int find_any_ssid = 0;
+
+	ENTER();
+
+	mutex_lock(&adapter->lock);
+	assoc_req = adapter->assoc_req;
+	adapter->assoc_req = NULL;
+	mutex_unlock(&adapter->lock);
+
+	if (!assoc_req) {
+		LEAVE();
+		return;
+	}
+
+	PRINTM(INFO, "ASSOC: starting new association request: flags = 0x%lX\n",
+		assoc_req->flags);
+
+	/* If 'any' SSID was specified, find an SSID to associate with */
+	if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
+	    && !assoc_req->ssid.SsidLength)
+		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)) {
+		if (memcmp(&assoc_req->bssid, bssid_any, ETH_ALEN)
+		    && memcmp(&assoc_req->bssid, bssid_off, ETH_ALEN))
+			find_any_ssid = 0;
+	}
+
+	if (find_any_ssid) {
+		enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode;
+
+		ret = libertas_find_best_network_SSID(priv, &assoc_req->ssid,
+				assoc_req->mode, &new_mode);
+		if (ret) {
+			PRINTM(INFO, "Could not find best network\n");
+			ret = -ENETUNREACH;
+			goto out;
+		}
+
+		/* Ensure we switch to the mode of the AP */
+		if (assoc_req->mode == Wlan802_11AutoUnknown) {
+			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.
+	 */
+	if (adapter->InfrastructureMode == Wlan802_11Infrastructure) {
+		if (should_deauth_infrastructure(adapter, assoc_req)) {
+			ret = libertas_send_deauthentication(priv);
+			if (ret) {
+				PRINTM(INFO, "Deauthentication due to new "
+					"configuration request failed: %d\n",
+					ret);
+			}
+		}
+	} else if (adapter->InfrastructureMode == Wlan802_11IBSS) {
+		if (should_stop_adhoc(adapter, assoc_req)) {
+			ret = libertas_stop_adhoc_network(priv);
+			if (ret) {
+				PRINTM(INFO, "Teardown of AdHoc network due to "
+					"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) {
+PRINTM(INFO, "ASSOC(:%d) mode: ret = %d\n", __LINE__, ret);
+			goto out;
+		}
+	}
+
+	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) {
+PRINTM(INFO, "ASSOC(:%d) wep_keys: ret = %d\n", __LINE__, ret);
+			goto out;
+		}
+	}
+
+	if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+		ret = assoc_helper_secinfo(priv, assoc_req);
+		if (ret) {
+PRINTM(INFO, "ASSOC(:%d) secinfo: ret = %d\n", __LINE__, ret);
+			goto out;
+		}
+	}
+
+	if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
+		ret = assoc_helper_wpa_ie(priv, assoc_req);
+		if (ret) {
+PRINTM(INFO, "ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__, ret);
+			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) {
+PRINTM(INFO, "ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__, ret);
+			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) {
+			PRINTM(INFO, "ASSOC: association attempt unsuccessful: %d\n",
+				ret);
+			success = 0;
+		}
+
+		if (adapter->MediaConnectStatus != WlanMediaStateConnected) {
+			PRINTM(INFO, "ASSOC: assoication attempt unsuccessful, "
+				"not connected.\n");
+			success = 0;
+		}
+
+		if (success) {
+			PRINTM(INFO, "ASSOC: association attempt successful. "
+				"Associated to '%s' (" MAC_FMT ")\n",
+				assoc_req->ssid.Ssid, MAC_ARG(assoc_req->bssid));
+		} else {
+			
+			ret = WLAN_STATUS_FAILURE;
+		}
+	}
+
+out:
+	if (ret) {
+		PRINTM(INFO, "ASSOC: reconfiguration attempt unsuccessful: %d\n",
+			ret);
+	}
+	kfree(assoc_req);
+	LEAVE();
+}
+
+
+/*
+ * Caller MUST hold any necessary locks
+ */
+struct assoc_request * wlan_get_association_request(wlan_adapter *adapter)
+{
+	struct assoc_request * assoc_req;
+
+	if (!adapter->assoc_req) {
+		adapter->assoc_req = kzalloc(sizeof(struct assoc_request), GFP_KERNEL);
+		if (!adapter->assoc_req) {
+			PRINTM(FATAL, "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.
+	 */
+	assoc_req = adapter->assoc_req;
+	if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		memcpy(&assoc_req->ssid, adapter->CurBssParams.ssid.Ssid,
+			adapter->CurBssParams.ssid.SsidLength);
+	}
+
+	if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
+		assoc_req->channel = adapter->CurBssParams.channel;
+
+	if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
+		assoc_req->mode = adapter->InfrastructureMode;
+
+	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],
+				sizeof(struct WLAN_802_11_KEY));
+		}
+	}
+
+	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,
+			sizeof(struct WLAN_802_11_KEY));
+	}
+
+	if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
+		memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key,
+			sizeof(struct WLAN_802_11_KEY));
+	}
+
+	if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+		memcpy(&assoc_req->secinfo, &adapter->SecInfo,
+			sizeof(struct wlan_802_11_security));
+	}
+
+	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;
+	}
+
+	return assoc_req;
+}
+
+
diff --git a/drivers/net/wireless/libertas/wlan_assoc.h b/drivers/net/wireless/libertas/wlan_assoc.h
new file mode 100644
index 0000000..31ba5f6
--- /dev/null
+++ b/drivers/net/wireless/libertas/wlan_assoc.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2006, Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _WLAN_ASSOC_H_
+#define _WLAN_ASSOC_H_
+
+#include "wlan_dev.h"
+
+void wlan_association_worker(void *user_data);
+
+struct assoc_request * wlan_get_association_request(wlan_adapter *adapter);
+
+#define ASSOC_DELAY (HZ / 2)
+static inline void wlan_postpone_association_work(wlan_private *priv)
+{
+	cancel_delayed_work(&priv->assoc_work);
+	schedule_delayed_work(&priv->assoc_work, ASSOC_DELAY);
+}
+
+static inline void wlan_cancel_association_work(wlan_private *priv)
+{
+	cancel_delayed_work(&priv->assoc_work);
+	if (priv->adapter->assoc_req) {
+		kfree(priv->adapter->assoc_req);
+		priv->adapter->assoc_req = NULL;
+	}
+}
+
+#endif /* _WLAN_ASSOC_H */
diff --git a/drivers/net/wireless/libertas/wlan_cmd.c b/drivers/net/wireless/libertas/wlan_cmd.c
index 2806c99..d338784 100644
--- a/drivers/net/wireless/libertas/wlan_cmd.c
+++ b/drivers/net/wireless/libertas/wlan_cmd.c
@@ -252,11 +252,14 @@ static int wlan_cmd_802_11_sleep_params(
  *  @return 	   	WLAN_STATUS_SUCCESS or WLAN_STATUS_FAILURE
  */
 static int wlan_cmd_802_11_set_wep(wlan_private * priv,
-				   struct HostCmd_DS_COMMAND *cmd, u32 cmd_act)
+                                   struct HostCmd_DS_COMMAND *cmd,
+                                   u32 cmd_act,
+                                   void * pdata_buf)
 {
 	struct HostCmd_DS_802_11_SET_WEP *wep = &cmd->params.wep;
 	wlan_adapter *Adapter = priv->adapter;
 	int ret = WLAN_STATUS_SUCCESS;
+	struct assoc_request * assoc_req = (struct assoc_request *) pdata_buf;
 
 	ENTER();
 
@@ -267,18 +270,24 @@ static int wlan_cmd_802_11_set_wep(wlan_
 	if (cmd_act == HostCmd_ACT_ADD) {
 		int i;
 
+		if (!assoc_req) {
+			PRINTM(INFO, "Invalid association request!");
+			ret = WLAN_STATUS_FAILURE;
+			goto done;
+		}
+
 		wep->Action = wlan_cpu_to_le16(HostCmd_ACT_ADD);
 
 		/* default tx key index */
 		wep->KeyIndex = wlan_cpu_to_le16((u16)
-				                 (Adapter->wep_tx_keyidx &
+				                 (assoc_req->wep_tx_keyidx &
 				                 (u32)HostCmd_WEP_KEY_INDEX_MASK));
 
 		PRINTM(INFO, "Tx Key Index: %u\n", wep->KeyIndex);
 
 		/* Copy key types and material to host command structure */
 		for (i = 0; i < 4; i++) {
-			struct WLAN_802_11_KEY * pKey = &Adapter->wep_keys[i];
+			struct WLAN_802_11_KEY * pKey = &assoc_req->wep_keys[i];
 
 			switch (pKey->len) {
 			case KEY_LEN_WEP_40:
@@ -519,10 +528,13 @@ static int wlan_cmd_802_11_snmp_mib(wlan
 
 	switch (cmd_oid) {
 	case OID_802_11_INFRASTRUCTURE_MODE:
+	{
+		enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode = 
+			(enum WLAN_802_11_NETWORK_INFRASTRUCTURE) pdata_buf;
 		pSNMPMIB->QueryType = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET);
 		pSNMPMIB->OID = wlan_cpu_to_le16((u16) DesiredBssType_i);
 		pSNMPMIB->BufSize = sizeof(u8);
-		if (Adapter->InfrastructureMode == Wlan802_11Infrastructure)
+		if (mode == Wlan802_11Infrastructure)
 			ucTemp = SNMP_MIB_VALUE_INFRA;
 		else
 			ucTemp = SNMP_MIB_VALUE_ADHOC;
@@ -530,6 +542,7 @@ static int wlan_cmd_802_11_snmp_mib(wlan
 		memmove(pSNMPMIB->Value, &ucTemp, sizeof(u8));
 
 		break;
+	}
 
 	case OID_802_11D_ENABLE:
 		{
@@ -1466,7 +1479,7 @@ int libertas_prepare_and_send_command(wl
 		break;
 
 	case HostCmd_CMD_802_11_SET_WEP:
-		ret = wlan_cmd_802_11_set_wep(priv, CmdPtr, cmd_action);
+		ret = wlan_cmd_802_11_set_wep(priv, CmdPtr, cmd_action, pdata_buf);
 		break;
 
 	case HostCmd_CMD_802_11_AD_HOC_START:
diff --git a/drivers/net/wireless/libertas/wlan_decl.h b/drivers/net/wireless/libertas/wlan_decl.h
index 49b5ee2..3b6be95 100644
--- a/drivers/net/wireless/libertas/wlan_decl.h
+++ b/drivers/net/wireless/libertas/wlan_decl.h
@@ -84,10 +84,6 @@ #ifdef REASSOCIATION
 void libertas_reassoc_timer_fn(unsigned long data);
 #endif				/* REASSOCIATION */
 
-struct iw_point;
-struct iw_request_info;
-int libertas_set_essid(struct net_device *dev, struct iw_request_info *info,
-		   struct iw_point *dwrq, char *extra);
 int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band);
 
 int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *);
diff --git a/drivers/net/wireless/libertas/wlan_dev.h b/drivers/net/wireless/libertas/wlan_dev.h
index 1fb6e22..ff9c291 100644
--- a/drivers/net/wireless/libertas/wlan_dev.h
+++ b/drivers/net/wireless/libertas/wlan_dev.h
@@ -178,12 +178,53 @@ struct _wlan_private {
 	/** thread to service interrupts */
 	struct wlan_thread MainThread;
 
+	struct work_struct assoc_work;
+
 #ifdef REASSOCIATION
 	/** thread to service mac events */
 	struct wlan_thread ReassocThread;
 #endif				/* REASSOCIATION */
 };
 
+/** Association request
+ *
+ * Encapsulates all the options that describe a specific assocation request
+ * or configuration of the wireless card's radio, mode, and security settings.
+ */
+struct assoc_request {
+#define ASSOC_FLAG_SSID			1
+#define ASSOC_FLAG_CHANNEL		2
+#define ASSOC_FLAG_MODE			3
+#define ASSOC_FLAG_BSSID		4
+#define ASSOC_FLAG_WEP_KEYS		5
+#define ASSOC_FLAG_WEP_TX_KEYIDX	6
+#define ASSOC_FLAG_WPA_MCAST_KEY	7
+#define ASSOC_FLAG_WPA_UCAST_KEY	8
+#define ASSOC_FLAG_SECINFO		9
+#define ASSOC_FLAG_WPA_IE		10
+	unsigned long flags;
+
+	struct WLAN_802_11_SSID ssid;
+	u8 channel;
+	enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode;
+	u8 bssid[ETH_ALEN];
+
+	/** WEP Keys */
+	struct WLAN_802_11_KEY wep_keys[4];
+	u16 wep_tx_keyidx;
+
+	/** WPA Keys */
+	struct WLAN_802_11_KEY wpa_mcast_key;
+	struct WLAN_802_11_KEY wpa_unicast_key;
+
+	struct wlan_802_11_security secinfo;
+
+	/** WPA Information Elements*/
+#define MAX_WPA_IE_LEN 64
+	u8 wpa_ie[MAX_WPA_IE_LEN];
+	u8 wpa_ie_len;
+};
+
 /** Wlan Adapter data structure*/
 struct _wlan_adapter {
 	/** STATUS variables */
@@ -316,6 +357,8 @@ #endif				/* REASSOCIATION */
 	u16 LocalListenInterval;
 	u16 NullPktInterval;
 
+	struct assoc_request * assoc_req;
+
 	/** Encryption parameter */
 	struct wlan_802_11_security SecInfo;
 
diff --git a/drivers/net/wireless/libertas/wlan_fw.c b/drivers/net/wireless/libertas/wlan_fw.c
index 3f29475..20d66ef 100644
--- a/drivers/net/wireless/libertas/wlan_fw.c
+++ b/drivers/net/wireless/libertas/wlan_fw.c
@@ -210,6 +210,8 @@ static void wlan_init_adapter(wlan_priva
 	Adapter->SecInfo.EncryptionMode = CIPHER_NONE;
 	Adapter->InfrastructureMode = Wlan802_11Infrastructure;
 
+	Adapter->assoc_req = NULL;
+
 	Adapter->NumInScanTable = 0;
 	Adapter->pAttemptedBSSDesc = NULL;
 #ifdef REASSOCIATION
diff --git a/drivers/net/wireless/libertas/wlan_join.c b/drivers/net/wireless/libertas/wlan_join.c
index 0cceceb..e4a24f7 100644
--- a/drivers/net/wireless/libertas/wlan_join.c
+++ b/drivers/net/wireless/libertas/wlan_join.c
@@ -47,8 +47,6 @@ #include "wlan_decl.h"
 #include "wlan_join.h"
 #include "wlan_dev.h"
 
-static int wlan_associate(wlan_private * priv, struct bss_descriptor * pBSSDesc);
-
 /**
  *  @brief This function finds out the common rates between rate1 and rate2.
  *
@@ -170,267 +168,6 @@ int libertas_do_adhocstop_ioctl(wlan_pri
 }
 
 /**
- *  @brief Set essid
- *
- *  @param dev          A pointer to net_device structure
- *  @param info         A pointer to iw_request_info structure
- *  @param dwrq         A pointer to iw_point structure
- *  @param extra        A pointer to extra data buf
- *  @return             WLAN_STATUS_SUCCESS--success, otherwise--fail
- */
-int libertas_set_essid(struct net_device *dev, struct iw_request_info *info,
-		   struct iw_point *dwrq, char *extra)
-{
-	wlan_private *priv = dev->priv;
-	wlan_adapter *Adapter = priv->adapter;
-	int ret = WLAN_STATUS_SUCCESS;
-	struct WLAN_802_11_SSID reqSSID;
-	int ssid_len = dwrq->length;
-	int i;
-
-	ENTER();
-
-#ifdef REASSOCIATION
-	// cancel re-association timer if there's one
-	del_timer(&Adapter->reassoc_timer);
-
-	if (down_interruptible(&Adapter->ReassocSem)) {
-		PRINTM(FATAL, "Acquire semaphore error, libertas_set_essid\n");
-		return -EBUSY;
-	}
-#endif /* REASSOCIATION */
-
-	/*
-	 * WE-20 and earlier NULL pad the end of the SSID and increment
-	 * SSID length so it can be used like a string.  WE-21 and later don't,
-	 * but some userspace tools aren't able to cope with the change.
-	 */
-	if ((ssid_len > 0) && (extra[ssid_len - 1] == '\0'))
-		ssid_len--;
-
-	/* Check the size of the string */
-	if (ssid_len > IW_ESSID_MAX_SIZE) {
-		ret = -E2BIG;
-		goto setessid_ret;
-	}
-
-	memset(&reqSSID, 0, sizeof(struct WLAN_802_11_SSID));
-
-	/*
-	 * Check if we asked for `any' or 'particular'
-	 */
-	if (!dwrq->flags) {
-		if (libertas_find_best_network_SSID(priv, &reqSSID)) {
-			PRINTM(INFO, "Could not find best network\n");
-			ret = WLAN_STATUS_SUCCESS;
-			goto setessid_ret;
-		}
-	} else {
-		/* Set the SSID */
-		memcpy(reqSSID.Ssid, extra, ssid_len);
-		reqSSID.SsidLength = ssid_len;
-	}
-
-	PRINTM(INFO, "Requested new SSID = %s\n",
-	       (reqSSID.SsidLength > 0) ? (char *)reqSSID.Ssid : "NULL");
-
-	if (!reqSSID.SsidLength || reqSSID.Ssid[0] < 0x20) {
-		PRINTM(INFO, "Invalid SSID - aborting set_essid\n");
-		ret = -EINVAL;
-		goto setessid_ret;
-	}
-
-	/* If the requested SSID is not a NULL string, join */
-
-	if (Adapter->InfrastructureMode == Wlan802_11Infrastructure) {
-		/* infrastructure mode */
-		PRINTM(INFO, "SSID requested = %s\n", reqSSID.Ssid);
-
-		if (Adapter->MediaConnectStatus == WlanMediaStateConnected) {
-			PRINTM(INFO, "Already Connected ..\n");
-			ret = libertas_send_deauthentication(priv);
-
-			if (ret) {
-				goto setessid_ret;
-			}
-		}
-		if (Adapter->Prescan)
-			libertas_send_specific_SSID_scan(priv, &reqSSID, 1);
-		i = libertas_find_SSID_in_list(Adapter, &reqSSID, NULL,
-				   Wlan802_11Infrastructure);
-		if (i >= 0) {
-			PRINTM(INFO,
-			       "SSID found in scan list ... associating...\n");
-
-			ret = wlan_associate(priv, &Adapter->ScanTable[i]);
-
-			if (ret) {
-				goto setessid_ret;
-			}
-		} else {	/* i >= 0 */
-			ret = i;	/* return -ENETUNREACH, passed from libertas_find_SSID_in_list */
-			goto setessid_ret;
-		}
-	} else {
-		/* ad hoc mode */
-		/* If the requested SSID matches current SSID return */
-		if (!libertas_SSID_cmp(&Adapter->CurBssParams.ssid, &reqSSID)) {
-			ret = WLAN_STATUS_SUCCESS;
-			goto setessid_ret;
-		}
-
-		if (Adapter->MediaConnectStatus == WlanMediaStateConnected) {
-			/*
-			 * Exit Adhoc mode
-			 */
-			PRINTM(INFO, "Sending Adhoc Stop\n");
-			ret = libertas_stop_adhoc_network(priv);
-
-			if (ret) {
-				goto setessid_ret;
-			}
-
-		}
-
-		/* Scan for the network, do not save previous results.  Stale
-		 *   scan data will cause us to join a non-existant adhoc network
-		 */
-		libertas_send_specific_SSID_scan(priv, &reqSSID, 0);
-
-		/* Search for the requested SSID in the scan table */
-		i = libertas_find_SSID_in_list(Adapter, &reqSSID, NULL, Wlan802_11IBSS);
-
-		if (i >= 0) {
-			PRINTM(INFO, "SSID found at %d in List, so join\n", i);
-			libertas_join_adhoc_network(priv, &Adapter->ScanTable[i]);
-		} else {
-			/* else send START command */
-			PRINTM(INFO, "SSID not found in list, "
-			       "so creating adhoc with ssid = %s\n",
-			       reqSSID.Ssid);
-
-			libertas_start_adhoc_network(priv, &reqSSID);
-		}		/* end of else (START command) */
-	}			/* end of else (Ad hoc mode) */
-
-	/*
-	 * The MediaConnectStatus change can be removed later when
-	 *   the ret code is being properly returned.
-	 */
-	/* Check to see if we successfully connected */
-	if (Adapter->MediaConnectStatus == WlanMediaStateConnected) {
-		ret = WLAN_STATUS_SUCCESS;
-	} else {
-		ret = -ENETDOWN;
-	}
-
-      setessid_ret:
-#ifdef REASSOCIATION
-	up(&Adapter->ReassocSem);
-#endif
-
-	LEAVE();
-	return ret;
-}
-
-/**
- *  @brief Connect to the AP or Ad-hoc Network with specific bssid
- *
- *  @param dev          A pointer to net_device structure
- *  @param info         A pointer to iw_request_info structure
- *  @param awrq         A pointer to iw_param structure
- *  @param extra        A pointer to extra data buf
- *  @return             WLAN_STATUS_SUCCESS --success, otherwise fail
- */
-int libertas_set_wap(struct net_device *dev, struct iw_request_info *info,
-		 struct sockaddr *awrq, char *extra)
-{
-	wlan_private *priv = dev->priv;
-	wlan_adapter *Adapter = priv->adapter;
-	int ret = WLAN_STATUS_SUCCESS;
-	const u8 bcast[ETH_ALEN] = { 255, 255, 255, 255, 255, 255 };
-	u8 reqBSSID[ETH_ALEN];
-	int i;
-
-	ENTER();
-
-//Application should call scan before call this function.    
-
-	if (awrq->sa_family != ARPHRD_ETHER)
-		return -EINVAL;
-
-	PRINTM(INFO, "ASSOC: WAP: sa_data: %02x:%02x:%02x:%02x:%02x:%02x\n",
-	       (u8) awrq->sa_data[0], (u8) awrq->sa_data[1],
-	       (u8) awrq->sa_data[2], (u8) awrq->sa_data[3],
-	       (u8) awrq->sa_data[4], (u8) awrq->sa_data[5]);
-
-#ifdef REASSOCIATION
-	// cancel re-association timer if there's one
-	del_timer(&Adapter->reassoc_timer);
-#endif /* REASSOCIATION */
-
-	if (!memcmp(bcast, awrq->sa_data, ETH_ALEN)) {
-		i = libertas_find_best_SSID_in_list(Adapter);
-	} else {
-		memcpy(reqBSSID, awrq->sa_data, ETH_ALEN);
-
-		PRINTM(INFO,
-		       "ASSOC: WAP: Bssid = %02x:%02x:%02x:%02x:%02x:%02x\n",
-		       reqBSSID[0], reqBSSID[1], reqBSSID[2], reqBSSID[3],
-		       reqBSSID[4], reqBSSID[5]);
-
-		/* Search for index position in list for requested MAC */
-		i = libertas_find_BSSID_in_list(Adapter, reqBSSID,
-				    Adapter->InfrastructureMode);
-	}
-
-	if (i < 0) {
-		PRINTM(INFO,
-		       "ASSOC: WAP: MAC address not found in BSSID List\n");
-		return -ENETUNREACH;
-	}
-
-	if (Adapter->InfrastructureMode == Wlan802_11Infrastructure) {
-		if (Adapter->MediaConnectStatus == WlanMediaStateConnected) {
-			ret = libertas_send_deauthentication(priv);
-
-			if (ret) {
-				LEAVE();
-				return ret;
-			}
-		}
-		ret = wlan_associate(priv, &Adapter->ScanTable[i]);
-
-		if (ret) {
-			LEAVE();
-			return ret;
-		}
-	} else {
-		if (Adapter->MediaConnectStatus == WlanMediaStateConnected) {
-			/* Exit Adhoc mode */
-			ret = libertas_stop_adhoc_network(priv);
-
-			if (ret) {
-				LEAVE();
-				return ret;
-			}
-		}
-
-		libertas_join_adhoc_network(priv, &Adapter->ScanTable[i]);
-	}
-
-	/* Check to see if we successfully connected */
-	if (Adapter->MediaConnectStatus == WlanMediaStateConnected) {
-		ret = WLAN_STATUS_SUCCESS;
-	} else {
-		ret = -ENETDOWN;
-	}
-
-	LEAVE();
-	return ret;
-}
-
-/**
  *  @brief Associated to a specific BSS discovered in a scan
  *
  *  @param priv      A pointer to wlan_private structure
@@ -438,11 +175,13 @@ #endif /* REASSOCIATION */
  *
  *  @return          WLAN_STATUS_SUCCESS-success, otherwise fail
  */
-static int wlan_associate(wlan_private * priv, struct bss_descriptor * pBSSDesc)
+int wlan_associate(wlan_private * priv, struct bss_descriptor * pBSSDesc)
 {
 	wlan_adapter *Adapter = priv->adapter;
 	int ret;
 
+	ENTER();
+
 	ret = libertas_prepare_and_send_command(priv, HostCmd_CMD_802_11_AUTHENTICATE,
 				    0, HostCmd_OPTION_WAITFORRSP,
 				    0, pBSSDesc->MacAddress);
@@ -1293,9 +1032,7 @@ int libertas_ret_80211_associate(wlan_pr
 
 	if (pAssocRsp->StatusCode) {
 
-		if (Adapter->MediaConnectStatus == WlanMediaStateConnected) {
-			libertas_mac_event_disconnected(priv);
-		}
+		libertas_mac_event_disconnected(priv);
 
 		PRINTM(INFO,
 		       "ASSOC_RESP: Association Failed, status code = %d\n",
@@ -1536,9 +1273,9 @@ int libertas_reassociation_thread(void *
 		}
 
 		/* The semaphore is used to avoid reassociation thread and 
-		   libertas_set_scan/libertas_set_essid interrupting each other.
+		   libertas_set_scan/wlan_set_essid interrupting each other.
 		   Reassociation should be disabled completely by application if 
-		   libertas_set_user_scan_ioctl/libertas_set_wap is used.
+		   libertas_set_user_scan_ioctl/wlan_set_wap is used.
 		 */
 		if (down_interruptible(&Adapter->ReassocSem)) {
 			PRINTM(FATAL,
diff --git a/drivers/net/wireless/libertas/wlan_join.h b/drivers/net/wireless/libertas/wlan_join.h
index 69f21a1..32e77f7 100644
--- a/drivers/net/wireless/libertas/wlan_join.h
+++ b/drivers/net/wireless/libertas/wlan_join.h
@@ -90,10 +90,6 @@ extern int libertas_send_deauth(wlan_pri
 
 extern int libertas_do_adhocstop_ioctl(wlan_private * priv);
 
-#ifdef __KERNEL__
-struct iw_request_info;
-extern int libertas_set_wap(struct net_device *dev, struct iw_request_info *info,
-			struct sockaddr *awrq, char *extra);
-#endif
+int wlan_associate(wlan_private * priv, struct bss_descriptor * pBSSDesc);
 
 #endif
diff --git a/drivers/net/wireless/libertas/wlan_main.c b/drivers/net/wireless/libertas/wlan_main.c
index 5ff8e4e..2c29a45 100644
--- a/drivers/net/wireless/libertas/wlan_main.c
+++ b/drivers/net/wireless/libertas/wlan_main.c
@@ -59,6 +59,7 @@ #include "wlan_dev.h"
 #include "wlan_fw.h"
 #include "wlan_wext.h"
 #include "wlan_debugfs.h"
+#include "wlan_assoc.h"
 
 #ifdef REASSOCIATION
 #include "wlan_join.h"
@@ -846,6 +847,8 @@ #define NETIF_F_DYNALLOC 16
 	wlan_create_thread(wlan_service_main_thread,
 			   &priv->MainThread, "wlan_main_service");
 
+	INIT_WORK(&priv->assoc_work, wlan_association_worker, dev);
+
 #ifdef REASSOCIATION
 	priv->ReassocThread.priv = priv;
 	wlan_create_thread(libertas_reassociation_thread,
diff --git a/drivers/net/wireless/libertas/wlan_scan.c b/drivers/net/wireless/libertas/wlan_scan.c
index b6c42f7..d7034bd 100644
--- a/drivers/net/wireless/libertas/wlan_scan.c
+++ b/drivers/net/wireless/libertas/wlan_scan.c
@@ -1419,9 +1419,9 @@ int libertas_find_SSID_in_list(wlan_adap
  *
  *  @return         index in BSSID list
  */
-int libertas_find_best_SSID_in_list(wlan_adapter * Adapter)
+int libertas_find_best_SSID_in_list(wlan_adapter * Adapter,
+                                    enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode)
 {
-	int mode = Adapter->InfrastructureMode;
 	int bestnet = -ENETUNREACH;
 	u8 bestrssi = 0;
 	int i;
@@ -1467,7 +1467,10 @@ int libertas_find_best_SSID_in_list(wlan
  *
  *  @return             WLAN_STATUS_SUCCESS--success, otherwise--fail
  */
-int libertas_find_best_network_SSID(wlan_private * priv, struct WLAN_802_11_SSID *pSSID)
+int libertas_find_best_network_SSID(wlan_private * priv,
+                                    struct WLAN_802_11_SSID *pSSID,
+                                    enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode,
+                                    enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode)
 {
 	wlan_adapter *Adapter = priv->adapter;
 	int ret = WLAN_STATUS_SUCCESS;
@@ -1480,37 +1483,22 @@ int libertas_find_best_network_SSID(wlan
 
 	wlan_scan_networks(priv, NULL);
 
-	i = libertas_find_best_SSID_in_list(Adapter);
-
-	if (i >= 0) {
-
-		pReqBSSID = &Adapter->ScanTable[i];
-		memcpy(pSSID, &pReqBSSID->Ssid,
-		       sizeof(struct WLAN_802_11_SSID));
-
-		/* Make sure we are in the right mode */
-		if (Adapter->InfrastructureMode == Wlan802_11AutoUnknown) {
-			Adapter->InfrastructureMode =
-			    pReqBSSID->InfrastructureMode;
-
-			ret = libertas_prepare_and_send_command(priv,
-						    HostCmd_CMD_802_11_SNMP_MIB,
-						    HostCmd_ACT_SET,
-						    HostCmd_OPTION_WAITFORRSP,
-						    OID_802_11_INFRASTRUCTURE_MODE,
-						    NULL);
-
-			if (ret) {
-				LEAVE();
-				return ret;
-			}
-		}
+	i = libertas_find_best_SSID_in_list(Adapter, preferred_mode);
+	if (i < 0) {
+		ret = WLAN_STATUS_FAILURE;
+		goto out;
 	}
 
+	pReqBSSID = &Adapter->ScanTable[i];
+	memcpy(pSSID, &pReqBSSID->Ssid,
+	       sizeof(struct WLAN_802_11_SSID));
+	*out_mode = pReqBSSID->InfrastructureMode;
+
 	if (!pSSID->SsidLength) {
 		ret = WLAN_STATUS_FAILURE;
 	}
 
+out:
 	LEAVE();
 	return ret;
 }
diff --git a/drivers/net/wireless/libertas/wlan_scan.h b/drivers/net/wireless/libertas/wlan_scan.h
index 3bdd155..0b9d7b8 100644
--- a/drivers/net/wireless/libertas/wlan_scan.h
+++ b/drivers/net/wireless/libertas/wlan_scan.h
@@ -281,11 +281,13 @@ extern int libertas_SSID_cmp(struct WLAN
 		   struct WLAN_802_11_SSID *ssid2);
 extern int libertas_find_SSID_in_list(wlan_adapter * Adapter, struct WLAN_802_11_SSID *ssid,
 			  u8 * bssid, int mode);
-extern int libertas_find_best_SSID_in_list(wlan_adapter * Adapter);
+int libertas_find_best_SSID_in_list(wlan_adapter * Adapter, enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode);
 extern int libertas_find_BSSID_in_list(wlan_adapter * Adapter, u8 * bssid, int mode);
 
-extern int libertas_find_best_network_SSID(wlan_private * priv,
-			       struct WLAN_802_11_SSID *pSSID);
+int libertas_find_best_network_SSID(wlan_private * priv,
+			struct WLAN_802_11_SSID *pSSID,
+			enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode,
+			enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode);
 
 extern int libertas_send_specific_SSID_scan(wlan_private * priv,
 				struct WLAN_802_11_SSID *pRequestedSSID,
diff --git a/drivers/net/wireless/libertas/wlan_wext.c b/drivers/net/wireless/libertas/wlan_wext.c
index c67e78b..2d35baa 100644
--- a/drivers/net/wireless/libertas/wlan_wext.c
+++ b/drivers/net/wireless/libertas/wlan_wext.c
@@ -39,7 +39,9 @@ #include <linux/delay.h>
 #include <linux/if.h>
 #include <linux/if_arp.h>
 #include <linux/wireless.h>
+#include <linux/bitops.h>
 
+#include <net/ieee80211.h>
 #include <net/iw_handler.h>
 
 #include "host.h"
@@ -50,6 +52,7 @@ #include "wlan_dev.h"
 #include "wlan_join.h"
 #include "wlan_version.h"
 #include "wlan_wext.h"
+#include "wlan_assoc.h"
 
 
 /** 
@@ -2035,30 +2038,30 @@ int wlan_get_rate(struct net_device *dev
 int wlan_set_mode(struct net_device *dev,
 		  struct iw_request_info *info, u32 * uwrq, char *extra)
 {
-	int ret = WLAN_STATUS_SUCCESS;
+	int ret = 0;
 	wlan_private *priv = dev->priv;
-	wlan_adapter *Adapter = priv->adapter;
-
-	enum WLAN_802_11_NETWORK_INFRASTRUCTURE WantedMode;
+	wlan_adapter *adapter = priv->adapter;
+	struct assoc_request * assoc_req;
+	enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode;
 
 	ENTER();
 
 	switch (*uwrq) {
 	case IW_MODE_ADHOC:
 		PRINTM(INFO, "Wanted Mode is ad-hoc: current DataRate=%#x\n",
-		       Adapter->DataRate);
-		WantedMode = Wlan802_11IBSS;
-		Adapter->AdhocChannel = DEFAULT_AD_HOC_CHANNEL;
+		       adapter->DataRate);
+		new_mode = Wlan802_11IBSS;
+		adapter->AdhocChannel = DEFAULT_AD_HOC_CHANNEL;
 		break;
 
 	case IW_MODE_INFRA:
 		PRINTM(INFO, "Wanted Mode is Infrastructure\n");
-		WantedMode = Wlan802_11Infrastructure;
+		new_mode = Wlan802_11Infrastructure;
 		break;
 
 	case IW_MODE_AUTO:
 		PRINTM(INFO, "Wanted Mode is Auto\n");
-		WantedMode = Wlan802_11AutoUnknown;
+		new_mode = Wlan802_11AutoUnknown;
 		break;
 
 	default:
@@ -2066,82 +2069,27 @@ int wlan_set_mode(struct net_device *dev
 		return -EINVAL;
 	}
 
-	if (Adapter->InfrastructureMode == WantedMode ||
-	    WantedMode == Wlan802_11AutoUnknown) {
-		PRINTM(INFO, "Already set to required mode! No change!\n");
-
-		Adapter->InfrastructureMode = WantedMode;
-
-		LEAVE();
-		return WLAN_STATUS_SUCCESS;
-	}
-
-	if (Adapter->InfrastructureMode == Wlan802_11Infrastructure) {
-		if (Adapter->PSState != PS_STATE_FULL_POWER) {
-			libertas_ps_wakeup(priv, HostCmd_OPTION_WAITFORRSP);
-		}
-		Adapter->PSMode = Wlan802_11PowerModeCAM;
-	}
-
-	if (Adapter->MediaConnectStatus == WlanMediaStateConnected) {
-		if (Adapter->InfrastructureMode == Wlan802_11Infrastructure) {
-			ret = libertas_send_deauthentication(priv);
-
-			if (ret) {
-				LEAVE();
-				return ret;
-			}
-		} else if (Adapter->InfrastructureMode == Wlan802_11IBSS) {
-			/* If current mode is Adhoc, clean stale information */
-			ret = libertas_stop_adhoc_network(priv);
-
-			if (ret) {
-				LEAVE();
-				return ret;
-			}
-		}
-	}
-
-	if (Adapter->SecInfo.WEPStatus == Wlan802_11WEPEnabled) {
-		/* If there is a key with the specified SSID, 
-		 * send REMOVE WEP command, to make sure we clean up
-		 * the WEP keys in firmware
-		 */
-		ret = libertas_prepare_and_send_command(priv,
-					    HostCmd_CMD_802_11_SET_WEP,
-					    HostCmd_ACT_REMOVE,
-					    HostCmd_OPTION_WAITFORRSP,
-					    0, NULL);
-
-		if (ret) {
-			LEAVE();
-			return ret;
-		}
-
-		Adapter->CurrentPacketFilter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
-
-		libertas_set_mac_packet_filter(priv);
+	mutex_lock(&adapter->lock);
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+	} else {
+		assoc_req->mode = new_mode;
 	}
 
-	Adapter->SecInfo.WEPStatus = Wlan802_11WEPDisabled;
-	Adapter->SecInfo.AuthenticationMode = Wlan802_11AuthModeOpen;
-
-	Adapter->InfrastructureMode = WantedMode;
-
-	ret = libertas_prepare_and_send_command(priv,
-				    HostCmd_CMD_802_11_SNMP_MIB,
-				    0, HostCmd_OPTION_WAITFORRSP,
-				    OID_802_11_INFRASTRUCTURE_MODE, NULL);
-
-	if (ret) {
-		LEAVE();
-		return ret;
+	if (ret == 0) {
+		set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
+		wlan_postpone_association_work(priv);
+	} else {
+		wlan_cancel_association_work(priv);
 	}
+	mutex_unlock(&adapter->lock);
 
 	LEAVE();
-	return WLAN_STATUS_SUCCESS;
+	return ret;
 }
 
+
 /** 
  *  @brief Get Encryption key
  *   
@@ -2155,7 +2103,6 @@ static int wlan_get_encode(struct net_de
 			   struct iw_request_info *info,
 			   struct iw_point *dwrq, u8 * extra)
 {
-
 	wlan_private *priv = dev->priv;
 	wlan_adapter *adapter = priv->adapter;
 	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
@@ -2191,6 +2138,8 @@ static int wlan_get_encode(struct net_de
 
 	memset(extra, 0, 16);
 
+	mutex_lock(&adapter->lock);
+
 	/* Default to returning current transmit key */
 	if (index < 0)
 		index = adapter->wep_tx_keyidx;
@@ -2212,6 +2161,8 @@ static int wlan_get_encode(struct net_de
 		dwrq->flags |= IW_ENCODE_DISABLED;
 	}
 
+	mutex_unlock(&adapter->lock);
+
 	dwrq->flags |= IW_ENCODE_NOKEY;
 
 	PRINTM(INFO, "Key:%02x:%02x:%02x:%02x:%02x:%02x KeyLen=%d\n",
@@ -2224,74 +2175,6 @@ static int wlan_get_encode(struct net_de
 	return WLAN_STATUS_SUCCESS;
 }
 
-/**
- *  @brief Send all encryption settings to the firmware
- *
- *  @param priv			A pointer to private card structure
- *  @return 	   		0 on success, otherwise failure code
- */
-static int wlan_set_security (wlan_private *priv)
-{
-	wlan_adapter *adapter = priv->adapter;
-	int ret;
-
-	ENTER();
-
-	/* Set or remove WEP keys */
-	if (   adapter->wep_keys[0].len
-	    || adapter->wep_keys[1].len
-	    || adapter->wep_keys[2].len
-	    || adapter->wep_keys[3].len) {
-		ret = libertas_prepare_and_send_command(priv,
-					    HostCmd_CMD_802_11_SET_WEP,
-					    HostCmd_ACT_ADD,
-					    HostCmd_OPTION_WAITFORRSP,
-					    0, NULL);
-	} else {
-		ret = libertas_prepare_and_send_command(priv,
-					    HostCmd_CMD_802_11_SET_WEP,
-					    HostCmd_ACT_REMOVE,
-					    HostCmd_OPTION_WAITFORRSP,
-					    0, NULL);
-	}
-
-	if (ret)
-		goto out;
-
-	/* Enable/disable the MAC's WEP packet filter */
-	if (adapter->SecInfo.WEPStatus == Wlan802_11WEPEnabled)
-		adapter->CurrentPacketFilter |= HostCmd_ACT_MAC_WEP_ENABLE;
-	else
-		adapter->CurrentPacketFilter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
-	ret = libertas_set_mac_packet_filter(priv);
-	if (ret)
-		goto out;
-
-	/* Enable/Disable RSN */
-	ret = libertas_prepare_and_send_command(priv,
-				    HostCmd_CMD_802_11_ENABLE_RSN,
-				    HostCmd_ACT_SET,
-				    HostCmd_OPTION_WAITFORRSP,
-				    0, NULL);
-	if (ret)
-		goto out;
-
-	ret = libertas_prepare_and_send_command(priv,
-				    HostCmd_CMD_802_11_KEY_MATERIAL,
-				    HostCmd_ACT_SET,
-				    HostCmd_OPTION_WAITFORRSP,
-				    0, NULL);
-	if (ret)
-		goto out;
-
-	ret = 0;
-
-out:
-	LEAVE();
-	return ret;
-}
-
-
 /** 
  *  @brief Set Encryption key (internal)
  *   
@@ -2302,13 +2185,12 @@ out:
  *  @param set_tx_key		Force set TX key (1 = yes, 0 = no)
  *  @return 	   		WLAN_STATUS_SUCCESS --success, otherwise fail
  */
-static int wlan_set_wep_key(wlan_private *priv,
+static int wlan_set_wep_key(struct assoc_request *assoc_req,
 			    const char *key_material,
 			    u16 key_length,
 			    u16 index,
 			    int set_tx_key)
 {
-	wlan_adapter *adapter = priv->adapter;
 	struct WLAN_802_11_KEY *pKey;
 
 	ENTER();
@@ -2325,11 +2207,12 @@ static int wlan_set_wep_key(wlan_private
 		return -EINVAL;
 	}
 
-	pKey = &adapter->wep_keys[index];
-	memset(pKey, 0, sizeof(struct WLAN_802_11_KEY));
-	pKey->type = KEY_TYPE_ID_WEP;
-	
+	pKey = &assoc_req->wep_keys[index];
+
 	if (key_length > 0) {
+		memset(pKey, 0, sizeof(struct WLAN_802_11_KEY));
+		pKey->type = KEY_TYPE_ID_WEP;
+	
 		/* Standardize the key length */
 		pKey->len = (key_length > KEY_LEN_WEP_40) ?
 		                KEY_LEN_WEP_104 : KEY_LEN_WEP_40;
@@ -2343,10 +2226,10 @@ static int wlan_set_wep_key(wlan_private
 			LEAVE();
 			return -EINVAL;
 		}
-		adapter->wep_tx_keyidx = index;
+		assoc_req->wep_tx_keyidx = index;
 	}
 
-	adapter->SecInfo.WEPStatus = Wlan802_11WEPEnabled;
+	assoc_req->secinfo.WEPStatus = Wlan802_11WEPEnabled;
 
 	LEAVE();
 	return 0;
@@ -2370,17 +2253,20 @@ static int validate_key_index(u16 def_in
 	return 0;
 }
 
-static void disable_wep(wlan_adapter *adapter)
+static void disable_wep(struct assoc_request *assoc_req)
 {
 	int i;
 
 	/* Set Open System auth mode */
-	adapter->SecInfo.AuthenticationMode = Wlan802_11AuthModeOpen;
+	assoc_req->secinfo.AuthenticationMode = Wlan802_11AuthModeOpen;
 
 	/* Clear WEP keys and mark WEP as disabled */
-	adapter->SecInfo.WEPStatus = Wlan802_11WEPDisabled;
+	assoc_req->secinfo.WEPStatus = Wlan802_11WEPDisabled;
 	for (i = 0; i < 4; i++)
-		adapter->wep_keys[i].len = 0;
+		assoc_req->wep_keys[i].len = 0;
+
+	set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+	set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
 }
 
 /** 
@@ -2399,67 +2285,61 @@ int wlan_set_encode(struct net_device *d
 	int ret = 0;
 	wlan_private *priv = dev->priv;
 	wlan_adapter *adapter = priv->adapter;
-	int prev_auth_mode;
+	struct assoc_request * assoc_req;
+	u16 is_default = 0, index = 0, set_tx_key = 0;
 
 	ENTER();
 
-	prev_auth_mode = adapter->SecInfo.AuthenticationMode;
+	mutex_lock(&adapter->lock);
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
 
 	if (dwrq->flags & IW_ENCODE_DISABLED) {
-		disable_wep (adapter);
-	} else {
-		u16 is_default = 0, index, set_tx_key = 0;
+		disable_wep (assoc_req);
+		goto out;
+	}
 		
-		ret = validate_key_index(adapter->wep_tx_keyidx,
-		                         (dwrq->flags & IW_ENCODE_INDEX),
-		                         &index, &is_default);
-		if (ret) {
-			LEAVE();
-			return -EINVAL;
-		}
- 
-		/* If WEP isn't enabled, or if there is no key data but a valid
-		 * index, set the TX key.
-		 */
-		if ((adapter->SecInfo.WEPStatus != Wlan802_11WEPEnabled)
-		    || (dwrq->length == 0 && !is_default))
-			set_tx_key = 1;
-
-		/* Send the key to the firmware */
-		ret = wlan_set_wep_key (priv, extra, dwrq->length, index,
-					set_tx_key);
-		if (ret) {
-			LEAVE();
-			return ret;
-		}
-
-		if (dwrq->flags & IW_ENCODE_RESTRICTED) {
-			adapter->SecInfo.AuthenticationMode =
-			    Wlan802_11AuthModeShared;
-		} else if (dwrq->flags & IW_ENCODE_OPEN) {
-			adapter->SecInfo.AuthenticationMode =
-			    Wlan802_11AuthModeOpen;
-		}
+	ret = validate_key_index(assoc_req->wep_tx_keyidx,
+	                         (dwrq->flags & IW_ENCODE_INDEX),
+	                         &index, &is_default);
+	if (ret) {
+		ret = -EINVAL;
+		goto out;
 	}
 
-#if 0
-	/*
-	 * If authentication mode changed - de-authenticate, set authentication
-	 * method and re-associate if we were previously associated.
+	/* If WEP isn't enabled, or if there is no key data but a valid
+	 * index, set the TX key.
 	 */
-	if (   adapter->SecInfo.AuthenticationMode != prev_auth_mode
-	    && adapter->MediaConnectStatus == WlanMediaStateConnected
-	    && adapter->InfrastructureMode == Wlan802_11Infrastructure) {
-		/* De-authenticate from AP */
-		ret = libertas_send_deauthentication(priv);
-		if (ret)
-			return ret;
+	if ((assoc_req->secinfo.WEPStatus != Wlan802_11WEPEnabled)
+	    || (dwrq->length == 0 && !is_default))
+		set_tx_key = 1;
+
+	ret = wlan_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key);
+	if (ret)
+		goto out;
+
+	if (dwrq->length)
+		set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+	if (set_tx_key)
+		set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
+
+	if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+		assoc_req->secinfo.AuthenticationMode = Wlan802_11AuthModeShared;
+	} else if (dwrq->flags & IW_ENCODE_OPEN) {
+		assoc_req->secinfo.AuthenticationMode = Wlan802_11AuthModeOpen;
 	}
-#endif
 
-	/* Push keys and security info to firmware */
-	if (ret == 0)
-		ret = wlan_set_security(priv);
+out:
+	if (ret == 0) {
+		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		wlan_postpone_association_work(priv);
+	} else {
+		wlan_cancel_association_work(priv);
+	}
+	mutex_unlock(&adapter->lock);
 
 	LEAVE();
 	return ret;
@@ -2570,45 +2450,57 @@ static int wlan_set_encodeext(struct net
 	wlan_adapter *adapter = priv->adapter;
 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
 	int alg = ext->alg;
+	struct assoc_request * assoc_req;
 
 	ENTER();
 
+	mutex_lock(&adapter->lock);
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
 	if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
-		disable_wep (adapter);
+		disable_wep (assoc_req);
 	} else if (alg == IW_ENCODE_ALG_WEP) {
 		u16 is_default = 0, index, set_tx_key = 0;
 
-		ret = validate_key_index(adapter->wep_tx_keyidx,
+		ret = validate_key_index(assoc_req->wep_tx_keyidx,
 		                         (dwrq->flags & IW_ENCODE_INDEX),
 		                         &index, &is_default);
-		if (ret) {
-			LEAVE();
-			return -EINVAL;
-		}
+		if (ret)
+			goto out;
 
 		/* If WEP isn't enabled, or if there is no key data but a valid
 		 * index, or if the set-TX-key flag was passed, set the TX key.
 		 */
-		if ((adapter->SecInfo.WEPStatus != Wlan802_11WEPEnabled)
+		if ((assoc_req->secinfo.WEPStatus != Wlan802_11WEPEnabled)
 		    || (dwrq->length == 0 && !is_default)
 		    || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
 			set_tx_key = 1;
 
 		/* Copy key to driver */
-		ret = wlan_set_wep_key (priv, ext->key, ext->key_len, index,
+		ret = wlan_set_wep_key (assoc_req, ext->key, ext->key_len, index,
 					set_tx_key);
-		if (ret) {
-			LEAVE();
-			return ret;
-		}
+		if (ret)
+			goto out;
 
 		if (dwrq->flags & IW_ENCODE_RESTRICTED) {
-			adapter->SecInfo.AuthenticationMode =
+			assoc_req->secinfo.AuthenticationMode =
 			    Wlan802_11AuthModeShared;
 		} else if (dwrq->flags & IW_ENCODE_OPEN) {
-			adapter->SecInfo.AuthenticationMode =
+			assoc_req->secinfo.AuthenticationMode =
 			    Wlan802_11AuthModeOpen;
 		}
+
+		/* Mark the various WEP bits as modified */
+		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		if (dwrq->length)
+			set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+		if (set_tx_key)
+			set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
+
 	} else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
 		struct WLAN_802_11_KEY * pKey;
 
@@ -2621,24 +2513,27 @@ static int wlan_set_encodeext(struct net
 				       "type %d.\n",
 				       ext->key_len,
 				       alg);
-				LEAVE();
-				return -EINVAL;
+				ret = -EINVAL;
+				goto out;
 		}
 
 		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
-			pKey = &adapter->wpa_mcast_key;
+			pKey = &assoc_req->wpa_mcast_key;
 		else
-			pKey = &adapter->wpa_unicast_key;
+			pKey = &assoc_req->wpa_unicast_key;
 
 		memset(pKey, 0, sizeof (struct WLAN_802_11_KEY));
 		memcpy(pKey->key, ext->key, ext->key_len);
 		pKey->len = ext->key_len;
 		pKey->flags = KEY_INFO_WPA_ENABLED;
 
-		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
 			pKey->flags |= KEY_INFO_WPA_MCAST;
-		else
+			set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
+		} else {
 			pKey->flags |= KEY_INFO_WPA_UNICAST;
+			set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
+		}
 
 		if (alg == IW_ENCODE_ALG_TKIP)
 			pKey->type = KEY_TYPE_ID_TKIP;
@@ -2646,18 +2541,23 @@ static int wlan_set_encodeext(struct net
 			pKey->type = KEY_TYPE_ID_AES;
 
 		/* If WPA isn't enabled yet, do that now */
-		if (   adapter->SecInfo.WPAEnabled == 0
-		    && adapter->SecInfo.WPA2Enabled == 0) {
-			adapter->SecInfo.WPAEnabled = 1;
-			adapter->SecInfo.WPA2Enabled = 1;
+		if (   assoc_req->secinfo.WPAEnabled == 0
+		    && assoc_req->secinfo.WPA2Enabled == 0) {
+			assoc_req->secinfo.WPAEnabled = 1;
+			assoc_req->secinfo.WPA2Enabled = 1;
+			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
 		}
 
-		disable_wep (adapter);
+		disable_wep (assoc_req);
  	}
 
-	/* Push keys and security info to firmware */
-	if (ret == 0)
-		ret = wlan_set_security(priv);
+out:
+	if (ret == 0) {
+		wlan_postpone_association_work(priv);
+	} else {
+		wlan_cancel_association_work(priv);
+	}
+	mutex_unlock(&adapter->lock);
 
 	LEAVE();
 	return ret;
@@ -2671,28 +2571,43 @@ static int wlan_set_genie(struct net_dev
 {
 	wlan_private *priv = dev->priv;
 	wlan_adapter *adapter = priv->adapter;
+	int ret = 0;
+	struct assoc_request * assoc_req;
 
-	if (!adapter->SecInfo.WPAEnabled && !adapter->SecInfo.WPA2Enabled) {
-		LEAVE();
-		return -EOPNOTSUPP;
+	ENTER();
+
+	mutex_lock(&adapter->lock);
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
 	}
 
 	if (dwrq->length > MAX_WPA_IE_LEN ||
 	    (dwrq->length && extra == NULL)) {
-		LEAVE();
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 
 	if (dwrq->length) {
-		memcpy(&adapter->Wpa_ie[0], extra, dwrq->length);
-		adapter->Wpa_ie_len = dwrq->length;
+		memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length);
+		assoc_req->wpa_ie_len = dwrq->length;
 	} else {
-		memset(&adapter->Wpa_ie[0], 0, sizeof(adapter->Wpa_ie));
-		adapter->Wpa_ie_len = 0;
+		memset(&assoc_req->wpa_ie[0], 0, sizeof(adapter->Wpa_ie));
+		assoc_req->wpa_ie_len = 0;
 	}
 
+out:
+	if (ret == 0) {
+		set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
+		wlan_postpone_association_work(priv);
+	} else {
+		wlan_cancel_association_work(priv);
+	}
+	mutex_unlock(&adapter->lock);
+
 	LEAVE();
-	return 0;
+	return ret;
 }
 
 static int wlan_get_genie(struct net_device *dev,
@@ -2731,9 +2646,19 @@ static int wlan_set_auth(struct net_devi
 {
 	wlan_private *priv = dev->priv;
 	wlan_adapter *adapter = priv->adapter;
+	struct assoc_request * assoc_req;
+	int ret = 0;
+	int updated = 0;
 
 	ENTER();
 
+	mutex_lock(&adapter->lock);
+	assoc_req = wlan_get_association_request(adapter);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
 	switch (dwrq->flags & IW_AUTH_INDEX) {
 	case IW_AUTH_TKIP_COUNTERMEASURES:
 	case IW_AUTH_CIPHER_PAIRWISE:
@@ -2746,21 +2671,22 @@ static int wlan_set_auth(struct net_devi
 
 	case IW_AUTH_WPA_VERSION:
 		if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
-			adapter->SecInfo.WPAEnabled = 0;
-			adapter->SecInfo.WPA2Enabled = 0;
+			assoc_req->secinfo.WPAEnabled = 0;
+			assoc_req->secinfo.WPA2Enabled = 0;
 		}
 		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
-			adapter->SecInfo.WPAEnabled = 1;
-			adapter->SecInfo.WEPStatus = Wlan802_11WEPDisabled;
-			adapter->SecInfo.AuthenticationMode =
+			assoc_req->secinfo.WPAEnabled = 1;
+			assoc_req->secinfo.WEPStatus = Wlan802_11WEPDisabled;
+			assoc_req->secinfo.AuthenticationMode =
 			    Wlan802_11AuthModeOpen;
 		}
 		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
-			adapter->SecInfo.WPA2Enabled = 1;
-			adapter->SecInfo.WEPStatus = Wlan802_11WEPDisabled;
-			adapter->SecInfo.AuthenticationMode =
+			assoc_req->secinfo.WPA2Enabled = 1;
+			assoc_req->secinfo.WEPStatus = Wlan802_11WEPDisabled;
+			assoc_req->secinfo.AuthenticationMode =
 			    Wlan802_11AuthModeOpen;
 		}
+		updated = 1;
 		break;
 
 	case IW_AUTH_DROP_UNENCRYPTED:
@@ -2771,46 +2697,59 @@ static int wlan_set_auth(struct net_devi
 			adapter->CurrentPacketFilter &=
 			    ~HostCmd_ACT_MAC_STRICT_PROTECTION_ENABLE;
 		}
+		updated = 1;
 		break;
 
 	case IW_AUTH_80211_AUTH_ALG:
 		if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) {
-			adapter->SecInfo.AuthenticationMode =
+			assoc_req->secinfo.AuthenticationMode =
 			    Wlan802_11AuthModeShared;
 		} else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
-			adapter->SecInfo.AuthenticationMode =
+			assoc_req->secinfo.AuthenticationMode =
 			    Wlan802_11AuthModeOpen;
 		} else if (dwrq->value & IW_AUTH_ALG_LEAP) {
-			adapter->SecInfo.AuthenticationMode =
+			assoc_req->secinfo.AuthenticationMode =
 			    Wlan802_11AuthModeNetworkEAP;
 		} else {
-			return -EINVAL;
+			ret = -EINVAL;
 		}
+		updated = 1;
 		break;
 
 	case IW_AUTH_WPA_ENABLED:
 		if (dwrq->value) {
-			if (!adapter->SecInfo.WPAEnabled &&
-			    !adapter->SecInfo.WPA2Enabled) {
-				adapter->SecInfo.WPAEnabled = 1;
-				adapter->SecInfo.WPA2Enabled = 1;
-				adapter->SecInfo.WEPStatus = Wlan802_11WEPDisabled;
-				adapter->SecInfo.AuthenticationMode =
+			if (!assoc_req->secinfo.WPAEnabled &&
+			    !assoc_req->secinfo.WPA2Enabled) {
+				assoc_req->secinfo.WPAEnabled = 1;
+				assoc_req->secinfo.WPA2Enabled = 1;
+				assoc_req->secinfo.WEPStatus = Wlan802_11WEPDisabled;
+				assoc_req->secinfo.AuthenticationMode =
 				    Wlan802_11AuthModeOpen;
 			}
 		} else {
-			adapter->SecInfo.WPAEnabled = 0;
-			adapter->SecInfo.WPA2Enabled = 0;
-		}		
+			assoc_req->secinfo.WPAEnabled = 0;
+			assoc_req->secinfo.WPA2Enabled = 0;
+		}
+		updated = 1;
 		break;
 
 	default:
-		LEAVE();
-		return -EOPNOTSUPP;
+		ret = -EOPNOTSUPP;
+		break;
 	}
 
+out:
+	if (ret == 0) {
+		if (updated)
+			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		wlan_postpone_association_work(priv);
+	} else if (ret != -EOPNOTSUPP) {
+		wlan_cancel_association_work(priv);
+	}
+	mutex_unlock(&adapter->lock);
+
 	LEAVE();
-	return 0;
+	return ret;
 }
 
 static int wlan_get_auth(struct net_device *dev,
@@ -2971,6 +2910,152 @@ int wlan_get_essid(struct net_device *de
 	return WLAN_STATUS_SUCCESS;
 }
 
+/**
+ *  @brief Set essid
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param dwrq         A pointer to iw_point structure
+ *  @param extra        A pointer to extra data buf
+ *  @return             WLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int wlan_set_essid(struct net_device *dev, struct iw_request_info *info,
+		   struct iw_point *dwrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *Adapter = priv->adapter;
+	int ret = 0;
+	struct WLAN_802_11_SSID ssid;
+	struct assoc_request * assoc_req;
+	int ssid_len = dwrq->length;
+
+	ENTER();
+
+#ifdef REASSOCIATION
+	// cancel re-association timer if there's one
+	del_timer(&Adapter->reassoc_timer);
+
+	if (down_interruptible(&Adapter->ReassocSem)) {
+		PRINTM(FATAL, "%s(%d): could not acquire reassociation lock\n",
+			__func__, __LINE__);
+		return -EBUSY;
+	}
+#endif /* REASSOCIATION */
+
+	/*
+	 * WE-20 and earlier NULL pad the end of the SSID and increment
+	 * SSID length so it can be used like a string.  WE-21 and later don't,
+	 * but some userspace tools aren't able to cope with the change.
+	 */
+	if ((ssid_len > 0) && (extra[ssid_len - 1] == '\0'))
+		ssid_len--;
+
+	/* Check the size of the string */
+	if (ssid_len > IW_ESSID_MAX_SIZE) {
+		ret = -E2BIG;
+		goto out;
+	}
+
+	memset(&ssid, 0, sizeof(struct WLAN_802_11_SSID));
+
+	if (!dwrq->flags || !ssid_len) {
+		/* "any" SSID requested; leave SSID blank */
+	} else {
+		/* Specific SSID requested */
+		memcpy(&ssid.Ssid, extra, ssid_len);
+		ssid.SsidLength = ssid_len;
+	}
+
+	PRINTM(INFO, "Requested new SSID = %s\n",
+	       (ssid.SsidLength > 0) ? (char *)ssid.Ssid : "any");
+
+	if (!ssid.SsidLength) {
+		PRINTM(INFO, "Invalid SSID - aborting set_essid\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+out:
+	mutex_lock(&Adapter->lock);
+	if (ret == 0) {
+		/* Get or create the current association request */
+		assoc_req = wlan_get_association_request(Adapter);
+		if (!assoc_req) {
+			ret = -ENOMEM;
+		} else {
+			/* Copy the SSID to the association request */
+			memcpy(&assoc_req->ssid, &ssid, sizeof(struct WLAN_802_11_SSID));
+			set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
+			wlan_postpone_association_work(priv);
+		}
+	}
+
+	/* Cancel the association request if there was an error */
+	if (ret != 0) {
+		wlan_cancel_association_work(priv);
+	}
+
+	mutex_unlock(&Adapter->lock);
+
+#ifdef REASSOCIATION
+	up(&Adapter->ReassocSem);
+#endif
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Connect to the AP or Ad-hoc Network with specific bssid
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param awrq         A pointer to iw_param structure
+ *  @param extra        A pointer to extra data buf
+ *  @return             WLAN_STATUS_SUCCESS --success, otherwise fail
+ */
+int wlan_set_wap(struct net_device *dev, struct iw_request_info *info,
+		 struct sockaddr *awrq, char *extra)
+{
+	wlan_private *priv = dev->priv;
+	wlan_adapter *Adapter = priv->adapter;
+	struct assoc_request * assoc_req;
+	int ret = 0;
+
+	ENTER();
+
+	if (awrq->sa_family != ARPHRD_ETHER)
+		return -EINVAL;
+
+	PRINTM(INFO, "ASSOC: WAP: sa_data: " MAC_FMT "\n", MAC_ARG(awrq->sa_data));
+
+#ifdef REASSOCIATION
+	// cancel re-association timer if there's one
+	del_timer(&Adapter->reassoc_timer);
+#endif /* REASSOCIATION */
+
+	mutex_lock(&Adapter->lock);
+
+	/* Get or create the current association request */
+	assoc_req = wlan_get_association_request(Adapter);
+	if (!assoc_req) {
+		wlan_cancel_association_work(priv);
+		ret = -ENOMEM;
+	} else {
+		/* Copy the BSSID to the association request */
+		memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN);
+		set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags);
+		wlan_postpone_association_work(priv);
+	}
+
+	mutex_unlock(&Adapter->lock);
+
+#ifdef REASSOCIATION
+	up(&Adapter->ReassocSem);
+#endif
+	return ret;
+}	
+
 /** 
  *  @brief Get version 
  *   
@@ -3025,13 +3110,13 @@ static const iw_handler wlan_handler[] =
 	iw_handler_get_spy,	/* SIOCGIWSPY */
 	iw_handler_set_thrspy,	/* SIOCSIWTHRSPY */
 	iw_handler_get_thrspy,	/* SIOCGIWTHRSPY */
-	(iw_handler) libertas_set_wap,	/* SIOCSIWAP */
+	(iw_handler) wlan_set_wap,	/* SIOCSIWAP */
 	(iw_handler) wlan_get_wap,	/* SIOCGIWAP */
 	(iw_handler) NULL,	/* SIOCSIWMLME */
 	(iw_handler) NULL,	/* SIOCGIWAPLIST - deprecated */
 	(iw_handler) libertas_set_scan,	/* SIOCSIWSCAN */
 	(iw_handler) libertas_get_scan,	/* SIOCGIWSCAN */
-	(iw_handler) libertas_set_essid,	/* SIOCSIWESSID */
+	(iw_handler) wlan_set_essid,	/* SIOCSIWESSID */
 	(iw_handler) wlan_get_essid,	/* SIOCGIWESSID */
 	(iw_handler) wlan_set_nick,	/* SIOCSIWNICKN */
 	(iw_handler) wlan_get_nick,	/* SIOCGIWNICKN */


More information about the Commits-kernel mailing list