linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6] mac80211: mlme work improvements
@ 2009-12-02 11:43 Johannes Berg
  2009-12-02 11:43 ` [PATCH 1/6] cfg80211: avoid sending spurious deauth to userspace Johannes Berg
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Johannes Berg @ 2009-12-02 11:43 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless

This patch series fixes some things and then
generalises the mlme work to be more usable
for things like Jouni's off-channel stuff and
also BT3 eventually.

johannes

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/6] cfg80211: avoid sending spurious deauth to userspace
  2009-12-02 11:43 [PATCH 0/6] mac80211: mlme work improvements Johannes Berg
@ 2009-12-02 11:43 ` Johannes Berg
  2009-12-02 11:43 ` [PATCH 2/6] mac80211: dont try to use existing sta for AP Johannes Berg
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2009-12-02 11:43 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless

Before
  commit ca9034592823e8179511e48a78731f95bfdd766c
  Author: Holger Schurig <hs4233@mail.mn-solutions.de>
  Date:   Tue Oct 13 13:45:28 2009 +0200

      cfg80211: remove warning in deauth case

we assumed that drivers never give us spurious deauth
frames because they filter them out based on the auth
state they keep track of. This turned out to be racy,
because userspace might deauth while the AP is also
sending a deauth frame, so the warning was removed.

However, in that case we should not tell userspace
about the AP's frame if it requested deauth "first",
where "first" means it came to cfg80211 first.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/wireless/mlme.c |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

--- wireless-testing.orig/net/wireless/mlme.c	2009-12-01 18:26:53.000000000 +0100
+++ wireless-testing/net/wireless/mlme.c	2009-12-01 18:26:59.000000000 +0100
@@ -137,22 +137,23 @@ void __cfg80211_send_deauth(struct net_d
 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
 	const u8 *bssid = mgmt->bssid;
 	int i;
+	bool found = false;
 
 	ASSERT_WDEV_LOCK(wdev);
 
-	nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
-
 	if (wdev->current_bss &&
 	    memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
 		cfg80211_unhold_bss(wdev->current_bss);
 		cfg80211_put_bss(&wdev->current_bss->pub);
 		wdev->current_bss = NULL;
+		found = true;
 	} else for (i = 0; i < MAX_AUTH_BSSES; i++) {
 		if (wdev->auth_bsses[i] &&
 		    memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
 			cfg80211_unhold_bss(wdev->auth_bsses[i]);
 			cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
 			wdev->auth_bsses[i] = NULL;
+			found = true;
 			break;
 		}
 		if (wdev->authtry_bsses[i] &&
@@ -160,10 +161,16 @@ void __cfg80211_send_deauth(struct net_d
 			cfg80211_unhold_bss(wdev->authtry_bsses[i]);
 			cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
 			wdev->authtry_bsses[i] = NULL;
+			found = true;
 			break;
 		}
 	}
 
+	if (!found)
+		return;
+
+	nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
+
 	if (wdev->sme_state == CFG80211_SME_CONNECTED) {
 		u16 reason_code;
 		bool from_ap;



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/6] mac80211: dont try to use existing sta for AP
  2009-12-02 11:43 [PATCH 0/6] mac80211: mlme work improvements Johannes Berg
  2009-12-02 11:43 ` [PATCH 1/6] cfg80211: avoid sending spurious deauth to userspace Johannes Berg
@ 2009-12-02 11:43 ` Johannes Berg
  2009-12-02 11:43 ` [PATCH 3/6] mac80211: let cfg80211 manage auth state Johannes Berg
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2009-12-02 11:43 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless

Clean out some cruft that could use an already existing
sta_info struct -- that case cannot happen. Also, there's
a bug there -- if allocation/insertion fails then it is
possible that we are left in a lingering state where
mac80211 waits for the AP, cfg80211 waits for mac80211,
but the AP has already replied. Since there's no way to
indicate an internal error, pretend there was a timeout,
i.e. that the AP never responded.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/mlme.c |   57 +++++++++++++++++++++-------------------------------
 1 file changed, 24 insertions(+), 33 deletions(-)

--- wireless-testing.orig/net/mac80211/mlme.c	2009-12-01 18:26:51.000000000 +0100
+++ wireless-testing/net/mac80211/mlme.c	2009-12-01 18:26:59.000000000 +0100
@@ -75,6 +75,9 @@ enum rx_mgmt_action {
 	/* caller must call cfg80211_send_disassoc() */
 	RX_MGMT_CFG80211_DISASSOC,
 
+	/* caller must tell cfg80211 about internal error */
+	RX_MGMT_CFG80211_ASSOC_ERROR,
+
 	/* caller must call cfg80211_auth_timeout() & free work */
 	RX_MGMT_CFG80211_AUTH_TO,
 
@@ -1479,8 +1482,8 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
 	u8 *pos;
 	u32 changed = 0;
-	int i, j;
-	bool have_higher_than_11mbit = false, newsta = false;
+	int i, j, err;
+	bool have_higher_than_11mbit = false;
 	u16 ap_ht_cap_flags;
 
 	/*
@@ -1542,30 +1545,18 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	printk(KERN_DEBUG "%s: associated\n", sdata->name);
 	ifmgd->aid = aid;
 
-	rcu_read_lock();
-
-	/* Add STA entry for the AP */
-	sta = sta_info_get(sdata, wk->bss->cbss.bssid);
+	sta = sta_info_alloc(sdata, wk->bss->cbss.bssid, GFP_KERNEL);
 	if (!sta) {
-		newsta = true;
-
-		rcu_read_unlock();
-
-		sta = sta_info_alloc(sdata, wk->bss->cbss.bssid, GFP_KERNEL);
-		if (!sta) {
-			printk(KERN_DEBUG "%s: failed to alloc STA entry for"
-			       " the AP\n", sdata->name);
-			return RX_MGMT_NONE;
-		}
-
-		set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC |
-				   WLAN_STA_ASSOC_AP);
-		if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
-			set_sta_flags(sta, WLAN_STA_AUTHORIZED);
-
-		rcu_read_lock();
+		printk(KERN_DEBUG "%s: failed to alloc STA entry for"
+		       " the AP\n", sdata->name);
+		return RX_MGMT_CFG80211_ASSOC_ERROR;
 	}
 
+	set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC |
+			   WLAN_STA_ASSOC_AP);
+	if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
+		set_sta_flags(sta, WLAN_STA_AUTHORIZED);
+
 	rates = 0;
 	basic_rates = 0;
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
@@ -1628,18 +1619,14 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	if (elems.wmm_param)
 		set_sta_flags(sta, WLAN_STA_WME);
 
-	if (newsta) {
-		int err = sta_info_insert(sta);
-		if (err) {
-			printk(KERN_DEBUG "%s: failed to insert STA entry for"
-			       " the AP (error %d)\n", sdata->name, err);
-			rcu_read_unlock();
-			return RX_MGMT_NONE;
-		}
+	err = sta_info_insert(sta);
+	sta = NULL;
+	if (err) {
+		printk(KERN_DEBUG "%s: failed to insert STA entry for"
+		       " the AP (error %d)\n", sdata->name, err);
+		return RX_MGMT_CFG80211_ASSOC_ERROR;
 	}
 
-	rcu_read_unlock();
-
 	if (elems.wmm_param)
 		ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param,
 					 elems.wmm_param_len);
@@ -2084,6 +2071,10 @@ static void ieee80211_sta_rx_queued_mgmt
 	case RX_MGMT_CFG80211_DEAUTH:
 		cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
 		break;
+	case RX_MGMT_CFG80211_ASSOC_ERROR:
+		/* an internal error -- pretend timeout for now */
+		cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
+		break;
 	default:
 		WARN(1, "unexpected: %d", rma);
 	}



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 3/6] mac80211: let cfg80211 manage auth state
  2009-12-02 11:43 [PATCH 0/6] mac80211: mlme work improvements Johannes Berg
  2009-12-02 11:43 ` [PATCH 1/6] cfg80211: avoid sending spurious deauth to userspace Johannes Berg
  2009-12-02 11:43 ` [PATCH 2/6] mac80211: dont try to use existing sta for AP Johannes Berg
@ 2009-12-02 11:43 ` Johannes Berg
  2009-12-02 11:43 ` [PATCH 4/6] mac80211: generalise management work a bit Johannes Berg
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2009-12-02 11:43 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless

mac80211 currently hangs on to the auth state by
keeping it on the work list. That can lead to
confusing behaviour like rejecting scans while
authenticated to any AP (but not yet associated.)
It also means that it needs to keep track of the
work struct while associated for when it gets
disassociated (or disassociates.)

Change this to free the work struct after the
authentication completed successfully and
allocate a new one for associating, thereby
letting cfg80211 manage the auth state. Another
change necessary for this is to tell cfg80211
about all unicast deauth frames sent to mac80211
since now it can no longer check the auth state,
but that check was racy anyway.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/ieee80211_i.h |    3 
 net/mac80211/mlme.c        |  173 ++++++++++++++++++---------------------------
 2 files changed, 73 insertions(+), 103 deletions(-)

--- wireless-testing.orig/net/mac80211/mlme.c	2009-12-01 18:26:59.000000000 +0100
+++ wireless-testing/net/mac80211/mlme.c	2009-12-01 18:26:59.000000000 +0100
@@ -949,11 +949,10 @@ static u32 ieee80211_handle_bss_capabili
 }
 
 static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
-				     struct ieee80211_mgd_work *wk,
+				     struct ieee80211_bss *bss,
 				     u32 bss_info_changed)
 {
 	struct ieee80211_local *local = sdata->local;
-	struct ieee80211_bss *bss = wk->bss;
 
 	bss_info_changed |= BSS_CHANGED_ASSOC;
 	/* set timing information */
@@ -966,7 +965,6 @@ static void ieee80211_set_associated(str
 		bss->cbss.capability, bss->has_erp_value, bss->erp_value);
 
 	sdata->u.mgd.associated = bss;
-	sdata->u.mgd.old_associate_work = wk;
 	memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN);
 
 	/* just to be sure */
@@ -1082,8 +1080,7 @@ ieee80211_authenticate(struct ieee80211_
 	return RX_MGMT_NONE;
 }
 
-static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
-				   bool deauth)
+static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_local *local = sdata->local;
@@ -1101,16 +1098,6 @@ static void ieee80211_set_disassoc(struc
 	ifmgd->associated = NULL;
 	memset(ifmgd->bssid, 0, ETH_ALEN);
 
-	if (deauth) {
-		kfree(ifmgd->old_associate_work);
-		ifmgd->old_associate_work = NULL;
-	} else {
-		struct ieee80211_mgd_work *wk = ifmgd->old_associate_work;
-
-		wk->state = IEEE80211_MGD_STATE_IDLE;
-		list_add(&wk->list, &ifmgd->work_list);
-	}
-
 	/*
 	 * we need to commit the associated = NULL change because the
 	 * scan code uses that to determine whether this iface should
@@ -1325,7 +1312,8 @@ EXPORT_SYMBOL(ieee80211_beacon_loss);
 static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
 				     struct ieee80211_mgd_work *wk)
 {
-	wk->state = IEEE80211_MGD_STATE_IDLE;
+	list_del(&wk->list);
+	kfree(wk);
 	printk(KERN_DEBUG "%s: authenticated\n", sdata->name);
 }
 
@@ -1403,7 +1391,6 @@ ieee80211_rx_mgmt_auth(struct ieee80211_
 
 static enum rx_mgmt_action __must_check
 ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
-			 struct ieee80211_mgd_work *wk,
 			 struct ieee80211_mgmt *mgmt, size_t len)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -1415,23 +1402,15 @@ ieee80211_rx_mgmt_deauth(struct ieee8021
 
 	ASSERT_MGD_MTX(ifmgd);
 
-	if (wk)
-		bssid = wk->bss->cbss.bssid;
-	else
-		bssid = ifmgd->associated->cbss.bssid;
+	bssid = ifmgd->associated->cbss.bssid;
 
 	reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
 
 	printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
 			sdata->name, bssid, reason_code);
 
-	if (!wk) {
-		ieee80211_set_disassoc(sdata, true);
-		ieee80211_recalc_idle(sdata->local);
-	} else {
-		list_del(&wk->list);
-		kfree(wk);
-	}
+	ieee80211_set_disassoc(sdata);
+	ieee80211_recalc_idle(sdata->local);
 
 	return RX_MGMT_CFG80211_DEAUTH;
 }
@@ -1460,7 +1439,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80
 	printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n",
 			sdata->name, mgmt->sa, reason_code);
 
-	ieee80211_set_disassoc(sdata, false);
+	ieee80211_set_disassoc(sdata);
 	ieee80211_recalc_idle(sdata->local);
 	return RX_MGMT_CFG80211_DISASSOC;
 }
@@ -1476,6 +1455,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_supported_band *sband;
 	struct sta_info *sta;
+	struct ieee80211_bss *bss = wk->bss;
 	u32 rates, basic_rates;
 	u16 capab_info, status_code, aid;
 	struct ieee802_11_elems elems;
@@ -1494,7 +1474,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	if (len < 24 + 6)
 		return RX_MGMT_NONE;
 
-	if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
+	if (memcmp(bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
 		return RX_MGMT_NONE;
 
 	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
@@ -1524,10 +1504,17 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 		return RX_MGMT_NONE;
 	}
 
+	/*
+	 * Here the association was either successful or not.
+	 */
+
+	/* delete work item -- must be before set_associated for PS */
+	list_del(&wk->list);
+	kfree(wk);
+
 	if (status_code != WLAN_STATUS_SUCCESS) {
 		printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
 		       sdata->name, status_code);
-		wk->state = IEEE80211_MGD_STATE_IDLE;
 		return RX_MGMT_CFG80211_ASSOC;
 	}
 
@@ -1545,7 +1532,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	printk(KERN_DEBUG "%s: associated\n", sdata->name);
 	ifmgd->aid = aid;
 
-	sta = sta_info_alloc(sdata, wk->bss->cbss.bssid, GFP_KERNEL);
+	sta = sta_info_alloc(sdata, bss->cbss.bssid, GFP_KERNEL);
 	if (!sta) {
 		printk(KERN_DEBUG "%s: failed to alloc STA entry for"
 		       " the AP\n", sdata->name);
@@ -1637,18 +1624,14 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	    (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
 	    !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
 		changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
-					       wk->bss->cbss.bssid,
+					       bss->cbss.bssid,
 					       ap_ht_cap_flags);
 
-        /* delete work item -- must be before set_associated for PS */
-	list_del(&wk->list);
-
 	/* set AID and assoc capability,
 	 * ieee80211_set_associated() will tell the driver */
 	bss_conf->aid = aid;
 	bss_conf->assoc_capability = capab_info;
-	/* this will take ownership of wk */
-	ieee80211_set_associated(sdata, wk, changed);
+	ieee80211_set_associated(sdata, bss, changed);
 
 	/*
 	 * Start timer to probe the connection to the AP now.
@@ -1991,8 +1974,7 @@ static void ieee80211_sta_rx_queued_mgmt
 						     skb->len, rx_status);
 			break;
 		case IEEE80211_STYPE_DEAUTH:
-			rma = ieee80211_rx_mgmt_deauth(sdata, NULL,
-						       mgmt, skb->len);
+			rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
 			break;
 		case IEEE80211_STYPE_DISASSOC:
 			rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
@@ -2043,8 +2025,15 @@ static void ieee80211_sta_rx_queued_mgmt
 							   skb->len, true);
 			break;
 		case IEEE80211_STYPE_DEAUTH:
-			rma = ieee80211_rx_mgmt_deauth(sdata, wk, mgmt,
-						       skb->len);
+			if (skb->len >= 24 + 2 /* mgmt + deauth reason */) {
+				/*
+				 * We get here if we get deauth while
+				 * trying to auth/assoc. Telling cfg80211
+				 * is handled below, unconditionally.
+				 */
+				list_del(&wk->list);
+				kfree(wk);
+			}
 			break;
 		}
 		/*
@@ -2058,6 +2047,12 @@ static void ieee80211_sta_rx_queued_mgmt
 
 	mutex_unlock(&ifmgd->mtx);
 
+	if (skb->len >= 24 + 2 /* mgmt + deauth reason */ &&
+	    (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH) {
+		WARN_ON(rma != RX_MGMT_NONE);
+		rma = RX_MGMT_CFG80211_DEAUTH;
+	}
+
 	switch (rma) {
 	case RX_MGMT_NONE:
 		/* no action */
@@ -2108,7 +2103,6 @@ static void ieee80211_sta_work(struct wo
 	struct ieee80211_mgd_work *wk, *tmp;
 	LIST_HEAD(free_work);
 	enum rx_mgmt_action rma;
-	bool anybusy = false;
 
 	if (!ieee80211_sdata_running(sdata))
 		return;
@@ -2163,7 +2157,7 @@ static void ieee80211_sta_work(struct wo
 			printk(KERN_DEBUG "No probe response from AP %pM"
 				" after %dms, disconnecting.\n",
 				bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
-			ieee80211_set_disassoc(sdata, true);
+			ieee80211_set_disassoc(sdata);
 			ieee80211_recalc_idle(local);
 			mutex_unlock(&ifmgd->mtx);
 			/*
@@ -2195,8 +2189,6 @@ static void ieee80211_sta_work(struct wo
 		switch (wk->state) {
 		default:
 			WARN_ON(1);
-			/* fall through */
-		case IEEE80211_MGD_STATE_IDLE:
 			/* nothing */
 			rma = RX_MGMT_NONE;
 			break;
@@ -2219,20 +2211,19 @@ static void ieee80211_sta_work(struct wo
 		case RX_MGMT_CFG80211_ASSOC_TO:
 			list_del(&wk->list);
 			list_add(&wk->list, &free_work);
-			wk->tries = rma; /* small abuse but only local */
+			/*
+			 * small abuse but only local -- keep the
+			 * action type in wk->timeout while the item
+			 * is on the cleanup list
+			 */
+			wk->timeout = rma;
 			break;
 		default:
 			WARN(1, "unexpected: %d", rma);
 		}
 	}
 
-	list_for_each_entry(wk, &ifmgd->work_list, list) {
-		if (wk->state != IEEE80211_MGD_STATE_IDLE) {
-			anybusy = true;
-			break;
-		}
-	}
-	if (!anybusy &&
+	if (list_empty(&ifmgd->work_list) &&
 	    test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request))
 		ieee80211_queue_delayed_work(&local->hw,
 					     &local->scan_work,
@@ -2241,7 +2232,8 @@ static void ieee80211_sta_work(struct wo
 	mutex_unlock(&ifmgd->mtx);
 
 	list_for_each_entry_safe(wk, tmp, &free_work, list) {
-		switch (wk->tries) {
+		/* see above how we're using wk->timeout */
+		switch (wk->timeout) {
 		case RX_MGMT_CFG80211_AUTH_TO:
 			cfg80211_send_auth_timeout(sdata->dev,
 						   wk->bss->cbss.bssid);
@@ -2251,7 +2243,7 @@ static void ieee80211_sta_work(struct wo
 						    wk->bss->cbss.bssid);
 			break;
 		default:
-			WARN(1, "unexpected: %d", wk->tries);
+			WARN(1, "unexpected: %lu", wk->timeout);
 		}
 
 		list_del(&wk->list);
@@ -2479,35 +2471,18 @@ int ieee80211_mgd_assoc(struct ieee80211
 			struct cfg80211_assoc_request *req)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_mgd_work *wk, *found = NULL;
+	struct ieee80211_mgd_work *wk;
+	const u8 *ssid;
 	int i, err;
 
 	mutex_lock(&ifmgd->mtx);
 
-	list_for_each_entry(wk, &ifmgd->work_list, list) {
-		if (&wk->bss->cbss == req->bss &&
-		    wk->state == IEEE80211_MGD_STATE_IDLE) {
-			found = wk;
-			break;
-		}
-	}
-
-	if (!found) {
-		err = -ENOLINK;
-		goto out;
-	}
-
-	list_del(&found->list);
-
-	wk = krealloc(found, sizeof(*wk) + req->ie_len, GFP_KERNEL);
+	wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
 	if (!wk) {
-		list_add(&found->list, &ifmgd->work_list);
 		err = -ENOMEM;
 		goto out;
 	}
 
-	list_add(&wk->list, &ifmgd->work_list);
-
 	ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
 
 	for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
@@ -2516,8 +2491,6 @@ int ieee80211_mgd_assoc(struct ieee80211
 		    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104)
 			ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
 
-	sdata->local->oper_channel = req->bss->channel;
-	ieee80211_hw_config(sdata->local, 0);
 
 	if (req->ie && req->ie_len) {
 		memcpy(wk->ie, req->ie, req->ie_len);
@@ -2525,11 +2498,16 @@ int ieee80211_mgd_assoc(struct ieee80211
 	} else
 		wk->ie_len = 0;
 
+	wk->bss = (void *)req->bss;
+
+	ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
+	memcpy(wk->ssid, ssid + 2, ssid[1]);
+	wk->ssid_len = ssid[1];
+
 	if (req->prev_bssid)
 		memcpy(wk->prev_bssid, req->prev_bssid, ETH_ALEN);
 
 	wk->state = IEEE80211_MGD_STATE_ASSOC;
-	wk->tries = 0;
 	wk->timeout = jiffies; /* run right away */
 
 	if (req->use_mfp) {
@@ -2545,6 +2523,10 @@ int ieee80211_mgd_assoc(struct ieee80211
 	else
 		ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT;
 
+	sdata->local->oper_channel = req->bss->channel;
+	ieee80211_hw_config(sdata->local, 0);
+
+	list_add(&wk->list, &ifmgd->work_list);
 	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work);
 
 	err = 0;
@@ -2560,23 +2542,23 @@ int ieee80211_mgd_deauth(struct ieee8021
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_mgd_work *wk;
-	const u8 *bssid = NULL;
+	const u8 *bssid = req->bss->bssid;
 	bool not_auth_yet = false;
 
 	mutex_lock(&ifmgd->mtx);
 
 	if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) {
 		bssid = req->bss->bssid;
-		ieee80211_set_disassoc(sdata, true);
+		ieee80211_set_disassoc(sdata);
 	} else list_for_each_entry(wk, &ifmgd->work_list, list) {
-		if (&wk->bss->cbss == req->bss) {
-			bssid = req->bss->bssid;
-			if (wk->state == IEEE80211_MGD_STATE_PROBE)
-				not_auth_yet = true;
-			list_del(&wk->list);
-			kfree(wk);
-			break;
-		}
+		if (wk->state != IEEE80211_MGD_STATE_PROBE)
+			continue;
+		if (req->bss != &wk->bss->cbss)
+			continue;
+		not_auth_yet = true;
+		list_del(&wk->list);
+		kfree(wk);
+		break;
 	}
 
 	/*
@@ -2593,17 +2575,6 @@ int ieee80211_mgd_deauth(struct ieee8021
 		return 0;
 	}
 
-	/*
-	 * cfg80211 should catch this ... but it's racy since
-	 * we can receive a deauth frame, process it, hand it
-	 * to cfg80211 while that's in a locked section already
-	 * trying to tell us that the user wants to disconnect.
-	 */
-	if (!bssid) {
-		mutex_unlock(&ifmgd->mtx);
-		return -ENOLINK;
-	}
-
 	mutex_unlock(&ifmgd->mtx);
 
 	printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
@@ -2640,7 +2611,7 @@ int ieee80211_mgd_disassoc(struct ieee80
 	printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n",
 	       sdata->name, req->bss->bssid, req->reason_code);
 
-	ieee80211_set_disassoc(sdata, false);
+	ieee80211_set_disassoc(sdata);
 
 	mutex_unlock(&ifmgd->mtx);
 
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2009-12-01 18:26:50.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h	2009-12-01 18:26:59.000000000 +0100
@@ -228,7 +228,7 @@ struct mesh_preq_queue {
 };
 
 enum ieee80211_mgd_state {
-	IEEE80211_MGD_STATE_IDLE,
+	IEEE80211_MGD_STATE_INVALID,
 	IEEE80211_MGD_STATE_PROBE,
 	IEEE80211_MGD_STATE_AUTH,
 	IEEE80211_MGD_STATE_ASSOC,
@@ -285,7 +285,6 @@ struct ieee80211_if_managed {
 
 	struct mutex mtx;
 	struct ieee80211_bss *associated;
-	struct ieee80211_mgd_work *old_associate_work;
 	struct list_head work_list;
 
 	u8 bssid[ETH_ALEN];



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 4/6] mac80211: generalise management work a bit
  2009-12-02 11:43 [PATCH 0/6] mac80211: mlme work improvements Johannes Berg
                   ` (2 preceding siblings ...)
  2009-12-02 11:43 ` [PATCH 3/6] mac80211: let cfg80211 manage auth state Johannes Berg
@ 2009-12-02 11:43 ` Johannes Berg
  2009-12-02 11:43 ` [PATCH 5/6] mac80211: generalise work handling Johannes Berg
  2009-12-02 11:43 ` [PATCH 6/6] mac80211: rewrite a few work messages Johannes Berg
  5 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2009-12-02 11:43 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless

As a first step of generalising management work,
this renames a few things and puts more information
directly into the struct so that auth/assoc need
not access the BSS pointer as often -- in fact it
can be removed from auth completely. Also since the
previous patch made sure a new work item is used
for association, we can make the different data a
union.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/ieee80211_i.h |   53 ++++++----
 net/mac80211/mlme.c        |  226 +++++++++++++++++++++++++++------------------
 2 files changed, 172 insertions(+), 107 deletions(-)

--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2009-12-02 11:22:22.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h	2009-12-02 11:23:57.000000000 +0100
@@ -227,31 +227,48 @@ struct mesh_preq_queue {
 	u8 flags;
 };
 
-enum ieee80211_mgd_state {
-	IEEE80211_MGD_STATE_INVALID,
-	IEEE80211_MGD_STATE_PROBE,
-	IEEE80211_MGD_STATE_AUTH,
-	IEEE80211_MGD_STATE_ASSOC,
+enum ieee80211_work_type {
+	IEEE80211_WORK_AUTH_PROBE,
+	IEEE80211_WORK_AUTH,
+	IEEE80211_WORK_ASSOC,
 };
 
-struct ieee80211_mgd_work {
+struct ieee80211_work {
 	struct list_head list;
-	struct ieee80211_bss *bss;
-	int ie_len;
-	u8 prev_bssid[ETH_ALEN];
-	u8 ssid[IEEE80211_MAX_SSID_LEN];
-	u8 ssid_len;
-	unsigned long timeout;
-	enum ieee80211_mgd_state state;
-	u16 auth_alg, auth_transaction;
 
-	int tries;
+	struct ieee80211_channel *chan;
+	/* XXX: chan type? -- right now not really needed */
+	unsigned long timeout;
+	enum ieee80211_work_type type;
 
-	u8 key[WLAN_KEY_LEN_WEP104];
-	u8 key_len, key_idx;
+	union {
+		struct {
+			int tries;
+			u16 algorithm, transaction;
+			u8 ssid[IEEE80211_MAX_SSID_LEN];
+			u8 ssid_len;
+			u8 bssid[ETH_ALEN];
+			u8 key[WLAN_KEY_LEN_WEP104];
+			u8 key_len, key_idx;
+			bool privacy;
+		} auth;
+		struct {
+			struct ieee80211_bss *bss;
+			const u8 *supp_rates;
+			const u8 *ht_information_ie;
+			int tries;
+			u16 capability;
+			u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
+			u8 ssid[IEEE80211_MAX_SSID_LEN];
+			u8 ssid_len;
+			u8 supp_rates_len;
+			bool wmm_used;
+		} assoc;
+	};
 
+	int ie_len;
 	/* must be last */
-	u8 ie[0]; /* for auth or assoc frame, not probe */
+	u8 ie[0];
 };
 
 /* flags used in struct ieee80211_if_managed.flags */
--- wireless-testing.orig/net/mac80211/mlme.c	2009-12-02 11:22:22.000000000 +0100
+++ wireless-testing/net/mac80211/mlme.c	2009-12-02 11:22:34.000000000 +0100
@@ -125,15 +125,15 @@ static int ecw2cw(int ecw)
 	return (1 << ecw) - 1;
 }
 
-static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
+static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
 				      struct ieee80211_supported_band *sband,
 				      u32 *rates)
 {
 	int i, j, count;
 	*rates = 0;
 	count = 0;
-	for (i = 0; i < bss->supp_rates_len; i++) {
-		int rate = (bss->supp_rates[i] & 0x7F) * 5;
+	for (i = 0; i < supp_rates_len; i++) {
+		int rate = (supp_rates[i] & 0x7F) * 5;
 
 		for (j = 0; j < sband->n_bitrates; j++)
 			if (sband->bitrates[j].bitrate == rate) {
@@ -232,7 +232,7 @@ static u32 ieee80211_enable_ht(struct ie
 /* frame sending functions */
 
 static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
-				 struct ieee80211_mgd_work *wk)
+				 struct ieee80211_work *wk)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_local *local = sdata->local;
@@ -248,7 +248,7 @@ static void ieee80211_send_assoc(struct 
 
 	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
 			    sizeof(*mgmt) + 200 + wk->ie_len +
-			    wk->ssid_len);
+			    wk->assoc.ssid_len);
 	if (!skb) {
 		printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
 		       "frame\n", sdata->name);
@@ -267,35 +267,37 @@ static void ieee80211_send_assoc(struct 
 			capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
 	}
 
-	if (wk->bss->cbss.capability & WLAN_CAPABILITY_PRIVACY)
+	if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY)
 		capab |= WLAN_CAPABILITY_PRIVACY;
-	if (wk->bss->wmm_used)
+	if (wk->assoc.wmm_used)
 		wmm = 1;
 
 	/* get all rates supported by the device and the AP as
 	 * some APs don't like getting a superset of their rates
 	 * in the association request (e.g. D-Link DAP 1353 in
 	 * b-only mode) */
-	rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates);
+	rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
+					       wk->assoc.supp_rates_len,
+					       sband, &rates);
 
-	if ((wk->bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
+	if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
 	    (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
 		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
 
 	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
 	memset(mgmt, 0, 24);
-	memcpy(mgmt->da, wk->bss->cbss.bssid, ETH_ALEN);
+	memcpy(mgmt->da, wk->assoc.bssid, ETH_ALEN);
 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-	memcpy(mgmt->bssid, wk->bss->cbss.bssid, ETH_ALEN);
+	memcpy(mgmt->bssid, wk->assoc.bssid, ETH_ALEN);
 
-	if (!is_zero_ether_addr(wk->prev_bssid)) {
+	if (!is_zero_ether_addr(wk->assoc.prev_bssid)) {
 		skb_put(skb, 10);
 		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
 						  IEEE80211_STYPE_REASSOC_REQ);
 		mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
 		mgmt->u.reassoc_req.listen_interval =
 				cpu_to_le16(local->hw.conf.listen_interval);
-		memcpy(mgmt->u.reassoc_req.current_ap, wk->prev_bssid,
+		memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid,
 		       ETH_ALEN);
 	} else {
 		skb_put(skb, 4);
@@ -307,10 +309,10 @@ static void ieee80211_send_assoc(struct 
 	}
 
 	/* SSID */
-	ies = pos = skb_put(skb, 2 + wk->ssid_len);
+	ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len);
 	*pos++ = WLAN_EID_SSID;
-	*pos++ = wk->ssid_len;
-	memcpy(pos, wk->ssid, wk->ssid_len);
+	*pos++ = wk->assoc.ssid_len;
+	memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len);
 
 	/* add all rates which were marked to be used above */
 	supp_rates_len = rates_len;
@@ -392,7 +394,7 @@ static void ieee80211_send_assoc(struct 
 	 */
 	if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
 	    sband->ht_cap.ht_supported &&
-	    (ht_ie = ieee80211_bss_get_ie(&wk->bss->cbss, WLAN_EID_HT_INFORMATION)) &&
+	    (ht_ie = wk->assoc.ht_information_ie) &&
 	    ht_ie[1] >= sizeof(struct ieee80211_ht_info) &&
 	    (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) {
 		struct ieee80211_ht_info *ht_info =
@@ -995,23 +997,43 @@ static void ieee80211_set_associated(str
 	netif_carrier_on(sdata->dev);
 }
 
+static void ieee80211_remove_auth_bss(struct ieee80211_local *local,
+				      struct ieee80211_work *wk)
+{
+	struct cfg80211_bss *cbss;
+	u16 capa_val = WLAN_CAPABILITY_ESS;
+
+	if (wk->auth.privacy)
+		capa_val |= WLAN_CAPABILITY_PRIVACY;
+
+	cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->auth.bssid,
+				wk->auth.ssid, wk->auth.ssid_len,
+				WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
+				capa_val);
+	if (!cbss)
+		return;
+
+	cfg80211_unlink_bss(local->hw.wiphy, cbss);
+	cfg80211_put_bss(cbss);
+}
+
 static enum rx_mgmt_action __must_check
 ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
-		       struct ieee80211_mgd_work *wk)
+		       struct ieee80211_work *wk)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_local *local = sdata->local;
 
-	wk->tries++;
-	if (wk->tries > IEEE80211_AUTH_MAX_TRIES) {
+	wk->auth.tries++;
+	if (wk->auth.tries > IEEE80211_AUTH_MAX_TRIES) {
 		printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
-		       sdata->name, wk->bss->cbss.bssid);
+		       sdata->name, wk->auth.bssid);
 
 		/*
 		 * Most likely AP is not in the range so remove the
 		 * bss struct for that AP.
 		 */
-		cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
+		ieee80211_remove_auth_bss(local, wk);
 
 		/*
 		 * We might have a pending scan which had no chance to run yet
@@ -1023,14 +1045,14 @@ ieee80211_direct_probe(struct ieee80211_
 	}
 
 	printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
-			sdata->name, wk->bss->cbss.bssid,
-			wk->tries);
+			sdata->name, wk->auth.bssid, wk->auth.tries);
 
 	/*
 	 * Direct probe is sent to broadcast address as some APs
 	 * will not answer to direct packet in unassociated state.
 	 */
-	ieee80211_send_probe_req(sdata, NULL, wk->ssid, wk->ssid_len, NULL, 0);
+	ieee80211_send_probe_req(sdata, NULL, wk->auth.ssid, wk->auth.ssid_len,
+				 NULL, 0);
 
 	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
 	run_again(ifmgd, wk->timeout);
@@ -1041,22 +1063,21 @@ ieee80211_direct_probe(struct ieee80211_
 
 static enum rx_mgmt_action __must_check
 ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
-		       struct ieee80211_mgd_work *wk)
+		       struct ieee80211_work *wk)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_local *local = sdata->local;
 
-	wk->tries++;
-	if (wk->tries > IEEE80211_AUTH_MAX_TRIES) {
+	wk->auth.tries++;
+	if (wk->auth.tries > IEEE80211_AUTH_MAX_TRIES) {
 		printk(KERN_DEBUG "%s: authentication with AP %pM"
-		       " timed out\n",
-		       sdata->name, wk->bss->cbss.bssid);
+		       " timed out\n", sdata->name, wk->auth.bssid);
 
 		/*
 		 * Most likely AP is not in the range so remove the
 		 * bss struct for that AP.
 		 */
-		cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
+		ieee80211_remove_auth_bss(local, wk);
 
 		/*
 		 * We might have a pending scan which had no chance to run yet
@@ -1068,11 +1089,11 @@ ieee80211_authenticate(struct ieee80211_
 	}
 
 	printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
-	       sdata->name, wk->bss->cbss.bssid, wk->tries);
+	       sdata->name, wk->auth.bssid, wk->auth.tries);
 
-	ieee80211_send_auth(sdata, 1, wk->auth_alg, wk->ie, wk->ie_len,
-			    wk->bss->cbss.bssid, NULL, 0, 0);
-	wk->auth_transaction = 2;
+	ieee80211_send_auth(sdata, 1, wk->auth.algorithm, wk->ie, wk->ie_len,
+			    wk->auth.bssid, NULL, 0, 0);
+	wk->auth.transaction = 2;
 
 	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
 	run_again(ifmgd, wk->timeout);
@@ -1168,22 +1189,22 @@ static void ieee80211_set_disassoc(struc
 
 static enum rx_mgmt_action __must_check
 ieee80211_associate(struct ieee80211_sub_if_data *sdata,
-		    struct ieee80211_mgd_work *wk)
+		    struct ieee80211_work *wk)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_local *local = sdata->local;
 
-	wk->tries++;
-	if (wk->tries > IEEE80211_ASSOC_MAX_TRIES) {
+	wk->assoc.tries++;
+	if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) {
 		printk(KERN_DEBUG "%s: association with AP %pM"
 		       " timed out\n",
-		       sdata->name, wk->bss->cbss.bssid);
+		       sdata->name, wk->assoc.bssid);
 
 		/*
 		 * Most likely AP is not in the range so remove the
 		 * bss struct for that AP.
 		 */
-		cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
+		cfg80211_unlink_bss(local->hw.wiphy, &wk->assoc.bss->cbss);
 
 		/*
 		 * We might have a pending scan which had no chance to run yet
@@ -1195,7 +1216,7 @@ ieee80211_associate(struct ieee80211_sub
 	}
 
 	printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
-	       sdata->name, wk->bss->cbss.bssid, wk->tries);
+	       sdata->name, wk->assoc.bssid, wk->assoc.tries);
 	ieee80211_send_assoc(sdata, wk);
 
 	wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
@@ -1310,7 +1331,7 @@ void ieee80211_beacon_loss(struct ieee80
 EXPORT_SYMBOL(ieee80211_beacon_loss);
 
 static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
-				     struct ieee80211_mgd_work *wk)
+				     struct ieee80211_work *wk)
 {
 	list_del(&wk->list);
 	kfree(wk);
@@ -1319,7 +1340,7 @@ static void ieee80211_auth_completed(str
 
 
 static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
-				     struct ieee80211_mgd_work *wk,
+				     struct ieee80211_work *wk,
 				     struct ieee80211_mgmt *mgmt,
 				     size_t len)
 {
@@ -1330,38 +1351,38 @@ static void ieee80211_auth_challenge(str
 	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
 	if (!elems.challenge)
 		return;
-	ieee80211_send_auth(sdata, 3, wk->auth_alg,
+	ieee80211_send_auth(sdata, 3, wk->auth.algorithm,
 			    elems.challenge - 2, elems.challenge_len + 2,
-			    wk->bss->cbss.bssid,
-			    wk->key, wk->key_len, wk->key_idx);
-	wk->auth_transaction = 4;
+			    wk->auth.bssid, wk->auth.key, wk->auth.key_len,
+			    wk->auth.key_idx);
+	wk->auth.transaction = 4;
 }
 
 static enum rx_mgmt_action __must_check
 ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
-		       struct ieee80211_mgd_work *wk,
+		       struct ieee80211_work *wk,
 		       struct ieee80211_mgmt *mgmt, size_t len)
 {
 	u16 auth_alg, auth_transaction, status_code;
 
-	if (wk->state != IEEE80211_MGD_STATE_AUTH)
+	if (wk->type != IEEE80211_WORK_AUTH)
 		return RX_MGMT_NONE;
 
 	if (len < 24 + 6)
 		return RX_MGMT_NONE;
 
-	if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
+	if (memcmp(wk->auth.bssid, mgmt->sa, ETH_ALEN) != 0)
 		return RX_MGMT_NONE;
 
-	if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0)
+	if (memcmp(wk->auth.bssid, mgmt->bssid, ETH_ALEN) != 0)
 		return RX_MGMT_NONE;
 
 	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
 	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
 	status_code = le16_to_cpu(mgmt->u.auth.status_code);
 
-	if (auth_alg != wk->auth_alg ||
-	    auth_transaction != wk->auth_transaction)
+	if (auth_alg != wk->auth.algorithm ||
+	    auth_transaction != wk->auth.transaction)
 		return RX_MGMT_NONE;
 
 	if (status_code != WLAN_STATUS_SUCCESS) {
@@ -1370,14 +1391,14 @@ ieee80211_rx_mgmt_auth(struct ieee80211_
 		return RX_MGMT_CFG80211_AUTH;
 	}
 
-	switch (wk->auth_alg) {
+	switch (wk->auth.algorithm) {
 	case WLAN_AUTH_OPEN:
 	case WLAN_AUTH_LEAP:
 	case WLAN_AUTH_FT:
 		ieee80211_auth_completed(sdata, wk);
 		return RX_MGMT_CFG80211_AUTH;
 	case WLAN_AUTH_SHARED_KEY:
-		if (wk->auth_transaction == 4) {
+		if (wk->auth.transaction == 4) {
 			ieee80211_auth_completed(sdata, wk);
 			return RX_MGMT_CFG80211_AUTH;
 		} else
@@ -1447,7 +1468,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80
 
 static enum rx_mgmt_action __must_check
 ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
-			     struct ieee80211_mgd_work *wk,
+			     struct ieee80211_work *wk,
 			     struct ieee80211_mgmt *mgmt, size_t len,
 			     bool reassoc)
 {
@@ -1455,7 +1476,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_supported_band *sband;
 	struct sta_info *sta;
-	struct ieee80211_bss *bss = wk->bss;
+	struct ieee80211_bss *bss = wk->assoc.bss;
 	u32 rates, basic_rates;
 	u16 capab_info, status_code, aid;
 	struct ieee802_11_elems elems;
@@ -1685,7 +1706,7 @@ static void ieee80211_rx_bss_info(struct
 
 
 static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
-					 struct ieee80211_mgd_work *wk,
+					 struct ieee80211_work *wk,
 					 struct ieee80211_mgmt *mgmt, size_t len,
 					 struct ieee80211_rx_status *rx_status)
 {
@@ -1710,11 +1731,11 @@ static void ieee80211_rx_mgmt_probe_resp
 	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
 
 	/* direct probe may be part of the association flow */
-	if (wk && wk->state == IEEE80211_MGD_STATE_PROBE) {
+	if (wk && wk->type == IEEE80211_WORK_AUTH_PROBE) {
 		printk(KERN_DEBUG "%s: direct probe responded\n",
 		       sdata->name);
-		wk->tries = 0;
-		wk->state = IEEE80211_MGD_STATE_AUTH;
+		wk->auth.tries = 0;
+		wk->type = IEEE80211_WORK_AUTH;
 		WARN_ON(ieee80211_authenticate(sdata, wk) != RX_MGMT_NONE);
 	}
 
@@ -1951,7 +1972,7 @@ static void ieee80211_sta_rx_queued_mgmt
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_mgmt *mgmt;
-	struct ieee80211_mgd_work *wk;
+	struct ieee80211_work *wk;
 	enum rx_mgmt_action rma = RX_MGMT_NONE;
 	u16 fc;
 
@@ -2005,7 +2026,20 @@ static void ieee80211_sta_rx_queued_mgmt
 	}
 
 	list_for_each_entry(wk, &ifmgd->work_list, list) {
-		if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0)
+		const u8 *bssid = NULL;
+
+		switch (wk->type) {
+		case IEEE80211_WORK_AUTH_PROBE:
+		case IEEE80211_WORK_AUTH:
+			bssid = wk->auth.bssid;
+			break;
+		case IEEE80211_WORK_ASSOC:
+			bssid = wk->assoc.bssid;
+			break;
+		default:
+			continue;
+		}
+		if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
 			continue;
 
 		switch (fc & IEEE80211_FCTL_STYPE) {
@@ -2100,7 +2134,7 @@ static void ieee80211_sta_work(struct wo
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd;
 	struct sk_buff *skb;
-	struct ieee80211_mgd_work *wk, *tmp;
+	struct ieee80211_work *wk, *tmp;
 	LIST_HEAD(free_work);
 	enum rx_mgmt_action rma;
 
@@ -2186,19 +2220,19 @@ static void ieee80211_sta_work(struct wo
 			continue;
 		}
 
-		switch (wk->state) {
+		switch (wk->type) {
 		default:
 			WARN_ON(1);
 			/* nothing */
 			rma = RX_MGMT_NONE;
 			break;
-		case IEEE80211_MGD_STATE_PROBE:
+		case IEEE80211_WORK_AUTH_PROBE:
 			rma = ieee80211_direct_probe(sdata, wk);
 			break;
-		case IEEE80211_MGD_STATE_AUTH:
+		case IEEE80211_WORK_AUTH:
 			rma = ieee80211_authenticate(sdata, wk);
 			break;
-		case IEEE80211_MGD_STATE_ASSOC:
+		case IEEE80211_WORK_ASSOC:
 			rma = ieee80211_associate(sdata, wk);
 			break;
 		}
@@ -2235,12 +2269,11 @@ static void ieee80211_sta_work(struct wo
 		/* see above how we're using wk->timeout */
 		switch (wk->timeout) {
 		case RX_MGMT_CFG80211_AUTH_TO:
-			cfg80211_send_auth_timeout(sdata->dev,
-						   wk->bss->cbss.bssid);
+			cfg80211_send_auth_timeout(sdata->dev, wk->auth.bssid);
 			break;
 		case RX_MGMT_CFG80211_ASSOC_TO:
 			cfg80211_send_assoc_timeout(sdata->dev,
-						    wk->bss->cbss.bssid);
+						    wk->assoc.bssid);
 			break;
 		default:
 			WARN(1, "unexpected: %lu", wk->timeout);
@@ -2407,7 +2440,7 @@ int ieee80211_mgd_auth(struct ieee80211_
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	const u8 *ssid;
-	struct ieee80211_mgd_work *wk;
+	struct ieee80211_work *wk;
 	u16 auth_alg;
 
 	switch (req->auth_type) {
@@ -2431,7 +2464,7 @@ int ieee80211_mgd_auth(struct ieee80211_
 	if (!wk)
 		return -ENOMEM;
 
-	wk->bss = (void *)req->bss;
+	memcpy(wk->auth.bssid, req->bss->bssid, ETH_ALEN);;
 
 	if (req->ie && req->ie_len) {
 		memcpy(wk->ie, req->ie, req->ie_len);
@@ -2439,22 +2472,27 @@ int ieee80211_mgd_auth(struct ieee80211_
 	}
 
 	if (req->key && req->key_len) {
-		wk->key_len = req->key_len;
-		wk->key_idx = req->key_idx;
-		memcpy(wk->key, req->key, req->key_len);
+		wk->auth.key_len = req->key_len;
+		wk->auth.key_idx = req->key_idx;
+		memcpy(wk->auth.key, req->key, req->key_len);
 	}
 
 	ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
-	memcpy(wk->ssid, ssid + 2, ssid[1]);
-	wk->ssid_len = ssid[1];
+	memcpy(wk->auth.ssid, ssid + 2, ssid[1]);
+	wk->auth.ssid_len = ssid[1];
+
+	wk->auth.algorithm = auth_alg;
+	wk->auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
 
-	wk->state = IEEE80211_MGD_STATE_PROBE;
-	wk->auth_alg = auth_alg;
+	wk->type = IEEE80211_WORK_AUTH_PROBE;
 	wk->timeout = jiffies; /* run right away */
+	wk->chan = req->bss->channel;
 
 	/*
 	 * XXX: if still associated need to tell AP that we're going
 	 *	to sleep and then change channel etc.
+	 *	For now switch channel here, later will be handled
+	 *	by submitting this as an off-channel work item.
 	 */
 	sdata->local->oper_channel = req->bss->channel;
 	ieee80211_hw_config(sdata->local, 0);
@@ -2471,7 +2509,7 @@ int ieee80211_mgd_assoc(struct ieee80211
 			struct cfg80211_assoc_request *req)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_mgd_work *wk;
+	struct ieee80211_work *wk;
 	const u8 *ssid;
 	int i, err;
 
@@ -2498,17 +2536,27 @@ int ieee80211_mgd_assoc(struct ieee80211
 	} else
 		wk->ie_len = 0;
 
-	wk->bss = (void *)req->bss;
+	wk->assoc.bss = (void *)req->bss;
+
+	memcpy(wk->assoc.bssid, req->bss->bssid, ETH_ALEN);
+
+	wk->assoc.capability = req->bss->capability;
+	wk->assoc.wmm_used = wk->assoc.bss->wmm_used;
+	wk->assoc.supp_rates = wk->assoc.bss->supp_rates;
+	wk->assoc.supp_rates_len = wk->assoc.bss->supp_rates_len;
+	wk->assoc.ht_information_ie =
+		ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION);
 
 	ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
-	memcpy(wk->ssid, ssid + 2, ssid[1]);
-	wk->ssid_len = ssid[1];
+	memcpy(wk->assoc.ssid, ssid + 2, ssid[1]);
+	wk->assoc.ssid_len = ssid[1];
 
 	if (req->prev_bssid)
-		memcpy(wk->prev_bssid, req->prev_bssid, ETH_ALEN);
+		memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN);
 
-	wk->state = IEEE80211_MGD_STATE_ASSOC;
+	wk->type = IEEE80211_WORK_ASSOC;
 	wk->timeout = jiffies; /* run right away */
+	wk->chan = req->bss->channel;
 
 	if (req->use_mfp) {
 		ifmgd->mfp = IEEE80211_MFP_REQUIRED;
@@ -2541,7 +2589,7 @@ int ieee80211_mgd_deauth(struct ieee8021
 			 void *cookie)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_mgd_work *wk;
+	struct ieee80211_work *wk;
 	const u8 *bssid = req->bss->bssid;
 	bool not_auth_yet = false;
 
@@ -2551,9 +2599,9 @@ int ieee80211_mgd_deauth(struct ieee8021
 		bssid = req->bss->bssid;
 		ieee80211_set_disassoc(sdata);
 	} else list_for_each_entry(wk, &ifmgd->work_list, list) {
-		if (wk->state != IEEE80211_MGD_STATE_PROBE)
+		if (wk->type != IEEE80211_WORK_AUTH_PROBE)
 			continue;
-		if (req->bss != &wk->bss->cbss)
+		if (memcmp(req->bss->bssid, wk->auth.bssid, ETH_ALEN))
 			continue;
 		not_auth_yet = true;
 		list_del(&wk->list);



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 5/6] mac80211: generalise work handling
  2009-12-02 11:43 [PATCH 0/6] mac80211: mlme work improvements Johannes Berg
                   ` (3 preceding siblings ...)
  2009-12-02 11:43 ` [PATCH 4/6] mac80211: generalise management work a bit Johannes Berg
@ 2009-12-02 11:43 ` Johannes Berg
  2009-12-07 14:53   ` [PATCH 5/6 v2] " Johannes Berg
  2009-12-02 11:43 ` [PATCH 6/6] mac80211: rewrite a few work messages Johannes Berg
  5 siblings, 1 reply; 8+ messages in thread
From: Johannes Berg @ 2009-12-02 11:43 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless

In order to use auth/assoc for different purposes
other than MLME, it needs to be split up. For other
purposes, a generic work handling (potentially on
another channel) will be useful.

To achieve that, this patch moves much of the MLME
work handling out of mlme into a new work API. The
API can currently handle probing a specific AP,
authentication and association. The MLME previously
handled probe/authentication as one step and will
continue to do so, but they are separate in the new
work handling.

Work items are RCU-managed to be able to check for
existence of an item for a specific frame in the RX
path, but they can be re-used which the MLME right
now will do for its combined probe/auth step.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/Makefile         |    2 
 net/mac80211/debugfs_netdev.c |    2 
 net/mac80211/ieee80211_i.h    |   58 +-
 net/mac80211/iface.c          |   11 
 net/mac80211/main.c           |    2 
 net/mac80211/mlme.c           |  924 +++++-------------------------------------
 net/mac80211/rx.c             |    5 
 net/mac80211/scan.c           |    8 
 net/mac80211/work.c           |  902 +++++++++++++++++++++++++++++++++++++++++
 9 files changed, 1094 insertions(+), 820 deletions(-)

--- wireless-testing.orig/net/mac80211/Makefile	2009-12-01 18:23:13.000000000 +0100
+++ wireless-testing/net/mac80211/Makefile	2009-12-01 21:19:53.000000000 +0100
@@ -9,7 +9,7 @@ mac80211-y := \
 	scan.o \
 	ht.o agg-tx.o agg-rx.o \
 	ibss.o \
-	mlme.o \
+	mlme.o work.o \
 	iface.o \
 	rate.o \
 	michael.o \
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2009-12-01 18:27:00.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h	2009-12-01 21:19:53.000000000 +0100
@@ -228,41 +228,63 @@ struct mesh_preq_queue {
 };
 
 enum ieee80211_work_type {
-	IEEE80211_WORK_AUTH_PROBE,
+	IEEE80211_WORK_DIRECT_PROBE,
 	IEEE80211_WORK_AUTH,
 	IEEE80211_WORK_ASSOC,
 };
 
+/**
+ * enum work_done_result - indicates what to do after work was done
+ *
+ * @WORK_DONE_DESTROY: This work item is no longer needed, destroy.
+ * @WORK_DONE_REQUEUE: This work item was reset to be reused, and
+ *	should be requeued.
+ */
+enum work_done_result {
+	WORK_DONE_DESTROY,
+	WORK_DONE_REQUEUE,
+};
+
 struct ieee80211_work {
 	struct list_head list;
 
+	struct rcu_head rcu_head;
+
+	struct ieee80211_sub_if_data *sdata;
+
+	enum work_done_result (*done)(struct ieee80211_work *wk,
+				      struct sk_buff *skb);
+
 	struct ieee80211_channel *chan;
 	/* XXX: chan type? -- right now not really needed */
+
 	unsigned long timeout;
 	enum ieee80211_work_type type;
 
+	u8 filter_ta[ETH_ALEN];
+
 	union {
 		struct {
 			int tries;
 			u16 algorithm, transaction;
 			u8 ssid[IEEE80211_MAX_SSID_LEN];
 			u8 ssid_len;
-			u8 bssid[ETH_ALEN];
 			u8 key[WLAN_KEY_LEN_WEP104];
 			u8 key_len, key_idx;
 			bool privacy;
-		} auth;
+		} probe_auth;
 		struct {
 			struct ieee80211_bss *bss;
 			const u8 *supp_rates;
 			const u8 *ht_information_ie;
+			enum ieee80211_smps_mode smps;
 			int tries;
 			u16 capability;
-			u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
+			u8 prev_bssid[ETH_ALEN];
 			u8 ssid[IEEE80211_MAX_SSID_LEN];
 			u8 ssid_len;
 			u8 supp_rates_len;
-			bool wmm_used;
+			bool wmm_used, use_11n;
 		} assoc;
 	};
 
@@ -276,17 +298,11 @@ enum ieee80211_sta_flags {
 	IEEE80211_STA_BEACON_POLL	= BIT(0),
 	IEEE80211_STA_CONNECTION_POLL	= BIT(1),
 	IEEE80211_STA_CONTROL_PORT	= BIT(2),
-	IEEE80211_STA_WMM_ENABLED	= BIT(3),
 	IEEE80211_STA_DISABLE_11N	= BIT(4),
 	IEEE80211_STA_CSA_RECEIVED	= BIT(5),
 	IEEE80211_STA_MFP_ENABLED	= BIT(6),
 };
 
-/* flags for MLME request */
-enum ieee80211_sta_request {
-	IEEE80211_STA_REQ_SCAN,
-};
-
 struct ieee80211_if_managed {
 	struct timer_list timer;
 	struct timer_list conn_mon_timer;
@@ -302,12 +318,10 @@ struct ieee80211_if_managed {
 
 	struct mutex mtx;
 	struct ieee80211_bss *associated;
-	struct list_head work_list;
 
 	u8 bssid[ETH_ALEN];
 
 	u16 aid;
-	u16 capab;
 
 	struct sk_buff_head skb_queue;
 
@@ -316,8 +330,6 @@ struct ieee80211_if_managed {
 	enum ieee80211_smps_mode req_smps, /* requested smps mode */
 				 ap_smps; /* smps mode AP thinks we're in */
 
-	unsigned long request;
-
 	unsigned int flags;
 
 	u32 beacon_crc;
@@ -584,6 +596,15 @@ struct ieee80211_local {
 	const struct ieee80211_ops *ops;
 
 	/*
+	 * work stuff, potentially off-channel (in the future)
+	 */
+	struct mutex work_mtx;
+	struct list_head work_list;
+	struct timer_list work_timer;
+	struct work_struct work_work;
+	struct sk_buff_head work_skb_queue;
+
+	/*
 	 * private workqueue to mac80211. mac80211 makes this accessible
 	 * via ieee80211_queue_work()
 	 */
@@ -1126,6 +1147,13 @@ int __ieee80211_request_smps(struct ieee
 void ieee80211_recalc_smps(struct ieee80211_local *local,
 			   struct ieee80211_sub_if_data *forsdata);
 
+/* internal work items */
+void ieee80211_work_init(struct ieee80211_local *local);
+void ieee80211_add_work(struct ieee80211_work *wk);
+void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata);
+ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
+					   struct sk_buff *skb);
+
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
 #else
--- wireless-testing.orig/net/mac80211/iface.c	2009-12-01 18:23:13.000000000 +0100
+++ wireless-testing/net/mac80211/iface.c	2009-12-01 21:19:53.000000000 +0100
@@ -362,6 +362,11 @@ static int ieee80211_stop(struct net_dev
 	netif_stop_queue(dev);
 
 	/*
+	 * Purge work for this interface.
+	 */
+	ieee80211_work_purge(sdata);
+
+	/*
 	 * Now delete all active aggregation sessions.
 	 */
 	rcu_read_lock();
@@ -937,6 +942,9 @@ u32 __ieee80211_recalc_idle(struct ieee8
 	struct ieee80211_sub_if_data *sdata;
 	int count = 0;
 
+	if (!list_empty(&local->work_list))
+		return ieee80211_idle_off(local, "working");
+
 	if (local->scanning)
 		return ieee80211_idle_off(local, "scanning");
 
@@ -945,8 +953,7 @@ u32 __ieee80211_recalc_idle(struct ieee8
 			continue;
 		/* do not count disabled managed interfaces */
 		if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-		    !sdata->u.mgd.associated &&
-		    list_empty(&sdata->u.mgd.work_list))
+		    !sdata->u.mgd.associated)
 			continue;
 		/* do not count unused IBSS interfaces */
 		if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
--- wireless-testing.orig/net/mac80211/mlme.c	2009-12-01 18:27:00.000000000 +0100
+++ wireless-testing/net/mac80211/mlme.c	2009-12-01 18:27:46.000000000 +0100
@@ -77,12 +77,6 @@ enum rx_mgmt_action {
 
 	/* caller must tell cfg80211 about internal error */
 	RX_MGMT_CFG80211_ASSOC_ERROR,
-
-	/* caller must call cfg80211_auth_timeout() & free work */
-	RX_MGMT_CFG80211_AUTH_TO,
-
-	/* caller must call cfg80211_assoc_timeout() & free work */
-	RX_MGMT_CFG80211_ASSOC_TO,
 };
 
 /* utils */
@@ -125,27 +119,6 @@ static int ecw2cw(int ecw)
 	return (1 << ecw) - 1;
 }
 
-static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
-				      struct ieee80211_supported_band *sband,
-				      u32 *rates)
-{
-	int i, j, count;
-	*rates = 0;
-	count = 0;
-	for (i = 0; i < supp_rates_len; i++) {
-		int rate = (supp_rates[i] & 0x7F) * 5;
-
-		for (j = 0; j < sband->n_bitrates; j++)
-			if (sband->bitrates[j].bitrate == rate) {
-				*rates |= BIT(j);
-				count++;
-				break;
-			}
-	}
-
-	return count;
-}
-
 /*
  * ieee80211_enable_ht should be called only after the operating band
  * has been determined as ht configuration depends on the hw's
@@ -231,266 +204,6 @@ static u32 ieee80211_enable_ht(struct ie
 
 /* frame sending functions */
 
-static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
-				 struct ieee80211_work *wk)
-{
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_local *local = sdata->local;
-	struct sk_buff *skb;
-	struct ieee80211_mgmt *mgmt;
-	u8 *pos;
-	const u8 *ies, *ht_ie;
-	int i, len, count, rates_len, supp_rates_len;
-	u16 capab;
-	int wmm = 0;
-	struct ieee80211_supported_band *sband;
-	u32 rates = 0;
-
-	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-			    sizeof(*mgmt) + 200 + wk->ie_len +
-			    wk->assoc.ssid_len);
-	if (!skb) {
-		printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
-		       "frame\n", sdata->name);
-		return;
-	}
-	skb_reserve(skb, local->hw.extra_tx_headroom);
-
-	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
-	capab = ifmgd->capab;
-
-	if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) {
-		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
-			capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
-		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
-			capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
-	}
-
-	if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY)
-		capab |= WLAN_CAPABILITY_PRIVACY;
-	if (wk->assoc.wmm_used)
-		wmm = 1;
-
-	/* get all rates supported by the device and the AP as
-	 * some APs don't like getting a superset of their rates
-	 * in the association request (e.g. D-Link DAP 1353 in
-	 * b-only mode) */
-	rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
-					       wk->assoc.supp_rates_len,
-					       sband, &rates);
-
-	if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
-	    (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
-		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
-
-	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
-	memset(mgmt, 0, 24);
-	memcpy(mgmt->da, wk->assoc.bssid, ETH_ALEN);
-	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-	memcpy(mgmt->bssid, wk->assoc.bssid, ETH_ALEN);
-
-	if (!is_zero_ether_addr(wk->assoc.prev_bssid)) {
-		skb_put(skb, 10);
-		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-						  IEEE80211_STYPE_REASSOC_REQ);
-		mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
-		mgmt->u.reassoc_req.listen_interval =
-				cpu_to_le16(local->hw.conf.listen_interval);
-		memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid,
-		       ETH_ALEN);
-	} else {
-		skb_put(skb, 4);
-		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-						  IEEE80211_STYPE_ASSOC_REQ);
-		mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
-		mgmt->u.assoc_req.listen_interval =
-				cpu_to_le16(local->hw.conf.listen_interval);
-	}
-
-	/* SSID */
-	ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len);
-	*pos++ = WLAN_EID_SSID;
-	*pos++ = wk->assoc.ssid_len;
-	memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len);
-
-	/* add all rates which were marked to be used above */
-	supp_rates_len = rates_len;
-	if (supp_rates_len > 8)
-		supp_rates_len = 8;
-
-	len = sband->n_bitrates;
-	pos = skb_put(skb, supp_rates_len + 2);
-	*pos++ = WLAN_EID_SUPP_RATES;
-	*pos++ = supp_rates_len;
-
-	count = 0;
-	for (i = 0; i < sband->n_bitrates; i++) {
-		if (BIT(i) & rates) {
-			int rate = sband->bitrates[i].bitrate;
-			*pos++ = (u8) (rate / 5);
-			if (++count == 8)
-				break;
-		}
-	}
-
-	if (rates_len > count) {
-		pos = skb_put(skb, rates_len - count + 2);
-		*pos++ = WLAN_EID_EXT_SUPP_RATES;
-		*pos++ = rates_len - count;
-
-		for (i++; i < sband->n_bitrates; i++) {
-			if (BIT(i) & rates) {
-				int rate = sband->bitrates[i].bitrate;
-				*pos++ = (u8) (rate / 5);
-			}
-		}
-	}
-
-	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
-		/* 1. power capabilities */
-		pos = skb_put(skb, 4);
-		*pos++ = WLAN_EID_PWR_CAPABILITY;
-		*pos++ = 2;
-		*pos++ = 0; /* min tx power */
-		*pos++ = local->hw.conf.channel->max_power; /* max tx power */
-
-		/* 2. supported channels */
-		/* TODO: get this in reg domain format */
-		pos = skb_put(skb, 2 * sband->n_channels + 2);
-		*pos++ = WLAN_EID_SUPPORTED_CHANNELS;
-		*pos++ = 2 * sband->n_channels;
-		for (i = 0; i < sband->n_channels; i++) {
-			*pos++ = ieee80211_frequency_to_channel(
-					sband->channels[i].center_freq);
-			*pos++ = 1; /* one channel in the subband*/
-		}
-	}
-
-	if (wk->ie_len && wk->ie) {
-		pos = skb_put(skb, wk->ie_len);
-		memcpy(pos, wk->ie, wk->ie_len);
-	}
-
-	if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) {
-		pos = skb_put(skb, 9);
-		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-		*pos++ = 7; /* len */
-		*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
-		*pos++ = 0x50;
-		*pos++ = 0xf2;
-		*pos++ = 2; /* WME */
-		*pos++ = 0; /* WME info */
-		*pos++ = 1; /* WME ver */
-		*pos++ = 0;
-	}
-
-	/* wmm support is a must to HT */
-	/*
-	 * IEEE802.11n does not allow TKIP/WEP as pairwise
-	 * ciphers in HT mode. We still associate in non-ht
-	 * mode (11a/b/g) if any one of these ciphers is
-	 * configured as pairwise.
-	 */
-	if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
-	    sband->ht_cap.ht_supported &&
-	    (ht_ie = wk->assoc.ht_information_ie) &&
-	    ht_ie[1] >= sizeof(struct ieee80211_ht_info) &&
-	    (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) {
-		struct ieee80211_ht_info *ht_info =
-			(struct ieee80211_ht_info *)(ht_ie + 2);
-		u16 cap = sband->ht_cap.cap;
-		__le16 tmp;
-		u32 flags = local->hw.conf.channel->flags;
-
-		/* determine capability flags */
-
-		if (ieee80211_disable_40mhz_24ghz &&
-		    sband->band == IEEE80211_BAND_2GHZ) {
-			cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-			cap &= ~IEEE80211_HT_CAP_SGI_40;
-		}
-
-		switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
-		case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-			if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
-				cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-				cap &= ~IEEE80211_HT_CAP_SGI_40;
-			}
-			break;
-		case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-			if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
-				cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-				cap &= ~IEEE80211_HT_CAP_SGI_40;
-			}
-			break;
-		}
-
-		/* set SM PS mode properly */
-		cap &= ~IEEE80211_HT_CAP_SM_PS;
-		/* new association always uses requested smps mode */
-		if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
-			if (ifmgd->powersave)
-				ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
-			else
-				ifmgd->ap_smps = IEEE80211_SMPS_OFF;
-		} else
-			ifmgd->ap_smps = ifmgd->req_smps;
-
-		switch (ifmgd->ap_smps) {
-		case IEEE80211_SMPS_AUTOMATIC:
-		case IEEE80211_SMPS_NUM_MODES:
-			WARN_ON(1);
-		case IEEE80211_SMPS_OFF:
-			cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
-				IEEE80211_HT_CAP_SM_PS_SHIFT;
-			break;
-		case IEEE80211_SMPS_STATIC:
-			cap |= WLAN_HT_CAP_SM_PS_STATIC <<
-				IEEE80211_HT_CAP_SM_PS_SHIFT;
-			break;
-		case IEEE80211_SMPS_DYNAMIC:
-			cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
-				IEEE80211_HT_CAP_SM_PS_SHIFT;
-			break;
-		}
-
-		/* reserve and fill IE */
-
-		pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
-		*pos++ = WLAN_EID_HT_CAPABILITY;
-		*pos++ = sizeof(struct ieee80211_ht_cap);
-		memset(pos, 0, sizeof(struct ieee80211_ht_cap));
-
-		/* capability flags */
-		tmp = cpu_to_le16(cap);
-		memcpy(pos, &tmp, sizeof(u16));
-		pos += sizeof(u16);
-
-		/* AMPDU parameters */
-		*pos++ = sband->ht_cap.ampdu_factor |
-			 (sband->ht_cap.ampdu_density <<
-			 	IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
-
-		/* MCS set */
-		memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
-		pos += sizeof(sband->ht_cap.mcs);
-
-		/* extended capabilities */
-		pos += sizeof(__le16);
-
-		/* BF capabilities */
-		pos += sizeof(__le32);
-
-		/* antenna selection */
-		pos += sizeof(u8);
-	}
-
-	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-	ieee80211_tx_skb(sdata, skb);
-}
-
-
 static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
 					   const u8 *bssid, u16 stype, u16 reason,
 					   void *cookie)
@@ -751,6 +464,11 @@ void ieee80211_recalc_ps(struct ieee8021
 		return;
 	}
 
+	if (!list_empty(&local->work_list)) {
+		local->ps_sdata = NULL;
+		goto change;
+	}
+
 	list_for_each_entry(sdata, &local->interfaces, list) {
 		if (!ieee80211_sdata_running(sdata))
 			continue;
@@ -761,7 +479,7 @@ void ieee80211_recalc_ps(struct ieee8021
 	}
 
 	if (count == 1 && found->u.mgd.powersave &&
-	    found->u.mgd.associated && list_empty(&found->u.mgd.work_list) &&
+	    found->u.mgd.associated &&
 	    !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
 				    IEEE80211_STA_CONNECTION_POLL))) {
 		s32 beaconint_us;
@@ -789,6 +507,7 @@ void ieee80211_recalc_ps(struct ieee8021
 		local->ps_sdata = NULL;
 	}
 
+ change:
 	ieee80211_change_ps(local);
 }
 
@@ -848,7 +567,7 @@ static void ieee80211_sta_wmm_params(str
 	int count;
 	u8 *pos;
 
-	if (!(ifmgd->flags & IEEE80211_STA_WMM_ENABLED))
+	if (local->hw.queues < 4)
 		return;
 
 	if (!wmm_param)
@@ -997,110 +716,6 @@ static void ieee80211_set_associated(str
 	netif_carrier_on(sdata->dev);
 }
 
-static void ieee80211_remove_auth_bss(struct ieee80211_local *local,
-				      struct ieee80211_work *wk)
-{
-	struct cfg80211_bss *cbss;
-	u16 capa_val = WLAN_CAPABILITY_ESS;
-
-	if (wk->auth.privacy)
-		capa_val |= WLAN_CAPABILITY_PRIVACY;
-
-	cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->auth.bssid,
-				wk->auth.ssid, wk->auth.ssid_len,
-				WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
-				capa_val);
-	if (!cbss)
-		return;
-
-	cfg80211_unlink_bss(local->hw.wiphy, cbss);
-	cfg80211_put_bss(cbss);
-}
-
-static enum rx_mgmt_action __must_check
-ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
-		       struct ieee80211_work *wk)
-{
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_local *local = sdata->local;
-
-	wk->auth.tries++;
-	if (wk->auth.tries > IEEE80211_AUTH_MAX_TRIES) {
-		printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
-		       sdata->name, wk->auth.bssid);
-
-		/*
-		 * Most likely AP is not in the range so remove the
-		 * bss struct for that AP.
-		 */
-		ieee80211_remove_auth_bss(local, wk);
-
-		/*
-		 * We might have a pending scan which had no chance to run yet
-		 * due to work needing to be done. Hence, queue the STAs work
-		 * again for that.
-		 */
-		ieee80211_queue_work(&local->hw, &ifmgd->work);
-		return RX_MGMT_CFG80211_AUTH_TO;
-	}
-
-	printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
-			sdata->name, wk->auth.bssid, wk->auth.tries);
-
-	/*
-	 * Direct probe is sent to broadcast address as some APs
-	 * will not answer to direct packet in unassociated state.
-	 */
-	ieee80211_send_probe_req(sdata, NULL, wk->auth.ssid, wk->auth.ssid_len,
-				 NULL, 0);
-
-	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
-	run_again(ifmgd, wk->timeout);
-
-	return RX_MGMT_NONE;
-}
-
-
-static enum rx_mgmt_action __must_check
-ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
-		       struct ieee80211_work *wk)
-{
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_local *local = sdata->local;
-
-	wk->auth.tries++;
-	if (wk->auth.tries > IEEE80211_AUTH_MAX_TRIES) {
-		printk(KERN_DEBUG "%s: authentication with AP %pM"
-		       " timed out\n", sdata->name, wk->auth.bssid);
-
-		/*
-		 * Most likely AP is not in the range so remove the
-		 * bss struct for that AP.
-		 */
-		ieee80211_remove_auth_bss(local, wk);
-
-		/*
-		 * We might have a pending scan which had no chance to run yet
-		 * due to work needing to be done. Hence, queue the STAs work
-		 * again for that.
-		 */
-		ieee80211_queue_work(&local->hw, &ifmgd->work);
-		return RX_MGMT_CFG80211_AUTH_TO;
-	}
-
-	printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
-	       sdata->name, wk->auth.bssid, wk->auth.tries);
-
-	ieee80211_send_auth(sdata, 1, wk->auth.algorithm, wk->ie, wk->ie_len,
-			    wk->auth.bssid, NULL, 0, 0);
-	wk->auth.transaction = 2;
-
-	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
-	run_again(ifmgd, wk->timeout);
-
-	return RX_MGMT_NONE;
-}
-
 static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -1187,44 +802,6 @@ static void ieee80211_set_disassoc(struc
 	sta_info_destroy(sta);
 }
 
-static enum rx_mgmt_action __must_check
-ieee80211_associate(struct ieee80211_sub_if_data *sdata,
-		    struct ieee80211_work *wk)
-{
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_local *local = sdata->local;
-
-	wk->assoc.tries++;
-	if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) {
-		printk(KERN_DEBUG "%s: association with AP %pM"
-		       " timed out\n",
-		       sdata->name, wk->assoc.bssid);
-
-		/*
-		 * Most likely AP is not in the range so remove the
-		 * bss struct for that AP.
-		 */
-		cfg80211_unlink_bss(local->hw.wiphy, &wk->assoc.bss->cbss);
-
-		/*
-		 * We might have a pending scan which had no chance to run yet
-		 * due to work needing to be done. Hence, queue the STAs work
-		 * again for that.
-		 */
-		ieee80211_queue_work(&local->hw, &ifmgd->work);
-		return RX_MGMT_CFG80211_ASSOC_TO;
-	}
-
-	printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
-	       sdata->name, wk->assoc.bssid, wk->assoc.tries);
-	ieee80211_send_assoc(sdata, wk);
-
-	wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
-	run_again(ifmgd, wk->timeout);
-
-	return RX_MGMT_NONE;
-}
-
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
 			     struct ieee80211_hdr *hdr)
 {
@@ -1330,86 +907,6 @@ void ieee80211_beacon_loss(struct ieee80
 }
 EXPORT_SYMBOL(ieee80211_beacon_loss);
 
-static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
-				     struct ieee80211_work *wk)
-{
-	list_del(&wk->list);
-	kfree(wk);
-	printk(KERN_DEBUG "%s: authenticated\n", sdata->name);
-}
-
-
-static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
-				     struct ieee80211_work *wk,
-				     struct ieee80211_mgmt *mgmt,
-				     size_t len)
-{
-	u8 *pos;
-	struct ieee802_11_elems elems;
-
-	pos = mgmt->u.auth.variable;
-	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
-	if (!elems.challenge)
-		return;
-	ieee80211_send_auth(sdata, 3, wk->auth.algorithm,
-			    elems.challenge - 2, elems.challenge_len + 2,
-			    wk->auth.bssid, wk->auth.key, wk->auth.key_len,
-			    wk->auth.key_idx);
-	wk->auth.transaction = 4;
-}
-
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
-		       struct ieee80211_work *wk,
-		       struct ieee80211_mgmt *mgmt, size_t len)
-{
-	u16 auth_alg, auth_transaction, status_code;
-
-	if (wk->type != IEEE80211_WORK_AUTH)
-		return RX_MGMT_NONE;
-
-	if (len < 24 + 6)
-		return RX_MGMT_NONE;
-
-	if (memcmp(wk->auth.bssid, mgmt->sa, ETH_ALEN) != 0)
-		return RX_MGMT_NONE;
-
-	if (memcmp(wk->auth.bssid, mgmt->bssid, ETH_ALEN) != 0)
-		return RX_MGMT_NONE;
-
-	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
-	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
-	status_code = le16_to_cpu(mgmt->u.auth.status_code);
-
-	if (auth_alg != wk->auth.algorithm ||
-	    auth_transaction != wk->auth.transaction)
-		return RX_MGMT_NONE;
-
-	if (status_code != WLAN_STATUS_SUCCESS) {
-		list_del(&wk->list);
-		kfree(wk);
-		return RX_MGMT_CFG80211_AUTH;
-	}
-
-	switch (wk->auth.algorithm) {
-	case WLAN_AUTH_OPEN:
-	case WLAN_AUTH_LEAP:
-	case WLAN_AUTH_FT:
-		ieee80211_auth_completed(sdata, wk);
-		return RX_MGMT_CFG80211_AUTH;
-	case WLAN_AUTH_SHARED_KEY:
-		if (wk->auth.transaction == 4) {
-			ieee80211_auth_completed(sdata, wk);
-			return RX_MGMT_CFG80211_AUTH;
-		} else
-			ieee80211_auth_challenge(sdata, wk, mgmt, len);
-		break;
-	}
-
-	return RX_MGMT_NONE;
-}
-
-
 static enum rx_mgmt_action __must_check
 ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 			 struct ieee80211_mgmt *mgmt, size_t len)
@@ -1466,98 +963,51 @@ ieee80211_rx_mgmt_disassoc(struct ieee80
 }
 
 
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
-			     struct ieee80211_work *wk,
-			     struct ieee80211_mgmt *mgmt, size_t len,
-			     bool reassoc)
+static bool ieee80211_assoc_success(struct ieee80211_work *wk,
+				    struct ieee80211_mgmt *mgmt, size_t len)
 {
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_supported_band *sband;
 	struct sta_info *sta;
 	struct ieee80211_bss *bss = wk->assoc.bss;
+	u8 *pos;
 	u32 rates, basic_rates;
-	u16 capab_info, status_code, aid;
+	u16 capab_info, aid;
 	struct ieee802_11_elems elems;
 	struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
-	u8 *pos;
 	u32 changed = 0;
 	int i, j, err;
 	bool have_higher_than_11mbit = false;
 	u16 ap_ht_cap_flags;
 
-	/*
-	 * AssocResp and ReassocResp have identical structure, so process both
-	 * of them in this function.
-	 */
-
-	if (len < 24 + 6)
-		return RX_MGMT_NONE;
-
-	if (memcmp(bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
-		return RX_MGMT_NONE;
+	/* AssocResp and ReassocResp have identical structure */
 
-	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
-	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
 	aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
-
-	printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
-	       "status=%d aid=%d)\n",
-	       sdata->name, reassoc ? "Rea" : "A", mgmt->sa,
-	       capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
-
-	pos = mgmt->u.assoc_resp.variable;
-	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
-
-	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
-	    elems.timeout_int && elems.timeout_int_len == 5 &&
-	    elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
-		u32 tu, ms;
-		tu = get_unaligned_le32(elems.timeout_int + 1);
-		ms = tu * 1024 / 1000;
-		printk(KERN_DEBUG "%s: AP rejected association temporarily; "
-		       "comeback duration %u TU (%u ms)\n",
-		       sdata->name, tu, ms);
-		wk->timeout = jiffies + msecs_to_jiffies(ms);
-		if (ms > IEEE80211_ASSOC_TIMEOUT)
-			run_again(ifmgd, jiffies + msecs_to_jiffies(ms));
-		return RX_MGMT_NONE;
-	}
-
-	/*
-	 * Here the association was either successful or not.
-	 */
-
-	/* delete work item -- must be before set_associated for PS */
-	list_del(&wk->list);
-	kfree(wk);
-
-	if (status_code != WLAN_STATUS_SUCCESS) {
-		printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
-		       sdata->name, status_code);
-		return RX_MGMT_CFG80211_ASSOC;
-	}
+	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
 
 	if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
 		printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
 		       "set\n", sdata->name, aid);
 	aid &= ~(BIT(15) | BIT(14));
 
+	pos = mgmt->u.assoc_resp.variable;
+	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+
 	if (!elems.supp_rates) {
 		printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n",
 		       sdata->name);
-		return RX_MGMT_NONE;
+		return false;
 	}
 
-	printk(KERN_DEBUG "%s: associated\n", sdata->name);
 	ifmgd->aid = aid;
 
 	sta = sta_info_alloc(sdata, bss->cbss.bssid, GFP_KERNEL);
 	if (!sta) {
 		printk(KERN_DEBUG "%s: failed to alloc STA entry for"
 		       " the AP\n", sdata->name);
-		return RX_MGMT_CFG80211_ASSOC_ERROR;
+		return false;
 	}
 
 	set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC |
@@ -1642,7 +1092,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 		ieee80211_set_wmm_default(sdata);
 
 	if (elems.ht_info_elem && elems.wmm_param &&
-	    (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
+	    (sdata->local->hw.queues >= 4) &&
 	    !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
 		changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
 					       bss->cbss.bssid,
@@ -1661,7 +1111,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
 	mod_beacon_timer(sdata);
 
-	return RX_MGMT_CFG80211_ASSOC;
+	return true;
 }
 
 
@@ -1706,12 +1156,12 @@ static void ieee80211_rx_bss_info(struct
 
 
 static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
-					 struct ieee80211_work *wk,
-					 struct ieee80211_mgmt *mgmt, size_t len,
-					 struct ieee80211_rx_status *rx_status)
+					 struct sk_buff *skb)
 {
+	struct ieee80211_mgmt *mgmt = (void *)skb->data;
 	struct ieee80211_if_managed *ifmgd;
-	size_t baselen;
+	struct ieee80211_rx_status *rx_status = (void *) skb->cb;
+	size_t baselen, len = skb->len;
 	struct ieee802_11_elems elems;
 
 	ifmgd = &sdata->u.mgd;
@@ -1730,15 +1180,6 @@ static void ieee80211_rx_mgmt_probe_resp
 
 	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
 
-	/* direct probe may be part of the association flow */
-	if (wk && wk->type == IEEE80211_WORK_AUTH_PROBE) {
-		printk(KERN_DEBUG "%s: direct probe responded\n",
-		       sdata->name);
-		wk->auth.tries = 0;
-		wk->type = IEEE80211_WORK_AUTH;
-		WARN_ON(ieee80211_authenticate(sdata, wk) != RX_MGMT_NONE);
-	}
-
 	if (ifmgd->associated &&
 	    memcmp(mgmt->bssid, ifmgd->associated->cbss.bssid, ETH_ALEN) == 0 &&
 	    ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
@@ -1952,9 +1393,6 @@ ieee80211_rx_result ieee80211_sta_rx_mgm
 	switch (fc & IEEE80211_FCTL_STYPE) {
 	case IEEE80211_STYPE_PROBE_RESP:
 	case IEEE80211_STYPE_BEACON:
-	case IEEE80211_STYPE_AUTH:
-	case IEEE80211_STYPE_ASSOC_RESP:
-	case IEEE80211_STYPE_REASSOC_RESP:
 	case IEEE80211_STYPE_DEAUTH:
 	case IEEE80211_STYPE_DISASSOC:
 	case IEEE80211_STYPE_ACTION:
@@ -1972,7 +1410,6 @@ static void ieee80211_sta_rx_queued_mgmt
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_mgmt *mgmt;
-	struct ieee80211_work *wk;
 	enum rx_mgmt_action rma = RX_MGMT_NONE;
 	u16 fc;
 
@@ -1991,8 +1428,7 @@ static void ieee80211_sta_rx_queued_mgmt
 						 rx_status);
 			break;
 		case IEEE80211_STYPE_PROBE_RESP:
-			ieee80211_rx_mgmt_probe_resp(sdata, NULL, mgmt,
-						     skb->len, rx_status);
+			ieee80211_rx_mgmt_probe_resp(sdata, skb);
 			break;
 		case IEEE80211_STYPE_DEAUTH:
 			rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
@@ -2025,88 +1461,11 @@ static void ieee80211_sta_rx_queued_mgmt
 		goto out;
 	}
 
-	list_for_each_entry(wk, &ifmgd->work_list, list) {
-		const u8 *bssid = NULL;
-
-		switch (wk->type) {
-		case IEEE80211_WORK_AUTH_PROBE:
-		case IEEE80211_WORK_AUTH:
-			bssid = wk->auth.bssid;
-			break;
-		case IEEE80211_WORK_ASSOC:
-			bssid = wk->assoc.bssid;
-			break;
-		default:
-			continue;
-		}
-		if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
-			continue;
-
-		switch (fc & IEEE80211_FCTL_STYPE) {
-		case IEEE80211_STYPE_PROBE_RESP:
-			ieee80211_rx_mgmt_probe_resp(sdata, wk, mgmt, skb->len,
-						     rx_status);
-			break;
-		case IEEE80211_STYPE_AUTH:
-			rma = ieee80211_rx_mgmt_auth(sdata, wk, mgmt, skb->len);
-			break;
-		case IEEE80211_STYPE_ASSOC_RESP:
-			rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt,
-							   skb->len, false);
-			break;
-		case IEEE80211_STYPE_REASSOC_RESP:
-			rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt,
-							   skb->len, true);
-			break;
-		case IEEE80211_STYPE_DEAUTH:
-			if (skb->len >= 24 + 2 /* mgmt + deauth reason */) {
-				/*
-				 * We get here if we get deauth while
-				 * trying to auth/assoc. Telling cfg80211
-				 * is handled below, unconditionally.
-				 */
-				list_del(&wk->list);
-				kfree(wk);
-			}
-			break;
-		}
-		/*
-		 * We've processed this frame for that work, so it can't
-		 * belong to another work struct.
-		 * NB: this is also required for correctness because the
-		 * called functions can free 'wk', and for 'rma'!
-		 */
-		break;
-	}
-
 	mutex_unlock(&ifmgd->mtx);
 
 	if (skb->len >= 24 + 2 /* mgmt + deauth reason */ &&
-	    (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH) {
-		WARN_ON(rma != RX_MGMT_NONE);
-		rma = RX_MGMT_CFG80211_DEAUTH;
-	}
-
-	switch (rma) {
-	case RX_MGMT_NONE:
-		/* no action */
-		break;
-	case RX_MGMT_CFG80211_AUTH:
-		cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, skb->len);
-		break;
-	case RX_MGMT_CFG80211_ASSOC:
-		cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len);
-		break;
-	case RX_MGMT_CFG80211_DEAUTH:
+	    (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH)
 		cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
-		break;
-	case RX_MGMT_CFG80211_ASSOC_ERROR:
-		/* an internal error -- pretend timeout for now */
-		cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
-		break;
-	default:
-		WARN(1, "unexpected: %d", rma);
-	}
 
  out:
 	kfree_skb(skb);
@@ -2134,9 +1493,6 @@ static void ieee80211_sta_work(struct wo
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd;
 	struct sk_buff *skb;
-	struct ieee80211_work *wk, *tmp;
-	LIST_HEAD(free_work);
-	enum rx_mgmt_action rma;
 
 	if (!ieee80211_sdata_running(sdata))
 		return;
@@ -2206,84 +1562,7 @@ static void ieee80211_sta_work(struct wo
 		}
 	}
 
-
-	ieee80211_recalc_idle(local);
-
-	list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) {
-		if (time_is_after_jiffies(wk->timeout)) {
-			/*
-			 * This work item isn't supposed to be worked on
-			 * right now, but take care to adjust the timer
-			 * properly.
-			 */
-			run_again(ifmgd, wk->timeout);
-			continue;
-		}
-
-		switch (wk->type) {
-		default:
-			WARN_ON(1);
-			/* nothing */
-			rma = RX_MGMT_NONE;
-			break;
-		case IEEE80211_WORK_AUTH_PROBE:
-			rma = ieee80211_direct_probe(sdata, wk);
-			break;
-		case IEEE80211_WORK_AUTH:
-			rma = ieee80211_authenticate(sdata, wk);
-			break;
-		case IEEE80211_WORK_ASSOC:
-			rma = ieee80211_associate(sdata, wk);
-			break;
-		}
-
-		switch (rma) {
-		case RX_MGMT_NONE:
-			/* no action required */
-			break;
-		case RX_MGMT_CFG80211_AUTH_TO:
-		case RX_MGMT_CFG80211_ASSOC_TO:
-			list_del(&wk->list);
-			list_add(&wk->list, &free_work);
-			/*
-			 * small abuse but only local -- keep the
-			 * action type in wk->timeout while the item
-			 * is on the cleanup list
-			 */
-			wk->timeout = rma;
-			break;
-		default:
-			WARN(1, "unexpected: %d", rma);
-		}
-	}
-
-	if (list_empty(&ifmgd->work_list) &&
-	    test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request))
-		ieee80211_queue_delayed_work(&local->hw,
-					     &local->scan_work,
-					     round_jiffies_relative(0));
-
 	mutex_unlock(&ifmgd->mtx);
-
-	list_for_each_entry_safe(wk, tmp, &free_work, list) {
-		/* see above how we're using wk->timeout */
-		switch (wk->timeout) {
-		case RX_MGMT_CFG80211_AUTH_TO:
-			cfg80211_send_auth_timeout(sdata->dev, wk->auth.bssid);
-			break;
-		case RX_MGMT_CFG80211_ASSOC_TO:
-			cfg80211_send_assoc_timeout(sdata->dev,
-						    wk->assoc.bssid);
-			break;
-		default:
-			WARN(1, "unexpected: %lu", wk->timeout);
-		}
-
-		list_del(&wk->list);
-		kfree(wk);
-	}
-
-	ieee80211_recalc_idle(local);
 }
 
 static void ieee80211_sta_bcn_mon_timer(unsigned long data)
@@ -2392,12 +1671,7 @@ void ieee80211_sta_setup_sdata(struct ie
 		    (unsigned long) sdata);
 	skb_queue_head_init(&ifmgd->skb_queue);
 
-	INIT_LIST_HEAD(&ifmgd->work_list);
-
-	ifmgd->capab = WLAN_CAPABILITY_ESS;
 	ifmgd->flags = 0;
-	if (sdata->local->hw.queues >= 4)
-		ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
 
 	mutex_init(&ifmgd->mtx);
 
@@ -2435,10 +1709,32 @@ int ieee80211_max_network_latency(struct
 }
 
 /* config hooks */
+static enum work_done_result
+ieee80211_probe_auth_done(struct ieee80211_work *wk,
+			  struct sk_buff *skb)
+{
+	if (!skb) {
+		cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta);
+		return WORK_DONE_DESTROY;
+	}
+
+	if (wk->type == IEEE80211_WORK_AUTH) {
+		cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len);
+		return WORK_DONE_DESTROY;
+	}
+
+	mutex_lock(&wk->sdata->u.mgd.mtx);
+	ieee80211_rx_mgmt_probe_resp(wk->sdata, skb);
+	mutex_unlock(&wk->sdata->u.mgd.mtx);
+
+	wk->type = IEEE80211_WORK_AUTH;
+	wk->probe_auth.tries = 0;
+	return WORK_DONE_REQUEUE;
+}
+
 int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
 		       struct cfg80211_auth_request *req)
 {
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	const u8 *ssid;
 	struct ieee80211_work *wk;
 	u16 auth_alg;
@@ -2464,7 +1760,7 @@ int ieee80211_mgd_auth(struct ieee80211_
 	if (!wk)
 		return -ENOMEM;
 
-	memcpy(wk->auth.bssid, req->bss->bssid, ETH_ALEN);;
+	memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);;
 
 	if (req->ie && req->ie_len) {
 		memcpy(wk->ie, req->ie, req->ie_len);
@@ -2472,21 +1768,22 @@ int ieee80211_mgd_auth(struct ieee80211_
 	}
 
 	if (req->key && req->key_len) {
-		wk->auth.key_len = req->key_len;
-		wk->auth.key_idx = req->key_idx;
-		memcpy(wk->auth.key, req->key, req->key_len);
+		wk->probe_auth.key_len = req->key_len;
+		wk->probe_auth.key_idx = req->key_idx;
+		memcpy(wk->probe_auth.key, req->key, req->key_len);
 	}
 
 	ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
-	memcpy(wk->auth.ssid, ssid + 2, ssid[1]);
-	wk->auth.ssid_len = ssid[1];
+	memcpy(wk->probe_auth.ssid, ssid + 2, ssid[1]);
+	wk->probe_auth.ssid_len = ssid[1];
 
-	wk->auth.algorithm = auth_alg;
-	wk->auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
+	wk->probe_auth.algorithm = auth_alg;
+	wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
 
-	wk->type = IEEE80211_WORK_AUTH_PROBE;
-	wk->timeout = jiffies; /* run right away */
+	wk->type = IEEE80211_WORK_DIRECT_PROBE;
 	wk->chan = req->bss->channel;
+	wk->sdata = sdata;
+	wk->done = ieee80211_probe_auth_done;
 
 	/*
 	 * XXX: if still associated need to tell AP that we're going
@@ -2497,29 +1794,58 @@ int ieee80211_mgd_auth(struct ieee80211_
 	sdata->local->oper_channel = req->bss->channel;
 	ieee80211_hw_config(sdata->local, 0);
 
-	mutex_lock(&ifmgd->mtx);
-	list_add(&wk->list, &sdata->u.mgd.work_list);
-	mutex_unlock(&ifmgd->mtx);
-
-	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work);
+	ieee80211_add_work(wk);
 	return 0;
 }
 
+static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
+						  struct sk_buff *skb)
+{
+	struct ieee80211_mgmt *mgmt;
+	u16 status;
+
+	if (!skb) {
+		cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
+		return WORK_DONE_DESTROY;
+	}
+
+	mgmt = (void *)skb->data;
+	status = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+
+	if (status == WLAN_STATUS_SUCCESS) {
+		mutex_lock(&wk->sdata->u.mgd.mtx);
+		if (!ieee80211_assoc_success(wk, mgmt, skb->len)) {
+			mutex_unlock(&wk->sdata->u.mgd.mtx);
+			/* oops -- internal error -- send timeout for now */
+			cfg80211_send_assoc_timeout(wk->sdata->dev,
+						    wk->filter_ta);
+			return WORK_DONE_DESTROY;
+		}
+		mutex_unlock(&wk->sdata->u.mgd.mtx);
+	}
+
+	cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len);
+	return WORK_DONE_DESTROY;
+}
+
 int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 			struct cfg80211_assoc_request *req)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_work *wk;
 	const u8 *ssid;
-	int i, err;
+	int i;
 
 	mutex_lock(&ifmgd->mtx);
+	if (ifmgd->associated) {
+		mutex_unlock(&ifmgd->mtx);
+		return -EALREADY;
+	}
+	mutex_unlock(&ifmgd->mtx);
 
 	wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
-	if (!wk) {
-		err = -ENOMEM;
-		goto out;
-	}
+	if (!wk)
+		return -ENOMEM;
 
 	ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
 
@@ -2538,8 +1864,19 @@ int ieee80211_mgd_assoc(struct ieee80211
 
 	wk->assoc.bss = (void *)req->bss;
 
-	memcpy(wk->assoc.bssid, req->bss->bssid, ETH_ALEN);
+	memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);
+
+	/* new association always uses requested smps mode */
+	if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
+		if (ifmgd->powersave)
+			ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
+		else
+			ifmgd->ap_smps = IEEE80211_SMPS_OFF;
+	} else
+		ifmgd->ap_smps = ifmgd->req_smps;
 
+	wk->assoc.smps = ifmgd->ap_smps;
+	wk->assoc.use_11n = !(ifmgd->flags & IEEE80211_STA_DISABLE_11N);
 	wk->assoc.capability = req->bss->capability;
 	wk->assoc.wmm_used = wk->assoc.bss->wmm_used;
 	wk->assoc.supp_rates = wk->assoc.bss->supp_rates;
@@ -2555,8 +1892,9 @@ int ieee80211_mgd_assoc(struct ieee80211
 		memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN);
 
 	wk->type = IEEE80211_WORK_ASSOC;
-	wk->timeout = jiffies; /* run right away */
 	wk->chan = req->bss->channel;
+	wk->sdata = sdata;
+	wk->done = ieee80211_assoc_done;
 
 	if (req->use_mfp) {
 		ifmgd->mfp = IEEE80211_MFP_REQUIRED;
@@ -2574,14 +1912,8 @@ int ieee80211_mgd_assoc(struct ieee80211
 	sdata->local->oper_channel = req->bss->channel;
 	ieee80211_hw_config(sdata->local, 0);
 
-	list_add(&wk->list, &ifmgd->work_list);
-	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work);
-
-	err = 0;
-
- out:
-	mutex_unlock(&ifmgd->mtx);
-	return err;
+	ieee80211_add_work(wk);
+	return 0;
 }
 
 int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
@@ -2598,15 +1930,19 @@ int ieee80211_mgd_deauth(struct ieee8021
 	if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) {
 		bssid = req->bss->bssid;
 		ieee80211_set_disassoc(sdata);
-	} else list_for_each_entry(wk, &ifmgd->work_list, list) {
-		if (wk->type != IEEE80211_WORK_AUTH_PROBE)
-			continue;
-		if (memcmp(req->bss->bssid, wk->auth.bssid, ETH_ALEN))
-			continue;
-		not_auth_yet = true;
-		list_del(&wk->list);
-		kfree(wk);
-		break;
+	} else {
+		rcu_read_lock();
+		list_for_each_entry_rcu(wk, &sdata->local->work_list, list) {
+			if (wk->type != IEEE80211_WORK_DIRECT_PROBE)
+				continue;
+			if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
+				continue;
+			not_auth_yet = true;
+			list_del(&wk->list);
+			kfree(wk);
+			break;
+		}
+		rcu_read_unlock();
 	}
 
 	/*
--- wireless-testing.orig/net/mac80211/scan.c	2009-12-01 18:23:13.000000000 +0100
+++ wireless-testing/net/mac80211/scan.c	2009-12-01 18:27:46.000000000 +0100
@@ -422,7 +422,6 @@ static int __ieee80211_start_scan(struct
 				  struct cfg80211_scan_request *req)
 {
 	struct ieee80211_local *local = sdata->local;
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	int rc;
 
 	if (local->scan_req)
@@ -452,11 +451,8 @@ static int __ieee80211_start_scan(struct
 	local->scan_req = req;
 	local->scan_sdata = sdata;
 
-	if (req != local->int_scan_req &&
-	    sdata->vif.type == NL80211_IFTYPE_STATION &&
-	    !list_empty(&ifmgd->work_list)) {
-		/* actually wait for the work it's doing to finish/time out */
-		set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
+	if (!list_empty(&local->work_list)) {
+		/* wait for the work to finish/time out */
 		return 0;
 	}
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/mac80211/work.c	2009-12-01 21:19:54.000000000 +0100
@@ -0,0 +1,902 @@
+/*
+ * mac80211 work implementation
+ *
+ * Copyright 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright 2004, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "ieee80211_i.h"
+#include "rate.h"
+
+#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
+#define IEEE80211_AUTH_MAX_TRIES 3
+#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
+#define IEEE80211_ASSOC_MAX_TRIES 3
+#define IEEE80211_MAX_PROBE_TRIES 5
+
+enum work_action {
+	WORK_ACT_NONE,
+	WORK_ACT_TIMEOUT,
+	WORK_ACT_DONE,
+};
+
+
+/* utils */
+static inline void ASSERT_WORK_MTX(struct ieee80211_local *local)
+{
+	WARN_ON(!mutex_is_locked(&local->work_mtx));
+}
+
+/*
+ * We can have multiple work items (and connection probing)
+ * scheduling this timer, but we need to take care to only
+ * reschedule it when it should fire _earlier_ than it was
+ * asked for before, or if it's not pending right now. This
+ * function ensures that. Note that it then is required to
+ * run this function for all timeouts after the first one
+ * has happened -- the work that runs from this timer will
+ * do that.
+ */
+static void run_again(struct ieee80211_local *local,
+		      unsigned long timeout)
+{
+	ASSERT_WORK_MTX(local);
+
+	if (!timer_pending(&local->work_timer) ||
+	    time_before(timeout, local->work_timer.expires))
+		mod_timer(&local->work_timer, timeout);
+}
+
+static void work_free_rcu(struct rcu_head *head)
+{
+	struct ieee80211_work *wk =
+		container_of(head, struct ieee80211_work, rcu_head);
+
+	kfree(wk);
+}
+
+static void free_work(struct ieee80211_work *wk)
+{
+	call_rcu(&wk->rcu_head, work_free_rcu);
+}
+
+static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
+				      struct ieee80211_supported_band *sband,
+				      u32 *rates)
+{
+	int i, j, count;
+	*rates = 0;
+	count = 0;
+	for (i = 0; i < supp_rates_len; i++) {
+		int rate = (supp_rates[i] & 0x7F) * 5;
+
+		for (j = 0; j < sband->n_bitrates; j++)
+			if (sband->bitrates[j].bitrate == rate) {
+				*rates |= BIT(j);
+				count++;
+				break;
+			}
+	}
+
+	return count;
+}
+
+/* frame sending functions */
+
+static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
+				 struct ieee80211_work *wk)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct sk_buff *skb;
+	struct ieee80211_mgmt *mgmt;
+	u8 *pos;
+	const u8 *ies, *ht_ie;
+	int i, len, count, rates_len, supp_rates_len;
+	u16 capab;
+	struct ieee80211_supported_band *sband;
+	u32 rates = 0;
+
+	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+			    sizeof(*mgmt) + 200 + wk->ie_len +
+			    wk->assoc.ssid_len);
+	if (!skb) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
+		       "frame\n", sdata->name);
+		return;
+	}
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+
+	sband = local->hw.wiphy->bands[wk->chan->band];
+
+	capab = WLAN_CAPABILITY_ESS;
+
+	if (sband->band == IEEE80211_BAND_2GHZ) {
+		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
+			capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
+			capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+	}
+
+	if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY)
+		capab |= WLAN_CAPABILITY_PRIVACY;
+
+	/*
+	 * Get all rates supported by the device and the AP as
+	 * some APs don't like getting a superset of their rates
+	 * in the association request (e.g. D-Link DAP 1353 in
+	 * b-only mode)...
+	 */
+	rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
+					       wk->assoc.supp_rates_len,
+					       sband, &rates);
+
+	if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
+	    (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
+		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+
+	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+	memset(mgmt, 0, 24);
+	memcpy(mgmt->da, wk->filter_ta, ETH_ALEN);
+	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+	memcpy(mgmt->bssid, wk->filter_ta, ETH_ALEN);
+
+	if (!is_zero_ether_addr(wk->assoc.prev_bssid)) {
+		skb_put(skb, 10);
+		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+						  IEEE80211_STYPE_REASSOC_REQ);
+		mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
+		mgmt->u.reassoc_req.listen_interval =
+				cpu_to_le16(local->hw.conf.listen_interval);
+		memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid,
+		       ETH_ALEN);
+	} else {
+		skb_put(skb, 4);
+		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+						  IEEE80211_STYPE_ASSOC_REQ);
+		mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
+		mgmt->u.assoc_req.listen_interval =
+				cpu_to_le16(local->hw.conf.listen_interval);
+	}
+
+	/* SSID */
+	ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len);
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = wk->assoc.ssid_len;
+	memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len);
+
+	/* add all rates which were marked to be used above */
+	supp_rates_len = rates_len;
+	if (supp_rates_len > 8)
+		supp_rates_len = 8;
+
+	len = sband->n_bitrates;
+	pos = skb_put(skb, supp_rates_len + 2);
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = supp_rates_len;
+
+	count = 0;
+	for (i = 0; i < sband->n_bitrates; i++) {
+		if (BIT(i) & rates) {
+			int rate = sband->bitrates[i].bitrate;
+			*pos++ = (u8) (rate / 5);
+			if (++count == 8)
+				break;
+		}
+	}
+
+	if (rates_len > count) {
+		pos = skb_put(skb, rates_len - count + 2);
+		*pos++ = WLAN_EID_EXT_SUPP_RATES;
+		*pos++ = rates_len - count;
+
+		for (i++; i < sband->n_bitrates; i++) {
+			if (BIT(i) & rates) {
+				int rate = sband->bitrates[i].bitrate;
+				*pos++ = (u8) (rate / 5);
+			}
+		}
+	}
+
+	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
+		/* 1. power capabilities */
+		pos = skb_put(skb, 4);
+		*pos++ = WLAN_EID_PWR_CAPABILITY;
+		*pos++ = 2;
+		*pos++ = 0; /* min tx power */
+		*pos++ = local->hw.conf.channel->max_power; /* max tx power */
+
+		/* 2. supported channels */
+		/* TODO: get this in reg domain format */
+		pos = skb_put(skb, 2 * sband->n_channels + 2);
+		*pos++ = WLAN_EID_SUPPORTED_CHANNELS;
+		*pos++ = 2 * sband->n_channels;
+		for (i = 0; i < sband->n_channels; i++) {
+			*pos++ = ieee80211_frequency_to_channel(
+					sband->channels[i].center_freq);
+			*pos++ = 1; /* one channel in the subband*/
+		}
+	}
+
+	if (wk->ie_len && wk->ie) {
+		pos = skb_put(skb, wk->ie_len);
+		memcpy(pos, wk->ie, wk->ie_len);
+	}
+
+	if (wk->assoc.wmm_used && local->hw.queues >= 4) {
+		pos = skb_put(skb, 9);
+		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+		*pos++ = 7; /* len */
+		*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+		*pos++ = 0x50;
+		*pos++ = 0xf2;
+		*pos++ = 2; /* WME */
+		*pos++ = 0; /* WME info */
+		*pos++ = 1; /* WME ver */
+		*pos++ = 0;
+	}
+
+	/* wmm support is a must to HT */
+	/*
+	 * IEEE802.11n does not allow TKIP/WEP as pairwise
+	 * ciphers in HT mode. We still associate in non-ht
+	 * mode (11a/b/g) if any one of these ciphers is
+	 * configured as pairwise.
+	 */
+	if (wk->assoc.use_11n && wk->assoc.wmm_used &&
+	    (local->hw.queues >= 4) &&
+	    sband->ht_cap.ht_supported &&
+	    (ht_ie = wk->assoc.ht_information_ie) &&
+	    ht_ie[1] >= sizeof(struct ieee80211_ht_info)) {
+		struct ieee80211_ht_info *ht_info =
+			(struct ieee80211_ht_info *)(ht_ie + 2);
+		u16 cap = sband->ht_cap.cap;
+		__le16 tmp;
+		u32 flags = local->hw.conf.channel->flags;
+
+		/* determine capability flags */
+
+		if (ieee80211_disable_40mhz_24ghz &&
+		    sband->band == IEEE80211_BAND_2GHZ) {
+			cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+			cap &= ~IEEE80211_HT_CAP_SGI_40;
+		}
+
+		switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+		case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+			if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
+				cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+				cap &= ~IEEE80211_HT_CAP_SGI_40;
+			}
+			break;
+		case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+			if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
+				cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+				cap &= ~IEEE80211_HT_CAP_SGI_40;
+			}
+			break;
+		}
+
+		/* set SM PS mode properly */
+		cap &= ~IEEE80211_HT_CAP_SM_PS;
+		switch (wk->assoc.smps) {
+		case IEEE80211_SMPS_AUTOMATIC:
+		case IEEE80211_SMPS_NUM_MODES:
+			WARN_ON(1);
+		case IEEE80211_SMPS_OFF:
+			cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
+				IEEE80211_HT_CAP_SM_PS_SHIFT;
+			break;
+		case IEEE80211_SMPS_STATIC:
+			cap |= WLAN_HT_CAP_SM_PS_STATIC <<
+				IEEE80211_HT_CAP_SM_PS_SHIFT;
+			break;
+		case IEEE80211_SMPS_DYNAMIC:
+			cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
+				IEEE80211_HT_CAP_SM_PS_SHIFT;
+			break;
+		}
+
+		/* reserve and fill IE */
+
+		pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+		*pos++ = WLAN_EID_HT_CAPABILITY;
+		*pos++ = sizeof(struct ieee80211_ht_cap);
+		memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+		/* capability flags */
+		tmp = cpu_to_le16(cap);
+		memcpy(pos, &tmp, sizeof(u16));
+		pos += sizeof(u16);
+
+		/* AMPDU parameters */
+		*pos++ = sband->ht_cap.ampdu_factor |
+			 (sband->ht_cap.ampdu_density <<
+			 	IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+		/* MCS set */
+		memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
+		pos += sizeof(sband->ht_cap.mcs);
+
+		/* extended capabilities */
+		pos += sizeof(__le16);
+
+		/* BF capabilities */
+		pos += sizeof(__le32);
+
+		/* antenna selection */
+		pos += sizeof(u8);
+	}
+
+	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+	ieee80211_tx_skb(sdata, skb);
+}
+
+static void ieee80211_remove_auth_bss(struct ieee80211_local *local,
+				      struct ieee80211_work *wk)
+{
+	struct cfg80211_bss *cbss;
+	u16 capa_val = WLAN_CAPABILITY_ESS;
+
+	if (wk->probe_auth.privacy)
+		capa_val |= WLAN_CAPABILITY_PRIVACY;
+
+	cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->filter_ta,
+				wk->probe_auth.ssid, wk->probe_auth.ssid_len,
+				WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
+				capa_val);
+	if (!cbss)
+		return;
+
+	cfg80211_unlink_bss(local->hw.wiphy, cbss);
+	cfg80211_put_bss(cbss);
+}
+
+static enum work_action __must_check
+ieee80211_direct_probe(struct ieee80211_work *wk)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+
+	wk->probe_auth.tries++;
+	if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
+		printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
+		       sdata->name, wk->filter_ta);
+
+		/*
+		 * Most likely AP is not in the range so remove the
+		 * bss struct for that AP.
+		 */
+		ieee80211_remove_auth_bss(local, wk);
+
+		/*
+		 * We might have a pending scan which had no chance to run yet
+		 * due to work needing to be done. Hence, queue the STAs work
+		 * again for that.
+		 */
+		ieee80211_queue_work(&local->hw, &local->work_work);
+		return WORK_ACT_TIMEOUT;
+	}
+
+	printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
+			sdata->name, wk->filter_ta, wk->probe_auth.tries);
+
+	/*
+	 * Direct probe is sent to broadcast address as some APs
+	 * will not answer to direct packet in unassociated state.
+	 */
+	ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid,
+				 wk->probe_auth.ssid_len, NULL, 0);
+
+	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+	run_again(local, wk->timeout);
+
+	return WORK_ACT_NONE;
+}
+
+
+static enum work_action __must_check
+ieee80211_authenticate(struct ieee80211_work *wk)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+
+	wk->probe_auth.tries++;
+	if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
+		printk(KERN_DEBUG "%s: authentication with AP %pM"
+		       " timed out\n", sdata->name, wk->filter_ta);
+
+		/*
+		 * Most likely AP is not in the range so remove the
+		 * bss struct for that AP.
+		 */
+		ieee80211_remove_auth_bss(local, wk);
+
+		/*
+		 * We might have a pending scan which had no chance to run yet
+		 * due to work needing to be done. Hence, queue the STAs work
+		 * again for that.
+		 */
+		ieee80211_queue_work(&local->hw, &local->work_work);
+		return WORK_ACT_TIMEOUT;
+	}
+
+	printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
+	       sdata->name, wk->filter_ta, wk->probe_auth.tries);
+
+	ieee80211_send_auth(sdata, 1, wk->probe_auth.algorithm, wk->ie,
+			    wk->ie_len, wk->filter_ta, NULL, 0, 0);
+	wk->probe_auth.transaction = 2;
+
+	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+	run_again(local, wk->timeout);
+
+	return WORK_ACT_NONE;
+}
+
+static enum work_action __must_check
+ieee80211_associate(struct ieee80211_work *wk)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+
+	wk->assoc.tries++;
+	if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) {
+		printk(KERN_DEBUG "%s: association with AP %pM"
+		       " timed out\n",
+		       sdata->name, wk->filter_ta);
+
+		/*
+		 * Most likely AP is not in the range so remove the
+		 * bss struct for that AP.
+		 */
+		if (wk->assoc.bss)
+			cfg80211_unlink_bss(local->hw.wiphy,
+					    &wk->assoc.bss->cbss);
+
+		/*
+		 * We might have a pending scan which had no chance to run yet
+		 * due to work needing to be done. Hence, queue the STAs work
+		 * again for that.
+		 */
+		ieee80211_queue_work(&local->hw, &local->work_work);
+		return WORK_ACT_TIMEOUT;
+	}
+
+	printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
+	       sdata->name, wk->filter_ta, wk->assoc.tries);
+	ieee80211_send_assoc(sdata, wk);
+
+	wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
+	run_again(local, wk->timeout);
+
+	return WORK_ACT_NONE;
+}
+
+static void ieee80211_auth_challenge(struct ieee80211_work *wk,
+				     struct ieee80211_mgmt *mgmt,
+				     size_t len)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	u8 *pos;
+	struct ieee802_11_elems elems;
+
+	pos = mgmt->u.auth.variable;
+	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+	if (!elems.challenge)
+		return;
+	ieee80211_send_auth(sdata, 3, wk->probe_auth.algorithm,
+			    elems.challenge - 2, elems.challenge_len + 2,
+			    wk->filter_ta, wk->probe_auth.key,
+			    wk->probe_auth.key_len, wk->probe_auth.key_idx);
+	wk->probe_auth.transaction = 4;
+}
+
+static enum work_action __must_check
+ieee80211_rx_mgmt_auth(struct ieee80211_work *wk,
+		       struct ieee80211_mgmt *mgmt, size_t len)
+{
+	u16 auth_alg, auth_transaction, status_code;
+
+	if (wk->type != IEEE80211_WORK_AUTH)
+		return WORK_ACT_NONE;
+
+	if (len < 24 + 6)
+		return WORK_ACT_NONE;
+
+	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
+	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
+	status_code = le16_to_cpu(mgmt->u.auth.status_code);
+
+	if (auth_alg != wk->probe_auth.algorithm ||
+	    auth_transaction != wk->probe_auth.transaction)
+		return WORK_ACT_NONE;
+
+	if (status_code != WLAN_STATUS_SUCCESS) {
+		printk(KERN_DEBUG "%s: %pM denied authentication (status %d)\n",
+		       wk->sdata->name, mgmt->sa, status_code);
+		return WORK_ACT_DONE;
+	}
+
+	switch (wk->probe_auth.algorithm) {
+	case WLAN_AUTH_OPEN:
+	case WLAN_AUTH_LEAP:
+	case WLAN_AUTH_FT:
+		break;
+	case WLAN_AUTH_SHARED_KEY:
+		if (wk->probe_auth.transaction != 4) {
+			ieee80211_auth_challenge(wk, mgmt, len);
+			/* need another frame */
+			return WORK_ACT_NONE;
+		}
+		break;
+	default:
+		WARN_ON(1);
+		return WORK_ACT_NONE;
+	}
+
+ 	printk(KERN_DEBUG "%s: authenticated\n", wk->sdata->name);
+ 	return WORK_ACT_DONE;
+}
+
+static enum work_action __must_check
+ieee80211_rx_mgmt_assoc_resp(struct ieee80211_work *wk,
+			     struct ieee80211_mgmt *mgmt, size_t len,
+			     bool reassoc)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+	u16 capab_info, status_code, aid;
+	struct ieee802_11_elems elems;
+	u8 *pos;
+
+	/*
+	 * AssocResp and ReassocResp have identical structure, so process both
+	 * of them in this function.
+	 */
+
+	if (len < 24 + 6)
+		return WORK_ACT_NONE;
+
+	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
+	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+	aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+
+	printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
+	       "status=%d aid=%d)\n",
+	       sdata->name, reassoc ? "Rea" : "A", mgmt->sa,
+	       capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
+
+	pos = mgmt->u.assoc_resp.variable;
+	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+
+	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
+	    elems.timeout_int && elems.timeout_int_len == 5 &&
+	    elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
+		u32 tu, ms;
+		tu = get_unaligned_le32(elems.timeout_int + 1);
+		ms = tu * 1024 / 1000;
+		printk(KERN_DEBUG "%s: AP rejected association temporarily; "
+		       "comeback duration %u TU (%u ms)\n",
+		       sdata->name, tu, ms);
+		wk->timeout = jiffies + msecs_to_jiffies(ms);
+		if (ms > IEEE80211_ASSOC_TIMEOUT)
+			run_again(local, wk->timeout);
+		return WORK_ACT_NONE;
+	}
+
+	if (status_code != WLAN_STATUS_SUCCESS)
+		printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
+		       sdata->name, status_code);
+	else
+		printk(KERN_DEBUG "%s: associated\n", sdata->name);
+
+	return WORK_ACT_DONE;
+}
+
+static enum work_action __must_check
+ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk,
+			     struct ieee80211_mgmt *mgmt, size_t len,
+			     struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+	size_t baselen;
+
+	ASSERT_WORK_MTX(local);
+
+	baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
+	if (baselen > len)
+		return WORK_ACT_NONE;
+
+	printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name);
+	return WORK_ACT_DONE;
+}
+
+static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
+					  struct sk_buff *skb)
+{
+	struct ieee80211_rx_status *rx_status;
+	struct ieee80211_mgmt *mgmt;
+	struct ieee80211_work *wk;
+	enum work_action rma = WORK_ACT_NONE;
+	u16 fc;
+
+	rx_status = (struct ieee80211_rx_status *) skb->cb;
+	mgmt = (struct ieee80211_mgmt *) skb->data;
+	fc = le16_to_cpu(mgmt->frame_control);
+
+	mutex_lock(&local->work_mtx);
+
+	list_for_each_entry(wk, &local->work_list, list) {
+		const u8 *bssid = NULL;
+
+		switch (wk->type) {
+		case IEEE80211_WORK_DIRECT_PROBE:
+		case IEEE80211_WORK_AUTH:
+		case IEEE80211_WORK_ASSOC:
+			bssid = wk->filter_ta;
+			break;
+		default:
+			continue;
+		}
+
+		/*
+		 * Before queuing, we already verified mgmt->sa,
+		 * so this is needed just for matching.
+		 */
+		if (compare_ether_addr(bssid, mgmt->bssid))
+			continue;
+
+		switch (fc & IEEE80211_FCTL_STYPE) {
+		case IEEE80211_STYPE_PROBE_RESP:
+			rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len,
+							   rx_status);
+			break;
+		case IEEE80211_STYPE_AUTH:
+			rma = ieee80211_rx_mgmt_auth(wk, mgmt, skb->len);
+			break;
+		case IEEE80211_STYPE_ASSOC_RESP:
+			rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt,
+							   skb->len, false);
+			break;
+		case IEEE80211_STYPE_REASSOC_RESP:
+			rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt,
+							   skb->len, true);
+			break;
+		default:
+			WARN_ON(1);
+		}
+		/*
+		 * We've processed this frame for that work, so it can't
+		 * belong to another work struct.
+		 * NB: this is also required for correctness for 'rma'!
+		 */
+		break;
+	}
+
+	switch (rma) {
+	case WORK_ACT_NONE:
+		break;
+	case WORK_ACT_DONE:
+		list_del_rcu(&wk->list);
+		break;
+	default:
+		WARN(1, "unexpected: %d", rma);
+	}
+
+	mutex_unlock(&local->work_mtx);
+
+	if (rma != WORK_ACT_DONE)
+		goto out;
+
+	switch (wk->done(wk, skb)) {
+	case WORK_DONE_DESTROY:
+		free_work(wk);
+		break;
+	case WORK_DONE_REQUEUE:
+		synchronize_rcu();
+		wk->timeout = jiffies; /* run again directly */
+		mutex_lock(&local->work_mtx);
+		list_add_tail(&wk->list, &local->work_list);
+		mutex_unlock(&local->work_mtx);
+	}
+
+ out:
+	kfree_skb(skb);
+}
+
+static void ieee80211_work_timer(unsigned long data)
+{
+	struct ieee80211_local *local = (void *) data;
+
+	if (local->quiescing)
+		return;
+
+	ieee80211_queue_work(&local->hw, &local->work_work);
+}
+
+static void ieee80211_work_work(struct work_struct *work)
+{
+	struct ieee80211_local *local =
+		container_of(work, struct ieee80211_local, work_work);
+	struct sk_buff *skb;
+	struct ieee80211_work *wk, *tmp;
+	LIST_HEAD(free_work);
+	enum work_action rma;
+
+	if (local->scanning)
+		return;
+
+	/*
+	 * ieee80211_queue_work() should have picked up most cases,
+	 * here we'll pick the the rest.
+	 */
+	if (WARN(local->suspended, "work scheduled while going to suspend\n"))
+		return;
+
+	/* first process frames to avoid timing out while a frame is pending */
+	while ((skb = skb_dequeue(&local->work_skb_queue)))
+		ieee80211_work_rx_queued_mgmt(local, skb);
+
+	ieee80211_recalc_idle(local);
+
+	mutex_lock(&local->work_mtx);
+
+	list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
+		if (time_is_after_jiffies(wk->timeout)) {
+			/*
+			 * This work item isn't supposed to be worked on
+			 * right now, but take care to adjust the timer
+			 * properly.
+			 */
+			run_again(local, wk->timeout);
+			continue;
+		}
+
+		switch (wk->type) {
+		default:
+			WARN_ON(1);
+			/* nothing */
+			rma = WORK_ACT_NONE;
+			break;
+		case IEEE80211_WORK_DIRECT_PROBE:
+			rma = ieee80211_direct_probe(wk);
+			break;
+		case IEEE80211_WORK_AUTH:
+			rma = ieee80211_authenticate(wk);
+			break;
+		case IEEE80211_WORK_ASSOC:
+			rma = ieee80211_associate(wk);
+			break;
+		}
+
+		switch (rma) {
+		case WORK_ACT_NONE:
+			/* no action required */
+			break;
+		case WORK_ACT_TIMEOUT:
+			list_del_rcu(&wk->list);
+			synchronize_rcu();
+			list_add(&wk->list, &free_work);
+			break;
+		default:
+			WARN(1, "unexpected: %d", rma);
+		}
+	}
+
+	if (list_empty(&local->work_list) && local->scan_req)
+		ieee80211_queue_delayed_work(&local->hw,
+					     &local->scan_work,
+					     round_jiffies_relative(0));
+
+	mutex_unlock(&local->work_mtx);
+
+	list_for_each_entry_safe(wk, tmp, &free_work, list) {
+		wk->done(wk, NULL);
+		list_del(&wk->list);
+		kfree(wk);
+	}
+}
+
+void ieee80211_add_work(struct ieee80211_work *wk)
+{
+	struct ieee80211_local *local;
+
+	if (WARN_ON(!wk->chan))
+		return;
+
+	if (WARN_ON(!wk->sdata))
+		return;
+
+	if (WARN_ON(!wk->done))
+		return;
+
+	wk->timeout = jiffies;
+
+	local = wk->sdata->local;
+	mutex_lock(&local->work_mtx);
+	list_add_tail(&wk->list, &local->work_list);
+	mutex_unlock(&local->work_mtx);
+
+	ieee80211_queue_work(&local->hw, &local->work_work);
+}
+
+void ieee80211_work_init(struct ieee80211_local *local)
+{
+	mutex_init(&local->work_mtx);
+	INIT_LIST_HEAD(&local->work_list);
+	setup_timer(&local->work_timer, ieee80211_work_timer,
+		    (unsigned long)local);
+	INIT_WORK(&local->work_work, ieee80211_work_work);
+	skb_queue_head_init(&local->work_skb_queue);
+}
+
+void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_work *wk, *tmp;
+
+	mutex_lock(&local->work_mtx);
+	list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
+		if (wk->sdata != sdata)
+			continue;
+		list_del(&wk->list);
+		free_work(wk);
+	}
+	mutex_unlock(&local->work_mtx);
+}
+
+ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
+					   struct sk_buff *skb)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_mgmt *mgmt;
+	struct ieee80211_work *wk;
+	u16 fc;
+
+	if (skb->len < 24)
+		return RX_DROP_MONITOR;
+
+	mgmt = (struct ieee80211_mgmt *) skb->data;
+	fc = le16_to_cpu(mgmt->frame_control);
+
+	list_for_each_entry_rcu(wk, &local->work_list, list) {
+		if (sdata != wk->sdata)
+			continue;
+		if (compare_ether_addr(wk->filter_ta, mgmt->sa))
+			continue;
+		if (compare_ether_addr(wk->filter_ta, mgmt->bssid))
+			continue;
+
+		switch (fc & IEEE80211_FCTL_STYPE) {
+		case IEEE80211_STYPE_AUTH:
+		case IEEE80211_STYPE_PROBE_RESP:
+		case IEEE80211_STYPE_ASSOC_RESP:
+		case IEEE80211_STYPE_REASSOC_RESP:
+		case IEEE80211_STYPE_DEAUTH:
+		case IEEE80211_STYPE_DISASSOC:
+			skb_queue_tail(&local->work_skb_queue, skb);
+			ieee80211_queue_work(&local->hw, &local->work_work);
+			return RX_QUEUED;
+		}
+	}
+
+	return RX_CONTINUE;
+}
--- wireless-testing.orig/net/mac80211/debugfs_netdev.c	2009-12-01 18:23:13.000000000 +0100
+++ wireless-testing/net/mac80211/debugfs_netdev.c	2009-12-01 18:27:46.000000000 +0100
@@ -133,7 +133,6 @@ IEEE80211_IF_FILE(max_ratectrl_rateidx, 
 /* STA attributes */
 IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
 IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
-IEEE80211_IF_FILE(capab, u.mgd.capab, HEX);
 
 static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
 			      enum ieee80211_smps_mode smps_mode)
@@ -270,7 +269,6 @@ static void add_sta_files(struct ieee802
 
 	DEBUGFS_ADD(bssid, sta);
 	DEBUGFS_ADD(aid, sta);
-	DEBUGFS_ADD(capab, sta);
 	DEBUGFS_ADD_MODE(smps, 0600);
 }
 
--- wireless-testing.orig/net/mac80211/main.c	2009-12-01 18:23:13.000000000 +0100
+++ wireless-testing/net/mac80211/main.c	2009-12-01 18:27:46.000000000 +0100
@@ -395,6 +395,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(
 
 	INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
 
+	ieee80211_work_init(local);
+
 	INIT_WORK(&local->restart_work, ieee80211_restart_work);
 
 	INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
--- wireless-testing.orig/net/mac80211/rx.c	2009-12-01 18:23:13.000000000 +0100
+++ wireless-testing/net/mac80211/rx.c	2009-12-01 21:19:53.000000000 +0100
@@ -2025,6 +2025,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_
 {
 	struct ieee80211_sub_if_data *sdata = rx->sdata;
 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
+	ieee80211_rx_result rxs;
 
 	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 		return RX_DROP_MONITOR;
@@ -2032,6 +2033,10 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_
 	if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
 		return RX_DROP_MONITOR;
 
+	rxs = ieee80211_work_rx_mgmt(rx->sdata, rx->skb);
+	if (rxs != RX_CONTINUE)
+		return rxs;
+
 	if (ieee80211_vif_is_mesh(&sdata->vif))
 		return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
 



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 6/6] mac80211: rewrite a few work messages
  2009-12-02 11:43 [PATCH 0/6] mac80211: mlme work improvements Johannes Berg
                   ` (4 preceding siblings ...)
  2009-12-02 11:43 ` [PATCH 5/6] mac80211: generalise work handling Johannes Berg
@ 2009-12-02 11:43 ` Johannes Berg
  5 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2009-12-02 11:43 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless

The station we're authenticating/associating with
may not always be an AP in the sense that word is
mostly understood, so print only the MAC address
of the peer instead.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/work.c |   20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

--- wireless-testing.orig/net/mac80211/work.c	2009-12-01 18:27:46.000000000 +0100
+++ wireless-testing/net/mac80211/work.c	2009-12-01 18:28:21.000000000 +0100
@@ -376,7 +376,7 @@ ieee80211_direct_probe(struct ieee80211_
 
 	wk->probe_auth.tries++;
 	if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
-		printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
+		printk(KERN_DEBUG "%s: direct probe to %pM timed out\n",
 		       sdata->name, wk->filter_ta);
 
 		/*
@@ -394,7 +394,7 @@ ieee80211_direct_probe(struct ieee80211_
 		return WORK_ACT_TIMEOUT;
 	}
 
-	printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
+	printk(KERN_DEBUG "%s: direct probe to %pM (try %d)\n",
 			sdata->name, wk->filter_ta, wk->probe_auth.tries);
 
 	/*
@@ -419,7 +419,7 @@ ieee80211_authenticate(struct ieee80211_
 
 	wk->probe_auth.tries++;
 	if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
-		printk(KERN_DEBUG "%s: authentication with AP %pM"
+		printk(KERN_DEBUG "%s: authentication with %pM"
 		       " timed out\n", sdata->name, wk->filter_ta);
 
 		/*
@@ -437,7 +437,7 @@ ieee80211_authenticate(struct ieee80211_
 		return WORK_ACT_TIMEOUT;
 	}
 
-	printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
+	printk(KERN_DEBUG "%s: authenticate with %pM (try %d)\n",
 	       sdata->name, wk->filter_ta, wk->probe_auth.tries);
 
 	ieee80211_send_auth(sdata, 1, wk->probe_auth.algorithm, wk->ie,
@@ -458,7 +458,7 @@ ieee80211_associate(struct ieee80211_wor
 
 	wk->assoc.tries++;
 	if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) {
-		printk(KERN_DEBUG "%s: association with AP %pM"
+		printk(KERN_DEBUG "%s: association with %pM"
 		       " timed out\n",
 		       sdata->name, wk->filter_ta);
 
@@ -479,7 +479,7 @@ ieee80211_associate(struct ieee80211_wor
 		return WORK_ACT_TIMEOUT;
 	}
 
-	printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
+	printk(KERN_DEBUG "%s: associate with %pM (try %d)\n",
 	       sdata->name, wk->filter_ta, wk->assoc.tries);
 	ieee80211_send_assoc(sdata, wk);
 
@@ -592,9 +592,9 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 		u32 tu, ms;
 		tu = get_unaligned_le32(elems.timeout_int + 1);
 		ms = tu * 1024 / 1000;
-		printk(KERN_DEBUG "%s: AP rejected association temporarily; "
+		printk(KERN_DEBUG "%s: %pM rejected association temporarily; "
 		       "comeback duration %u TU (%u ms)\n",
-		       sdata->name, tu, ms);
+		       sdata->name, mgmt->sa, tu, ms);
 		wk->timeout = jiffies + msecs_to_jiffies(ms);
 		if (ms > IEEE80211_ASSOC_TIMEOUT)
 			run_again(local, wk->timeout);
@@ -602,8 +602,8 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	}
 
 	if (status_code != WLAN_STATUS_SUCCESS)
-		printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
-		       sdata->name, status_code);
+		printk(KERN_DEBUG "%s: %pM denied association (code=%d)\n",
+		       sdata->name, mgmt->sa, status_code);
 	else
 		printk(KERN_DEBUG "%s: associated\n", sdata->name);
 



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 5/6 v2] mac80211: generalise work handling
  2009-12-02 11:43 ` [PATCH 5/6] mac80211: generalise work handling Johannes Berg
@ 2009-12-07 14:53   ` Johannes Berg
  0 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2009-12-07 14:53 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless

In order to use auth/assoc for different purposes
other than MLME, it needs to be split up. For other
purposes, a generic work handling (potentially on
another channel) will be useful.

To achieve that, this patch moves much of the MLME
work handling out of mlme into a new work API. The
API can currently handle probing a specific AP,
authentication and association. The MLME previously
handled probe/authentication as one step and will
continue to do so, but they are separate in the new
work handling.

Work items are RCU-managed to be able to check for
existence of an item for a specific frame in the RX
path, but they can be re-used which the MLME right
now will do for its combined probe/auth step.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
v2: fix locking/freeing in mgd_deauth()

 net/mac80211/Makefile         |    2 
 net/mac80211/debugfs_netdev.c |    2 
 net/mac80211/ieee80211_i.h    |   59 +-
 net/mac80211/iface.c          |   11 
 net/mac80211/main.c           |    2 
 net/mac80211/mlme.c           |  956 ++++++------------------------------------
 net/mac80211/rx.c             |    5 
 net/mac80211/scan.c           |    8 
 net/mac80211/work.c           |  902 +++++++++++++++++++++++++++++++++++++++
 9 files changed, 1112 insertions(+), 835 deletions(-)

--- wireless-testing.orig/net/mac80211/Makefile	2009-12-07 15:43:43.000000000 +0100
+++ wireless-testing/net/mac80211/Makefile	2009-12-07 15:49:31.000000000 +0100
@@ -9,7 +9,7 @@ mac80211-y := \
 	scan.o \
 	ht.o agg-tx.o agg-rx.o \
 	ibss.o \
-	mlme.o \
+	mlme.o work.o \
 	iface.o \
 	rate.o \
 	michael.o \
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2009-12-07 15:43:43.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h	2009-12-07 15:49:52.000000000 +0100
@@ -228,41 +228,63 @@ struct mesh_preq_queue {
 };
 
 enum ieee80211_work_type {
-	IEEE80211_WORK_AUTH_PROBE,
+	IEEE80211_WORK_DIRECT_PROBE,
 	IEEE80211_WORK_AUTH,
 	IEEE80211_WORK_ASSOC,
 };
 
+/**
+ * enum work_done_result - indicates what to do after work was done
+ *
+ * @WORK_DONE_DESTROY: This work item is no longer needed, destroy.
+ * @WORK_DONE_REQUEUE: This work item was reset to be reused, and
+ *	should be requeued.
+ */
+enum work_done_result {
+	WORK_DONE_DESTROY,
+	WORK_DONE_REQUEUE,
+};
+
 struct ieee80211_work {
 	struct list_head list;
 
+	struct rcu_head rcu_head;
+
+	struct ieee80211_sub_if_data *sdata;
+
+	enum work_done_result (*done)(struct ieee80211_work *wk,
+				      struct sk_buff *skb);
+
 	struct ieee80211_channel *chan;
 	/* XXX: chan type? -- right now not really needed */
+
 	unsigned long timeout;
 	enum ieee80211_work_type type;
 
+	u8 filter_ta[ETH_ALEN];
+
 	union {
 		struct {
 			int tries;
 			u16 algorithm, transaction;
 			u8 ssid[IEEE80211_MAX_SSID_LEN];
 			u8 ssid_len;
-			u8 bssid[ETH_ALEN];
 			u8 key[WLAN_KEY_LEN_WEP104];
 			u8 key_len, key_idx;
 			bool privacy;
-		} auth;
+		} probe_auth;
 		struct {
 			struct ieee80211_bss *bss;
 			const u8 *supp_rates;
 			const u8 *ht_information_ie;
+			enum ieee80211_smps_mode smps;
 			int tries;
 			u16 capability;
-			u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
+			u8 prev_bssid[ETH_ALEN];
 			u8 ssid[IEEE80211_MAX_SSID_LEN];
 			u8 ssid_len;
 			u8 supp_rates_len;
-			bool wmm_used;
+			bool wmm_used, use_11n;
 		} assoc;
 	};
 
@@ -276,17 +298,11 @@ enum ieee80211_sta_flags {
 	IEEE80211_STA_BEACON_POLL	= BIT(0),
 	IEEE80211_STA_CONNECTION_POLL	= BIT(1),
 	IEEE80211_STA_CONTROL_PORT	= BIT(2),
-	IEEE80211_STA_WMM_ENABLED	= BIT(3),
 	IEEE80211_STA_DISABLE_11N	= BIT(4),
 	IEEE80211_STA_CSA_RECEIVED	= BIT(5),
 	IEEE80211_STA_MFP_ENABLED	= BIT(6),
 };
 
-/* flags for MLME request */
-enum ieee80211_sta_request {
-	IEEE80211_STA_REQ_SCAN,
-};
-
 struct ieee80211_if_managed {
 	struct timer_list timer;
 	struct timer_list conn_mon_timer;
@@ -302,12 +318,10 @@ struct ieee80211_if_managed {
 
 	struct mutex mtx;
 	struct ieee80211_bss *associated;
-	struct list_head work_list;
 
 	u8 bssid[ETH_ALEN];
 
 	u16 aid;
-	u16 capab;
 
 	struct sk_buff_head skb_queue;
 
@@ -316,8 +330,6 @@ struct ieee80211_if_managed {
 	enum ieee80211_smps_mode req_smps, /* requested smps mode */
 				 ap_smps; /* smps mode AP thinks we're in */
 
-	unsigned long request;
-
 	unsigned int flags;
 
 	u32 beacon_crc;
@@ -584,6 +596,15 @@ struct ieee80211_local {
 	const struct ieee80211_ops *ops;
 
 	/*
+	 * work stuff, potentially off-channel (in the future)
+	 */
+	struct mutex work_mtx;
+	struct list_head work_list;
+	struct timer_list work_timer;
+	struct work_struct work_work;
+	struct sk_buff_head work_skb_queue;
+
+	/*
 	 * private workqueue to mac80211. mac80211 makes this accessible
 	 * via ieee80211_queue_work()
 	 */
@@ -1126,6 +1147,14 @@ int __ieee80211_request_smps(struct ieee
 void ieee80211_recalc_smps(struct ieee80211_local *local,
 			   struct ieee80211_sub_if_data *forsdata);
 
+/* internal work items */
+void ieee80211_work_init(struct ieee80211_local *local);
+void ieee80211_add_work(struct ieee80211_work *wk);
+void free_work(struct ieee80211_work *wk);
+void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata);
+ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
+					   struct sk_buff *skb);
+
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
 #else
--- wireless-testing.orig/net/mac80211/iface.c	2009-12-07 15:43:43.000000000 +0100
+++ wireless-testing/net/mac80211/iface.c	2009-12-07 15:44:16.000000000 +0100
@@ -362,6 +362,11 @@ static int ieee80211_stop(struct net_dev
 	netif_stop_queue(dev);
 
 	/*
+	 * Purge work for this interface.
+	 */
+	ieee80211_work_purge(sdata);
+
+	/*
 	 * Now delete all active aggregation sessions.
 	 */
 	rcu_read_lock();
@@ -937,6 +942,9 @@ u32 __ieee80211_recalc_idle(struct ieee8
 	struct ieee80211_sub_if_data *sdata;
 	int count = 0;
 
+	if (!list_empty(&local->work_list))
+		return ieee80211_idle_off(local, "working");
+
 	if (local->scanning)
 		return ieee80211_idle_off(local, "scanning");
 
@@ -945,8 +953,7 @@ u32 __ieee80211_recalc_idle(struct ieee8
 			continue;
 		/* do not count disabled managed interfaces */
 		if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-		    !sdata->u.mgd.associated &&
-		    list_empty(&sdata->u.mgd.work_list))
+		    !sdata->u.mgd.associated)
 			continue;
 		/* do not count unused IBSS interfaces */
 		if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
--- wireless-testing.orig/net/mac80211/mlme.c	2009-12-07 15:43:43.000000000 +0100
+++ wireless-testing/net/mac80211/mlme.c	2009-12-07 15:49:31.000000000 +0100
@@ -77,12 +77,6 @@ enum rx_mgmt_action {
 
 	/* caller must tell cfg80211 about internal error */
 	RX_MGMT_CFG80211_ASSOC_ERROR,
-
-	/* caller must call cfg80211_auth_timeout() & free work */
-	RX_MGMT_CFG80211_AUTH_TO,
-
-	/* caller must call cfg80211_assoc_timeout() & free work */
-	RX_MGMT_CFG80211_ASSOC_TO,
 };
 
 /* utils */
@@ -125,27 +119,6 @@ static int ecw2cw(int ecw)
 	return (1 << ecw) - 1;
 }
 
-static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
-				      struct ieee80211_supported_band *sband,
-				      u32 *rates)
-{
-	int i, j, count;
-	*rates = 0;
-	count = 0;
-	for (i = 0; i < supp_rates_len; i++) {
-		int rate = (supp_rates[i] & 0x7F) * 5;
-
-		for (j = 0; j < sband->n_bitrates; j++)
-			if (sband->bitrates[j].bitrate == rate) {
-				*rates |= BIT(j);
-				count++;
-				break;
-			}
-	}
-
-	return count;
-}
-
 /*
  * ieee80211_enable_ht should be called only after the operating band
  * has been determined as ht configuration depends on the hw's
@@ -231,266 +204,6 @@ static u32 ieee80211_enable_ht(struct ie
 
 /* frame sending functions */
 
-static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
-				 struct ieee80211_work *wk)
-{
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_local *local = sdata->local;
-	struct sk_buff *skb;
-	struct ieee80211_mgmt *mgmt;
-	u8 *pos;
-	const u8 *ies, *ht_ie;
-	int i, len, count, rates_len, supp_rates_len;
-	u16 capab;
-	int wmm = 0;
-	struct ieee80211_supported_band *sband;
-	u32 rates = 0;
-
-	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-			    sizeof(*mgmt) + 200 + wk->ie_len +
-			    wk->assoc.ssid_len);
-	if (!skb) {
-		printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
-		       "frame\n", sdata->name);
-		return;
-	}
-	skb_reserve(skb, local->hw.extra_tx_headroom);
-
-	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
-	capab = ifmgd->capab;
-
-	if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) {
-		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
-			capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
-		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
-			capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
-	}
-
-	if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY)
-		capab |= WLAN_CAPABILITY_PRIVACY;
-	if (wk->assoc.wmm_used)
-		wmm = 1;
-
-	/* get all rates supported by the device and the AP as
-	 * some APs don't like getting a superset of their rates
-	 * in the association request (e.g. D-Link DAP 1353 in
-	 * b-only mode) */
-	rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
-					       wk->assoc.supp_rates_len,
-					       sband, &rates);
-
-	if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
-	    (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
-		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
-
-	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
-	memset(mgmt, 0, 24);
-	memcpy(mgmt->da, wk->assoc.bssid, ETH_ALEN);
-	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-	memcpy(mgmt->bssid, wk->assoc.bssid, ETH_ALEN);
-
-	if (!is_zero_ether_addr(wk->assoc.prev_bssid)) {
-		skb_put(skb, 10);
-		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-						  IEEE80211_STYPE_REASSOC_REQ);
-		mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
-		mgmt->u.reassoc_req.listen_interval =
-				cpu_to_le16(local->hw.conf.listen_interval);
-		memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid,
-		       ETH_ALEN);
-	} else {
-		skb_put(skb, 4);
-		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-						  IEEE80211_STYPE_ASSOC_REQ);
-		mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
-		mgmt->u.assoc_req.listen_interval =
-				cpu_to_le16(local->hw.conf.listen_interval);
-	}
-
-	/* SSID */
-	ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len);
-	*pos++ = WLAN_EID_SSID;
-	*pos++ = wk->assoc.ssid_len;
-	memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len);
-
-	/* add all rates which were marked to be used above */
-	supp_rates_len = rates_len;
-	if (supp_rates_len > 8)
-		supp_rates_len = 8;
-
-	len = sband->n_bitrates;
-	pos = skb_put(skb, supp_rates_len + 2);
-	*pos++ = WLAN_EID_SUPP_RATES;
-	*pos++ = supp_rates_len;
-
-	count = 0;
-	for (i = 0; i < sband->n_bitrates; i++) {
-		if (BIT(i) & rates) {
-			int rate = sband->bitrates[i].bitrate;
-			*pos++ = (u8) (rate / 5);
-			if (++count == 8)
-				break;
-		}
-	}
-
-	if (rates_len > count) {
-		pos = skb_put(skb, rates_len - count + 2);
-		*pos++ = WLAN_EID_EXT_SUPP_RATES;
-		*pos++ = rates_len - count;
-
-		for (i++; i < sband->n_bitrates; i++) {
-			if (BIT(i) & rates) {
-				int rate = sband->bitrates[i].bitrate;
-				*pos++ = (u8) (rate / 5);
-			}
-		}
-	}
-
-	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
-		/* 1. power capabilities */
-		pos = skb_put(skb, 4);
-		*pos++ = WLAN_EID_PWR_CAPABILITY;
-		*pos++ = 2;
-		*pos++ = 0; /* min tx power */
-		*pos++ = local->hw.conf.channel->max_power; /* max tx power */
-
-		/* 2. supported channels */
-		/* TODO: get this in reg domain format */
-		pos = skb_put(skb, 2 * sband->n_channels + 2);
-		*pos++ = WLAN_EID_SUPPORTED_CHANNELS;
-		*pos++ = 2 * sband->n_channels;
-		for (i = 0; i < sband->n_channels; i++) {
-			*pos++ = ieee80211_frequency_to_channel(
-					sband->channels[i].center_freq);
-			*pos++ = 1; /* one channel in the subband*/
-		}
-	}
-
-	if (wk->ie_len && wk->ie) {
-		pos = skb_put(skb, wk->ie_len);
-		memcpy(pos, wk->ie, wk->ie_len);
-	}
-
-	if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) {
-		pos = skb_put(skb, 9);
-		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-		*pos++ = 7; /* len */
-		*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
-		*pos++ = 0x50;
-		*pos++ = 0xf2;
-		*pos++ = 2; /* WME */
-		*pos++ = 0; /* WME info */
-		*pos++ = 1; /* WME ver */
-		*pos++ = 0;
-	}
-
-	/* wmm support is a must to HT */
-	/*
-	 * IEEE802.11n does not allow TKIP/WEP as pairwise
-	 * ciphers in HT mode. We still associate in non-ht
-	 * mode (11a/b/g) if any one of these ciphers is
-	 * configured as pairwise.
-	 */
-	if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
-	    sband->ht_cap.ht_supported &&
-	    (ht_ie = wk->assoc.ht_information_ie) &&
-	    ht_ie[1] >= sizeof(struct ieee80211_ht_info) &&
-	    (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) {
-		struct ieee80211_ht_info *ht_info =
-			(struct ieee80211_ht_info *)(ht_ie + 2);
-		u16 cap = sband->ht_cap.cap;
-		__le16 tmp;
-		u32 flags = local->hw.conf.channel->flags;
-
-		/* determine capability flags */
-
-		if (ieee80211_disable_40mhz_24ghz &&
-		    sband->band == IEEE80211_BAND_2GHZ) {
-			cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-			cap &= ~IEEE80211_HT_CAP_SGI_40;
-		}
-
-		switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
-		case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-			if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
-				cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-				cap &= ~IEEE80211_HT_CAP_SGI_40;
-			}
-			break;
-		case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-			if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
-				cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-				cap &= ~IEEE80211_HT_CAP_SGI_40;
-			}
-			break;
-		}
-
-		/* set SM PS mode properly */
-		cap &= ~IEEE80211_HT_CAP_SM_PS;
-		/* new association always uses requested smps mode */
-		if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
-			if (ifmgd->powersave)
-				ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
-			else
-				ifmgd->ap_smps = IEEE80211_SMPS_OFF;
-		} else
-			ifmgd->ap_smps = ifmgd->req_smps;
-
-		switch (ifmgd->ap_smps) {
-		case IEEE80211_SMPS_AUTOMATIC:
-		case IEEE80211_SMPS_NUM_MODES:
-			WARN_ON(1);
-		case IEEE80211_SMPS_OFF:
-			cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
-				IEEE80211_HT_CAP_SM_PS_SHIFT;
-			break;
-		case IEEE80211_SMPS_STATIC:
-			cap |= WLAN_HT_CAP_SM_PS_STATIC <<
-				IEEE80211_HT_CAP_SM_PS_SHIFT;
-			break;
-		case IEEE80211_SMPS_DYNAMIC:
-			cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
-				IEEE80211_HT_CAP_SM_PS_SHIFT;
-			break;
-		}
-
-		/* reserve and fill IE */
-
-		pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
-		*pos++ = WLAN_EID_HT_CAPABILITY;
-		*pos++ = sizeof(struct ieee80211_ht_cap);
-		memset(pos, 0, sizeof(struct ieee80211_ht_cap));
-
-		/* capability flags */
-		tmp = cpu_to_le16(cap);
-		memcpy(pos, &tmp, sizeof(u16));
-		pos += sizeof(u16);
-
-		/* AMPDU parameters */
-		*pos++ = sband->ht_cap.ampdu_factor |
-			 (sband->ht_cap.ampdu_density <<
-			 	IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
-
-		/* MCS set */
-		memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
-		pos += sizeof(sband->ht_cap.mcs);
-
-		/* extended capabilities */
-		pos += sizeof(__le16);
-
-		/* BF capabilities */
-		pos += sizeof(__le32);
-
-		/* antenna selection */
-		pos += sizeof(u8);
-	}
-
-	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-	ieee80211_tx_skb(sdata, skb);
-}
-
-
 static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
 					   const u8 *bssid, u16 stype, u16 reason,
 					   void *cookie)
@@ -751,6 +464,11 @@ void ieee80211_recalc_ps(struct ieee8021
 		return;
 	}
 
+	if (!list_empty(&local->work_list)) {
+		local->ps_sdata = NULL;
+		goto change;
+	}
+
 	list_for_each_entry(sdata, &local->interfaces, list) {
 		if (!ieee80211_sdata_running(sdata))
 			continue;
@@ -761,7 +479,7 @@ void ieee80211_recalc_ps(struct ieee8021
 	}
 
 	if (count == 1 && found->u.mgd.powersave &&
-	    found->u.mgd.associated && list_empty(&found->u.mgd.work_list) &&
+	    found->u.mgd.associated &&
 	    !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
 				    IEEE80211_STA_CONNECTION_POLL))) {
 		s32 beaconint_us;
@@ -789,6 +507,7 @@ void ieee80211_recalc_ps(struct ieee8021
 		local->ps_sdata = NULL;
 	}
 
+ change:
 	ieee80211_change_ps(local);
 }
 
@@ -848,7 +567,7 @@ static void ieee80211_sta_wmm_params(str
 	int count;
 	u8 *pos;
 
-	if (!(ifmgd->flags & IEEE80211_STA_WMM_ENABLED))
+	if (local->hw.queues < 4)
 		return;
 
 	if (!wmm_param)
@@ -997,110 +716,6 @@ static void ieee80211_set_associated(str
 	netif_carrier_on(sdata->dev);
 }
 
-static void ieee80211_remove_auth_bss(struct ieee80211_local *local,
-				      struct ieee80211_work *wk)
-{
-	struct cfg80211_bss *cbss;
-	u16 capa_val = WLAN_CAPABILITY_ESS;
-
-	if (wk->auth.privacy)
-		capa_val |= WLAN_CAPABILITY_PRIVACY;
-
-	cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->auth.bssid,
-				wk->auth.ssid, wk->auth.ssid_len,
-				WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
-				capa_val);
-	if (!cbss)
-		return;
-
-	cfg80211_unlink_bss(local->hw.wiphy, cbss);
-	cfg80211_put_bss(cbss);
-}
-
-static enum rx_mgmt_action __must_check
-ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
-		       struct ieee80211_work *wk)
-{
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_local *local = sdata->local;
-
-	wk->auth.tries++;
-	if (wk->auth.tries > IEEE80211_AUTH_MAX_TRIES) {
-		printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
-		       sdata->name, wk->auth.bssid);
-
-		/*
-		 * Most likely AP is not in the range so remove the
-		 * bss struct for that AP.
-		 */
-		ieee80211_remove_auth_bss(local, wk);
-
-		/*
-		 * We might have a pending scan which had no chance to run yet
-		 * due to work needing to be done. Hence, queue the STAs work
-		 * again for that.
-		 */
-		ieee80211_queue_work(&local->hw, &ifmgd->work);
-		return RX_MGMT_CFG80211_AUTH_TO;
-	}
-
-	printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
-			sdata->name, wk->auth.bssid, wk->auth.tries);
-
-	/*
-	 * Direct probe is sent to broadcast address as some APs
-	 * will not answer to direct packet in unassociated state.
-	 */
-	ieee80211_send_probe_req(sdata, NULL, wk->auth.ssid, wk->auth.ssid_len,
-				 NULL, 0);
-
-	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
-	run_again(ifmgd, wk->timeout);
-
-	return RX_MGMT_NONE;
-}
-
-
-static enum rx_mgmt_action __must_check
-ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
-		       struct ieee80211_work *wk)
-{
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_local *local = sdata->local;
-
-	wk->auth.tries++;
-	if (wk->auth.tries > IEEE80211_AUTH_MAX_TRIES) {
-		printk(KERN_DEBUG "%s: authentication with AP %pM"
-		       " timed out\n", sdata->name, wk->auth.bssid);
-
-		/*
-		 * Most likely AP is not in the range so remove the
-		 * bss struct for that AP.
-		 */
-		ieee80211_remove_auth_bss(local, wk);
-
-		/*
-		 * We might have a pending scan which had no chance to run yet
-		 * due to work needing to be done. Hence, queue the STAs work
-		 * again for that.
-		 */
-		ieee80211_queue_work(&local->hw, &ifmgd->work);
-		return RX_MGMT_CFG80211_AUTH_TO;
-	}
-
-	printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
-	       sdata->name, wk->auth.bssid, wk->auth.tries);
-
-	ieee80211_send_auth(sdata, 1, wk->auth.algorithm, wk->ie, wk->ie_len,
-			    wk->auth.bssid, NULL, 0, 0);
-	wk->auth.transaction = 2;
-
-	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
-	run_again(ifmgd, wk->timeout);
-
-	return RX_MGMT_NONE;
-}
-
 static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -1187,44 +802,6 @@ static void ieee80211_set_disassoc(struc
 	sta_info_destroy(sta);
 }
 
-static enum rx_mgmt_action __must_check
-ieee80211_associate(struct ieee80211_sub_if_data *sdata,
-		    struct ieee80211_work *wk)
-{
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct ieee80211_local *local = sdata->local;
-
-	wk->assoc.tries++;
-	if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) {
-		printk(KERN_DEBUG "%s: association with AP %pM"
-		       " timed out\n",
-		       sdata->name, wk->assoc.bssid);
-
-		/*
-		 * Most likely AP is not in the range so remove the
-		 * bss struct for that AP.
-		 */
-		cfg80211_unlink_bss(local->hw.wiphy, &wk->assoc.bss->cbss);
-
-		/*
-		 * We might have a pending scan which had no chance to run yet
-		 * due to work needing to be done. Hence, queue the STAs work
-		 * again for that.
-		 */
-		ieee80211_queue_work(&local->hw, &ifmgd->work);
-		return RX_MGMT_CFG80211_ASSOC_TO;
-	}
-
-	printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
-	       sdata->name, wk->assoc.bssid, wk->assoc.tries);
-	ieee80211_send_assoc(sdata, wk);
-
-	wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
-	run_again(ifmgd, wk->timeout);
-
-	return RX_MGMT_NONE;
-}
-
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
 			     struct ieee80211_hdr *hdr)
 {
@@ -1330,86 +907,6 @@ void ieee80211_beacon_loss(struct ieee80
 }
 EXPORT_SYMBOL(ieee80211_beacon_loss);
 
-static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
-				     struct ieee80211_work *wk)
-{
-	list_del(&wk->list);
-	kfree(wk);
-	printk(KERN_DEBUG "%s: authenticated\n", sdata->name);
-}
-
-
-static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
-				     struct ieee80211_work *wk,
-				     struct ieee80211_mgmt *mgmt,
-				     size_t len)
-{
-	u8 *pos;
-	struct ieee802_11_elems elems;
-
-	pos = mgmt->u.auth.variable;
-	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
-	if (!elems.challenge)
-		return;
-	ieee80211_send_auth(sdata, 3, wk->auth.algorithm,
-			    elems.challenge - 2, elems.challenge_len + 2,
-			    wk->auth.bssid, wk->auth.key, wk->auth.key_len,
-			    wk->auth.key_idx);
-	wk->auth.transaction = 4;
-}
-
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
-		       struct ieee80211_work *wk,
-		       struct ieee80211_mgmt *mgmt, size_t len)
-{
-	u16 auth_alg, auth_transaction, status_code;
-
-	if (wk->type != IEEE80211_WORK_AUTH)
-		return RX_MGMT_NONE;
-
-	if (len < 24 + 6)
-		return RX_MGMT_NONE;
-
-	if (memcmp(wk->auth.bssid, mgmt->sa, ETH_ALEN) != 0)
-		return RX_MGMT_NONE;
-
-	if (memcmp(wk->auth.bssid, mgmt->bssid, ETH_ALEN) != 0)
-		return RX_MGMT_NONE;
-
-	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
-	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
-	status_code = le16_to_cpu(mgmt->u.auth.status_code);
-
-	if (auth_alg != wk->auth.algorithm ||
-	    auth_transaction != wk->auth.transaction)
-		return RX_MGMT_NONE;
-
-	if (status_code != WLAN_STATUS_SUCCESS) {
-		list_del(&wk->list);
-		kfree(wk);
-		return RX_MGMT_CFG80211_AUTH;
-	}
-
-	switch (wk->auth.algorithm) {
-	case WLAN_AUTH_OPEN:
-	case WLAN_AUTH_LEAP:
-	case WLAN_AUTH_FT:
-		ieee80211_auth_completed(sdata, wk);
-		return RX_MGMT_CFG80211_AUTH;
-	case WLAN_AUTH_SHARED_KEY:
-		if (wk->auth.transaction == 4) {
-			ieee80211_auth_completed(sdata, wk);
-			return RX_MGMT_CFG80211_AUTH;
-		} else
-			ieee80211_auth_challenge(sdata, wk, mgmt, len);
-		break;
-	}
-
-	return RX_MGMT_NONE;
-}
-
-
 static enum rx_mgmt_action __must_check
 ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 			 struct ieee80211_mgmt *mgmt, size_t len)
@@ -1466,98 +963,51 @@ ieee80211_rx_mgmt_disassoc(struct ieee80
 }
 
 
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
-			     struct ieee80211_work *wk,
-			     struct ieee80211_mgmt *mgmt, size_t len,
-			     bool reassoc)
+static bool ieee80211_assoc_success(struct ieee80211_work *wk,
+				    struct ieee80211_mgmt *mgmt, size_t len)
 {
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_supported_band *sband;
 	struct sta_info *sta;
 	struct ieee80211_bss *bss = wk->assoc.bss;
+	u8 *pos;
 	u32 rates, basic_rates;
-	u16 capab_info, status_code, aid;
+	u16 capab_info, aid;
 	struct ieee802_11_elems elems;
 	struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
-	u8 *pos;
 	u32 changed = 0;
 	int i, j, err;
 	bool have_higher_than_11mbit = false;
 	u16 ap_ht_cap_flags;
 
-	/*
-	 * AssocResp and ReassocResp have identical structure, so process both
-	 * of them in this function.
-	 */
+	/* AssocResp and ReassocResp have identical structure */
 
-	if (len < 24 + 6)
-		return RX_MGMT_NONE;
-
-	if (memcmp(bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
-		return RX_MGMT_NONE;
-
-	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
-	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
 	aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
-
-	printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
-	       "status=%d aid=%d)\n",
-	       sdata->name, reassoc ? "Rea" : "A", mgmt->sa,
-	       capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
-
-	pos = mgmt->u.assoc_resp.variable;
-	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
-
-	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
-	    elems.timeout_int && elems.timeout_int_len == 5 &&
-	    elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
-		u32 tu, ms;
-		tu = get_unaligned_le32(elems.timeout_int + 1);
-		ms = tu * 1024 / 1000;
-		printk(KERN_DEBUG "%s: AP rejected association temporarily; "
-		       "comeback duration %u TU (%u ms)\n",
-		       sdata->name, tu, ms);
-		wk->timeout = jiffies + msecs_to_jiffies(ms);
-		if (ms > IEEE80211_ASSOC_TIMEOUT)
-			run_again(ifmgd, jiffies + msecs_to_jiffies(ms));
-		return RX_MGMT_NONE;
-	}
-
-	/*
-	 * Here the association was either successful or not.
-	 */
-
-	/* delete work item -- must be before set_associated for PS */
-	list_del(&wk->list);
-	kfree(wk);
-
-	if (status_code != WLAN_STATUS_SUCCESS) {
-		printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
-		       sdata->name, status_code);
-		return RX_MGMT_CFG80211_ASSOC;
-	}
+	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
 
 	if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
 		printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
 		       "set\n", sdata->name, aid);
 	aid &= ~(BIT(15) | BIT(14));
 
+	pos = mgmt->u.assoc_resp.variable;
+	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+
 	if (!elems.supp_rates) {
 		printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n",
 		       sdata->name);
-		return RX_MGMT_NONE;
+		return false;
 	}
 
-	printk(KERN_DEBUG "%s: associated\n", sdata->name);
 	ifmgd->aid = aid;
 
 	sta = sta_info_alloc(sdata, bss->cbss.bssid, GFP_KERNEL);
 	if (!sta) {
 		printk(KERN_DEBUG "%s: failed to alloc STA entry for"
 		       " the AP\n", sdata->name);
-		return RX_MGMT_CFG80211_ASSOC_ERROR;
+		return false;
 	}
 
 	set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC |
@@ -1642,7 +1092,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 		ieee80211_set_wmm_default(sdata);
 
 	if (elems.ht_info_elem && elems.wmm_param &&
-	    (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
+	    (sdata->local->hw.queues >= 4) &&
 	    !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
 		changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
 					       bss->cbss.bssid,
@@ -1661,7 +1111,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 	ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
 	mod_beacon_timer(sdata);
 
-	return RX_MGMT_CFG80211_ASSOC;
+	return true;
 }
 
 
@@ -1706,12 +1156,12 @@ static void ieee80211_rx_bss_info(struct
 
 
 static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
-					 struct ieee80211_work *wk,
-					 struct ieee80211_mgmt *mgmt, size_t len,
-					 struct ieee80211_rx_status *rx_status)
+					 struct sk_buff *skb)
 {
+	struct ieee80211_mgmt *mgmt = (void *)skb->data;
 	struct ieee80211_if_managed *ifmgd;
-	size_t baselen;
+	struct ieee80211_rx_status *rx_status = (void *) skb->cb;
+	size_t baselen, len = skb->len;
 	struct ieee802_11_elems elems;
 
 	ifmgd = &sdata->u.mgd;
@@ -1730,15 +1180,6 @@ static void ieee80211_rx_mgmt_probe_resp
 
 	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
 
-	/* direct probe may be part of the association flow */
-	if (wk && wk->type == IEEE80211_WORK_AUTH_PROBE) {
-		printk(KERN_DEBUG "%s: direct probe responded\n",
-		       sdata->name);
-		wk->auth.tries = 0;
-		wk->type = IEEE80211_WORK_AUTH;
-		WARN_ON(ieee80211_authenticate(sdata, wk) != RX_MGMT_NONE);
-	}
-
 	if (ifmgd->associated &&
 	    memcmp(mgmt->bssid, ifmgd->associated->cbss.bssid, ETH_ALEN) == 0 &&
 	    ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
@@ -1952,9 +1393,6 @@ ieee80211_rx_result ieee80211_sta_rx_mgm
 	switch (fc & IEEE80211_FCTL_STYPE) {
 	case IEEE80211_STYPE_PROBE_RESP:
 	case IEEE80211_STYPE_BEACON:
-	case IEEE80211_STYPE_AUTH:
-	case IEEE80211_STYPE_ASSOC_RESP:
-	case IEEE80211_STYPE_REASSOC_RESP:
 	case IEEE80211_STYPE_DEAUTH:
 	case IEEE80211_STYPE_DISASSOC:
 	case IEEE80211_STYPE_ACTION:
@@ -1972,7 +1410,6 @@ static void ieee80211_sta_rx_queued_mgmt
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_mgmt *mgmt;
-	struct ieee80211_work *wk;
 	enum rx_mgmt_action rma = RX_MGMT_NONE;
 	u16 fc;
 
@@ -1991,8 +1428,7 @@ static void ieee80211_sta_rx_queued_mgmt
 						 rx_status);
 			break;
 		case IEEE80211_STYPE_PROBE_RESP:
-			ieee80211_rx_mgmt_probe_resp(sdata, NULL, mgmt,
-						     skb->len, rx_status);
+			ieee80211_rx_mgmt_probe_resp(sdata, skb);
 			break;
 		case IEEE80211_STYPE_DEAUTH:
 			rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
@@ -2025,88 +1461,11 @@ static void ieee80211_sta_rx_queued_mgmt
 		goto out;
 	}
 
-	list_for_each_entry(wk, &ifmgd->work_list, list) {
-		const u8 *bssid = NULL;
-
-		switch (wk->type) {
-		case IEEE80211_WORK_AUTH_PROBE:
-		case IEEE80211_WORK_AUTH:
-			bssid = wk->auth.bssid;
-			break;
-		case IEEE80211_WORK_ASSOC:
-			bssid = wk->assoc.bssid;
-			break;
-		default:
-			continue;
-		}
-		if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
-			continue;
-
-		switch (fc & IEEE80211_FCTL_STYPE) {
-		case IEEE80211_STYPE_PROBE_RESP:
-			ieee80211_rx_mgmt_probe_resp(sdata, wk, mgmt, skb->len,
-						     rx_status);
-			break;
-		case IEEE80211_STYPE_AUTH:
-			rma = ieee80211_rx_mgmt_auth(sdata, wk, mgmt, skb->len);
-			break;
-		case IEEE80211_STYPE_ASSOC_RESP:
-			rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt,
-							   skb->len, false);
-			break;
-		case IEEE80211_STYPE_REASSOC_RESP:
-			rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt,
-							   skb->len, true);
-			break;
-		case IEEE80211_STYPE_DEAUTH:
-			if (skb->len >= 24 + 2 /* mgmt + deauth reason */) {
-				/*
-				 * We get here if we get deauth while
-				 * trying to auth/assoc. Telling cfg80211
-				 * is handled below, unconditionally.
-				 */
-				list_del(&wk->list);
-				kfree(wk);
-			}
-			break;
-		}
-		/*
-		 * We've processed this frame for that work, so it can't
-		 * belong to another work struct.
-		 * NB: this is also required for correctness because the
-		 * called functions can free 'wk', and for 'rma'!
-		 */
-		break;
-	}
-
 	mutex_unlock(&ifmgd->mtx);
 
 	if (skb->len >= 24 + 2 /* mgmt + deauth reason */ &&
-	    (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH) {
-		WARN_ON(rma != RX_MGMT_NONE);
-		rma = RX_MGMT_CFG80211_DEAUTH;
-	}
-
-	switch (rma) {
-	case RX_MGMT_NONE:
-		/* no action */
-		break;
-	case RX_MGMT_CFG80211_AUTH:
-		cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, skb->len);
-		break;
-	case RX_MGMT_CFG80211_ASSOC:
-		cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len);
-		break;
-	case RX_MGMT_CFG80211_DEAUTH:
+	    (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH)
 		cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
-		break;
-	case RX_MGMT_CFG80211_ASSOC_ERROR:
-		/* an internal error -- pretend timeout for now */
-		cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
-		break;
-	default:
-		WARN(1, "unexpected: %d", rma);
-	}
 
  out:
 	kfree_skb(skb);
@@ -2134,9 +1493,6 @@ static void ieee80211_sta_work(struct wo
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd;
 	struct sk_buff *skb;
-	struct ieee80211_work *wk, *tmp;
-	LIST_HEAD(free_work);
-	enum rx_mgmt_action rma;
 
 	if (!ieee80211_sdata_running(sdata))
 		return;
@@ -2206,84 +1562,7 @@ static void ieee80211_sta_work(struct wo
 		}
 	}
 
-
-	ieee80211_recalc_idle(local);
-
-	list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) {
-		if (time_is_after_jiffies(wk->timeout)) {
-			/*
-			 * This work item isn't supposed to be worked on
-			 * right now, but take care to adjust the timer
-			 * properly.
-			 */
-			run_again(ifmgd, wk->timeout);
-			continue;
-		}
-
-		switch (wk->type) {
-		default:
-			WARN_ON(1);
-			/* nothing */
-			rma = RX_MGMT_NONE;
-			break;
-		case IEEE80211_WORK_AUTH_PROBE:
-			rma = ieee80211_direct_probe(sdata, wk);
-			break;
-		case IEEE80211_WORK_AUTH:
-			rma = ieee80211_authenticate(sdata, wk);
-			break;
-		case IEEE80211_WORK_ASSOC:
-			rma = ieee80211_associate(sdata, wk);
-			break;
-		}
-
-		switch (rma) {
-		case RX_MGMT_NONE:
-			/* no action required */
-			break;
-		case RX_MGMT_CFG80211_AUTH_TO:
-		case RX_MGMT_CFG80211_ASSOC_TO:
-			list_del(&wk->list);
-			list_add(&wk->list, &free_work);
-			/*
-			 * small abuse but only local -- keep the
-			 * action type in wk->timeout while the item
-			 * is on the cleanup list
-			 */
-			wk->timeout = rma;
-			break;
-		default:
-			WARN(1, "unexpected: %d", rma);
-		}
-	}
-
-	if (list_empty(&ifmgd->work_list) &&
-	    test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request))
-		ieee80211_queue_delayed_work(&local->hw,
-					     &local->scan_work,
-					     round_jiffies_relative(0));
-
 	mutex_unlock(&ifmgd->mtx);
-
-	list_for_each_entry_safe(wk, tmp, &free_work, list) {
-		/* see above how we're using wk->timeout */
-		switch (wk->timeout) {
-		case RX_MGMT_CFG80211_AUTH_TO:
-			cfg80211_send_auth_timeout(sdata->dev, wk->auth.bssid);
-			break;
-		case RX_MGMT_CFG80211_ASSOC_TO:
-			cfg80211_send_assoc_timeout(sdata->dev,
-						    wk->assoc.bssid);
-			break;
-		default:
-			WARN(1, "unexpected: %lu", wk->timeout);
-		}
-
-		list_del(&wk->list);
-		kfree(wk);
-	}
-
-	ieee80211_recalc_idle(local);
 }
 
 static void ieee80211_sta_bcn_mon_timer(unsigned long data)
@@ -2392,12 +1671,7 @@ void ieee80211_sta_setup_sdata(struct ie
 		    (unsigned long) sdata);
 	skb_queue_head_init(&ifmgd->skb_queue);
 
-	INIT_LIST_HEAD(&ifmgd->work_list);
-
-	ifmgd->capab = WLAN_CAPABILITY_ESS;
 	ifmgd->flags = 0;
-	if (sdata->local->hw.queues >= 4)
-		ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
 
 	mutex_init(&ifmgd->mtx);
 
@@ -2435,10 +1709,32 @@ int ieee80211_max_network_latency(struct
 }
 
 /* config hooks */
+static enum work_done_result
+ieee80211_probe_auth_done(struct ieee80211_work *wk,
+			  struct sk_buff *skb)
+{
+	if (!skb) {
+		cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta);
+		return WORK_DONE_DESTROY;
+	}
+
+	if (wk->type == IEEE80211_WORK_AUTH) {
+		cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len);
+		return WORK_DONE_DESTROY;
+	}
+
+	mutex_lock(&wk->sdata->u.mgd.mtx);
+	ieee80211_rx_mgmt_probe_resp(wk->sdata, skb);
+	mutex_unlock(&wk->sdata->u.mgd.mtx);
+
+	wk->type = IEEE80211_WORK_AUTH;
+	wk->probe_auth.tries = 0;
+	return WORK_DONE_REQUEUE;
+}
+
 int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
 		       struct cfg80211_auth_request *req)
 {
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	const u8 *ssid;
 	struct ieee80211_work *wk;
 	u16 auth_alg;
@@ -2464,7 +1760,7 @@ int ieee80211_mgd_auth(struct ieee80211_
 	if (!wk)
 		return -ENOMEM;
 
-	memcpy(wk->auth.bssid, req->bss->bssid, ETH_ALEN);;
+	memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);;
 
 	if (req->ie && req->ie_len) {
 		memcpy(wk->ie, req->ie, req->ie_len);
@@ -2472,21 +1768,22 @@ int ieee80211_mgd_auth(struct ieee80211_
 	}
 
 	if (req->key && req->key_len) {
-		wk->auth.key_len = req->key_len;
-		wk->auth.key_idx = req->key_idx;
-		memcpy(wk->auth.key, req->key, req->key_len);
+		wk->probe_auth.key_len = req->key_len;
+		wk->probe_auth.key_idx = req->key_idx;
+		memcpy(wk->probe_auth.key, req->key, req->key_len);
 	}
 
 	ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
-	memcpy(wk->auth.ssid, ssid + 2, ssid[1]);
-	wk->auth.ssid_len = ssid[1];
+	memcpy(wk->probe_auth.ssid, ssid + 2, ssid[1]);
+	wk->probe_auth.ssid_len = ssid[1];
 
-	wk->auth.algorithm = auth_alg;
-	wk->auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
+	wk->probe_auth.algorithm = auth_alg;
+	wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
 
-	wk->type = IEEE80211_WORK_AUTH_PROBE;
-	wk->timeout = jiffies; /* run right away */
+	wk->type = IEEE80211_WORK_DIRECT_PROBE;
 	wk->chan = req->bss->channel;
+	wk->sdata = sdata;
+	wk->done = ieee80211_probe_auth_done;
 
 	/*
 	 * XXX: if still associated need to tell AP that we're going
@@ -2497,29 +1794,58 @@ int ieee80211_mgd_auth(struct ieee80211_
 	sdata->local->oper_channel = req->bss->channel;
 	ieee80211_hw_config(sdata->local, 0);
 
-	mutex_lock(&ifmgd->mtx);
-	list_add(&wk->list, &sdata->u.mgd.work_list);
-	mutex_unlock(&ifmgd->mtx);
-
-	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work);
+	ieee80211_add_work(wk);
 	return 0;
 }
 
+static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
+						  struct sk_buff *skb)
+{
+	struct ieee80211_mgmt *mgmt;
+	u16 status;
+
+	if (!skb) {
+		cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
+		return WORK_DONE_DESTROY;
+	}
+
+	mgmt = (void *)skb->data;
+	status = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+
+	if (status == WLAN_STATUS_SUCCESS) {
+		mutex_lock(&wk->sdata->u.mgd.mtx);
+		if (!ieee80211_assoc_success(wk, mgmt, skb->len)) {
+			mutex_unlock(&wk->sdata->u.mgd.mtx);
+			/* oops -- internal error -- send timeout for now */
+			cfg80211_send_assoc_timeout(wk->sdata->dev,
+						    wk->filter_ta);
+			return WORK_DONE_DESTROY;
+		}
+		mutex_unlock(&wk->sdata->u.mgd.mtx);
+	}
+
+	cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len);
+	return WORK_DONE_DESTROY;
+}
+
 int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 			struct cfg80211_assoc_request *req)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_work *wk;
 	const u8 *ssid;
-	int i, err;
+	int i;
 
 	mutex_lock(&ifmgd->mtx);
+	if (ifmgd->associated) {
+		mutex_unlock(&ifmgd->mtx);
+		return -EALREADY;
+	}
+	mutex_unlock(&ifmgd->mtx);
 
 	wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
-	if (!wk) {
-		err = -ENOMEM;
-		goto out;
-	}
+	if (!wk)
+		return -ENOMEM;
 
 	ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
 
@@ -2538,8 +1864,19 @@ int ieee80211_mgd_assoc(struct ieee80211
 
 	wk->assoc.bss = (void *)req->bss;
 
-	memcpy(wk->assoc.bssid, req->bss->bssid, ETH_ALEN);
+	memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);
+
+	/* new association always uses requested smps mode */
+	if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
+		if (ifmgd->powersave)
+			ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
+		else
+			ifmgd->ap_smps = IEEE80211_SMPS_OFF;
+	} else
+		ifmgd->ap_smps = ifmgd->req_smps;
 
+	wk->assoc.smps = ifmgd->ap_smps;
+	wk->assoc.use_11n = !(ifmgd->flags & IEEE80211_STA_DISABLE_11N);
 	wk->assoc.capability = req->bss->capability;
 	wk->assoc.wmm_used = wk->assoc.bss->wmm_used;
 	wk->assoc.supp_rates = wk->assoc.bss->supp_rates;
@@ -2555,8 +1892,9 @@ int ieee80211_mgd_assoc(struct ieee80211
 		memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN);
 
 	wk->type = IEEE80211_WORK_ASSOC;
-	wk->timeout = jiffies; /* run right away */
 	wk->chan = req->bss->channel;
+	wk->sdata = sdata;
+	wk->done = ieee80211_assoc_done;
 
 	if (req->use_mfp) {
 		ifmgd->mfp = IEEE80211_MFP_REQUIRED;
@@ -2574,56 +1912,56 @@ int ieee80211_mgd_assoc(struct ieee80211
 	sdata->local->oper_channel = req->bss->channel;
 	ieee80211_hw_config(sdata->local, 0);
 
-	list_add(&wk->list, &ifmgd->work_list);
-	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work);
-
-	err = 0;
-
- out:
-	mutex_unlock(&ifmgd->mtx);
-	return err;
+	ieee80211_add_work(wk);
+	return 0;
 }
 
 int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 			 struct cfg80211_deauth_request *req,
 			 void *cookie)
 {
+	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_work *wk;
 	const u8 *bssid = req->bss->bssid;
-	bool not_auth_yet = false;
 
 	mutex_lock(&ifmgd->mtx);
 
 	if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) {
 		bssid = req->bss->bssid;
 		ieee80211_set_disassoc(sdata);
-	} else list_for_each_entry(wk, &ifmgd->work_list, list) {
-		if (wk->type != IEEE80211_WORK_AUTH_PROBE)
-			continue;
-		if (memcmp(req->bss->bssid, wk->auth.bssid, ETH_ALEN))
-			continue;
-		not_auth_yet = true;
-		list_del(&wk->list);
-		kfree(wk);
-		break;
-	}
+		mutex_unlock(&ifmgd->mtx);
+	} else {
+		bool not_auth_yet = false;
 
-	/*
-	 * If somebody requests authentication and we haven't
-	 * sent out an auth frame yet there's no need to send
-	 * out a deauth frame either. If the state was PROBE,
-	 * then this is the case. If it's AUTH we have sent a
-	 * frame, and if it's IDLE we have completed the auth
-	 * process already.
-	 */
-	if (not_auth_yet) {
 		mutex_unlock(&ifmgd->mtx);
-		__cfg80211_auth_canceled(sdata->dev, bssid);
-		return 0;
-	}
 
-	mutex_unlock(&ifmgd->mtx);
+		mutex_lock(&local->work_mtx);
+		list_for_each_entry(wk, &local->work_list, list) {
+			if (wk->type != IEEE80211_WORK_DIRECT_PROBE)
+				continue;
+			if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
+				continue;
+			not_auth_yet = true;
+			list_del(&wk->list);
+			free_work(wk);
+			break;
+		}
+		mutex_unlock(&local->work_mtx);
+
+		/*
+		 * If somebody requests authentication and we haven't
+		 * sent out an auth frame yet there's no need to send
+		 * out a deauth frame either. If the state was PROBE,
+		 * then this is the case. If it's AUTH we have sent a
+		 * frame, and if it's IDLE we have completed the auth
+		 * process already.
+		 */
+		if (not_auth_yet) {
+			__cfg80211_auth_canceled(sdata->dev, bssid);
+			return 0;
+		}
+	}
 
 	printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
 	       sdata->name, bssid, req->reason_code);
--- wireless-testing.orig/net/mac80211/scan.c	2009-12-07 15:43:43.000000000 +0100
+++ wireless-testing/net/mac80211/scan.c	2009-12-07 15:49:31.000000000 +0100
@@ -422,7 +422,6 @@ static int __ieee80211_start_scan(struct
 				  struct cfg80211_scan_request *req)
 {
 	struct ieee80211_local *local = sdata->local;
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	int rc;
 
 	if (local->scan_req)
@@ -452,11 +451,8 @@ static int __ieee80211_start_scan(struct
 	local->scan_req = req;
 	local->scan_sdata = sdata;
 
-	if (req != local->int_scan_req &&
-	    sdata->vif.type == NL80211_IFTYPE_STATION &&
-	    !list_empty(&ifmgd->work_list)) {
-		/* actually wait for the work it's doing to finish/time out */
-		set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
+	if (!list_empty(&local->work_list)) {
+		/* wait for the work to finish/time out */
 		return 0;
 	}
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/mac80211/work.c	2009-12-07 15:49:31.000000000 +0100
@@ -0,0 +1,902 @@
+/*
+ * mac80211 work implementation
+ *
+ * Copyright 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright 2004, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "ieee80211_i.h"
+#include "rate.h"
+
+#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
+#define IEEE80211_AUTH_MAX_TRIES 3
+#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
+#define IEEE80211_ASSOC_MAX_TRIES 3
+#define IEEE80211_MAX_PROBE_TRIES 5
+
+enum work_action {
+	WORK_ACT_NONE,
+	WORK_ACT_TIMEOUT,
+	WORK_ACT_DONE,
+};
+
+
+/* utils */
+static inline void ASSERT_WORK_MTX(struct ieee80211_local *local)
+{
+	WARN_ON(!mutex_is_locked(&local->work_mtx));
+}
+
+/*
+ * We can have multiple work items (and connection probing)
+ * scheduling this timer, but we need to take care to only
+ * reschedule it when it should fire _earlier_ than it was
+ * asked for before, or if it's not pending right now. This
+ * function ensures that. Note that it then is required to
+ * run this function for all timeouts after the first one
+ * has happened -- the work that runs from this timer will
+ * do that.
+ */
+static void run_again(struct ieee80211_local *local,
+		      unsigned long timeout)
+{
+	ASSERT_WORK_MTX(local);
+
+	if (!timer_pending(&local->work_timer) ||
+	    time_before(timeout, local->work_timer.expires))
+		mod_timer(&local->work_timer, timeout);
+}
+
+static void work_free_rcu(struct rcu_head *head)
+{
+	struct ieee80211_work *wk =
+		container_of(head, struct ieee80211_work, rcu_head);
+
+	kfree(wk);
+}
+
+void free_work(struct ieee80211_work *wk)
+{
+	call_rcu(&wk->rcu_head, work_free_rcu);
+}
+
+static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
+				      struct ieee80211_supported_band *sband,
+				      u32 *rates)
+{
+	int i, j, count;
+	*rates = 0;
+	count = 0;
+	for (i = 0; i < supp_rates_len; i++) {
+		int rate = (supp_rates[i] & 0x7F) * 5;
+
+		for (j = 0; j < sband->n_bitrates; j++)
+			if (sband->bitrates[j].bitrate == rate) {
+				*rates |= BIT(j);
+				count++;
+				break;
+			}
+	}
+
+	return count;
+}
+
+/* frame sending functions */
+
+static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
+				 struct ieee80211_work *wk)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct sk_buff *skb;
+	struct ieee80211_mgmt *mgmt;
+	u8 *pos;
+	const u8 *ies, *ht_ie;
+	int i, len, count, rates_len, supp_rates_len;
+	u16 capab;
+	struct ieee80211_supported_band *sband;
+	u32 rates = 0;
+
+	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+			    sizeof(*mgmt) + 200 + wk->ie_len +
+			    wk->assoc.ssid_len);
+	if (!skb) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
+		       "frame\n", sdata->name);
+		return;
+	}
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+
+	sband = local->hw.wiphy->bands[wk->chan->band];
+
+	capab = WLAN_CAPABILITY_ESS;
+
+	if (sband->band == IEEE80211_BAND_2GHZ) {
+		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
+			capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
+			capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+	}
+
+	if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY)
+		capab |= WLAN_CAPABILITY_PRIVACY;
+
+	/*
+	 * Get all rates supported by the device and the AP as
+	 * some APs don't like getting a superset of their rates
+	 * in the association request (e.g. D-Link DAP 1353 in
+	 * b-only mode)...
+	 */
+	rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
+					       wk->assoc.supp_rates_len,
+					       sband, &rates);
+
+	if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
+	    (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
+		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+
+	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+	memset(mgmt, 0, 24);
+	memcpy(mgmt->da, wk->filter_ta, ETH_ALEN);
+	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+	memcpy(mgmt->bssid, wk->filter_ta, ETH_ALEN);
+
+	if (!is_zero_ether_addr(wk->assoc.prev_bssid)) {
+		skb_put(skb, 10);
+		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+						  IEEE80211_STYPE_REASSOC_REQ);
+		mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
+		mgmt->u.reassoc_req.listen_interval =
+				cpu_to_le16(local->hw.conf.listen_interval);
+		memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid,
+		       ETH_ALEN);
+	} else {
+		skb_put(skb, 4);
+		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+						  IEEE80211_STYPE_ASSOC_REQ);
+		mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
+		mgmt->u.assoc_req.listen_interval =
+				cpu_to_le16(local->hw.conf.listen_interval);
+	}
+
+	/* SSID */
+	ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len);
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = wk->assoc.ssid_len;
+	memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len);
+
+	/* add all rates which were marked to be used above */
+	supp_rates_len = rates_len;
+	if (supp_rates_len > 8)
+		supp_rates_len = 8;
+
+	len = sband->n_bitrates;
+	pos = skb_put(skb, supp_rates_len + 2);
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = supp_rates_len;
+
+	count = 0;
+	for (i = 0; i < sband->n_bitrates; i++) {
+		if (BIT(i) & rates) {
+			int rate = sband->bitrates[i].bitrate;
+			*pos++ = (u8) (rate / 5);
+			if (++count == 8)
+				break;
+		}
+	}
+
+	if (rates_len > count) {
+		pos = skb_put(skb, rates_len - count + 2);
+		*pos++ = WLAN_EID_EXT_SUPP_RATES;
+		*pos++ = rates_len - count;
+
+		for (i++; i < sband->n_bitrates; i++) {
+			if (BIT(i) & rates) {
+				int rate = sband->bitrates[i].bitrate;
+				*pos++ = (u8) (rate / 5);
+			}
+		}
+	}
+
+	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
+		/* 1. power capabilities */
+		pos = skb_put(skb, 4);
+		*pos++ = WLAN_EID_PWR_CAPABILITY;
+		*pos++ = 2;
+		*pos++ = 0; /* min tx power */
+		*pos++ = local->hw.conf.channel->max_power; /* max tx power */
+
+		/* 2. supported channels */
+		/* TODO: get this in reg domain format */
+		pos = skb_put(skb, 2 * sband->n_channels + 2);
+		*pos++ = WLAN_EID_SUPPORTED_CHANNELS;
+		*pos++ = 2 * sband->n_channels;
+		for (i = 0; i < sband->n_channels; i++) {
+			*pos++ = ieee80211_frequency_to_channel(
+					sband->channels[i].center_freq);
+			*pos++ = 1; /* one channel in the subband*/
+		}
+	}
+
+	if (wk->ie_len && wk->ie) {
+		pos = skb_put(skb, wk->ie_len);
+		memcpy(pos, wk->ie, wk->ie_len);
+	}
+
+	if (wk->assoc.wmm_used && local->hw.queues >= 4) {
+		pos = skb_put(skb, 9);
+		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+		*pos++ = 7; /* len */
+		*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+		*pos++ = 0x50;
+		*pos++ = 0xf2;
+		*pos++ = 2; /* WME */
+		*pos++ = 0; /* WME info */
+		*pos++ = 1; /* WME ver */
+		*pos++ = 0;
+	}
+
+	/* wmm support is a must to HT */
+	/*
+	 * IEEE802.11n does not allow TKIP/WEP as pairwise
+	 * ciphers in HT mode. We still associate in non-ht
+	 * mode (11a/b/g) if any one of these ciphers is
+	 * configured as pairwise.
+	 */
+	if (wk->assoc.use_11n && wk->assoc.wmm_used &&
+	    (local->hw.queues >= 4) &&
+	    sband->ht_cap.ht_supported &&
+	    (ht_ie = wk->assoc.ht_information_ie) &&
+	    ht_ie[1] >= sizeof(struct ieee80211_ht_info)) {
+		struct ieee80211_ht_info *ht_info =
+			(struct ieee80211_ht_info *)(ht_ie + 2);
+		u16 cap = sband->ht_cap.cap;
+		__le16 tmp;
+		u32 flags = local->hw.conf.channel->flags;
+
+		/* determine capability flags */
+
+		if (ieee80211_disable_40mhz_24ghz &&
+		    sband->band == IEEE80211_BAND_2GHZ) {
+			cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+			cap &= ~IEEE80211_HT_CAP_SGI_40;
+		}
+
+		switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+		case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+			if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
+				cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+				cap &= ~IEEE80211_HT_CAP_SGI_40;
+			}
+			break;
+		case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+			if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
+				cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+				cap &= ~IEEE80211_HT_CAP_SGI_40;
+			}
+			break;
+		}
+
+		/* set SM PS mode properly */
+		cap &= ~IEEE80211_HT_CAP_SM_PS;
+		switch (wk->assoc.smps) {
+		case IEEE80211_SMPS_AUTOMATIC:
+		case IEEE80211_SMPS_NUM_MODES:
+			WARN_ON(1);
+		case IEEE80211_SMPS_OFF:
+			cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
+				IEEE80211_HT_CAP_SM_PS_SHIFT;
+			break;
+		case IEEE80211_SMPS_STATIC:
+			cap |= WLAN_HT_CAP_SM_PS_STATIC <<
+				IEEE80211_HT_CAP_SM_PS_SHIFT;
+			break;
+		case IEEE80211_SMPS_DYNAMIC:
+			cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
+				IEEE80211_HT_CAP_SM_PS_SHIFT;
+			break;
+		}
+
+		/* reserve and fill IE */
+
+		pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+		*pos++ = WLAN_EID_HT_CAPABILITY;
+		*pos++ = sizeof(struct ieee80211_ht_cap);
+		memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+		/* capability flags */
+		tmp = cpu_to_le16(cap);
+		memcpy(pos, &tmp, sizeof(u16));
+		pos += sizeof(u16);
+
+		/* AMPDU parameters */
+		*pos++ = sband->ht_cap.ampdu_factor |
+			 (sband->ht_cap.ampdu_density <<
+			 	IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+		/* MCS set */
+		memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
+		pos += sizeof(sband->ht_cap.mcs);
+
+		/* extended capabilities */
+		pos += sizeof(__le16);
+
+		/* BF capabilities */
+		pos += sizeof(__le32);
+
+		/* antenna selection */
+		pos += sizeof(u8);
+	}
+
+	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+	ieee80211_tx_skb(sdata, skb);
+}
+
+static void ieee80211_remove_auth_bss(struct ieee80211_local *local,
+				      struct ieee80211_work *wk)
+{
+	struct cfg80211_bss *cbss;
+	u16 capa_val = WLAN_CAPABILITY_ESS;
+
+	if (wk->probe_auth.privacy)
+		capa_val |= WLAN_CAPABILITY_PRIVACY;
+
+	cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->filter_ta,
+				wk->probe_auth.ssid, wk->probe_auth.ssid_len,
+				WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
+				capa_val);
+	if (!cbss)
+		return;
+
+	cfg80211_unlink_bss(local->hw.wiphy, cbss);
+	cfg80211_put_bss(cbss);
+}
+
+static enum work_action __must_check
+ieee80211_direct_probe(struct ieee80211_work *wk)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+
+	wk->probe_auth.tries++;
+	if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
+		printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
+		       sdata->name, wk->filter_ta);
+
+		/*
+		 * Most likely AP is not in the range so remove the
+		 * bss struct for that AP.
+		 */
+		ieee80211_remove_auth_bss(local, wk);
+
+		/*
+		 * We might have a pending scan which had no chance to run yet
+		 * due to work needing to be done. Hence, queue the STAs work
+		 * again for that.
+		 */
+		ieee80211_queue_work(&local->hw, &local->work_work);
+		return WORK_ACT_TIMEOUT;
+	}
+
+	printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
+			sdata->name, wk->filter_ta, wk->probe_auth.tries);
+
+	/*
+	 * Direct probe is sent to broadcast address as some APs
+	 * will not answer to direct packet in unassociated state.
+	 */
+	ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid,
+				 wk->probe_auth.ssid_len, NULL, 0);
+
+	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+	run_again(local, wk->timeout);
+
+	return WORK_ACT_NONE;
+}
+
+
+static enum work_action __must_check
+ieee80211_authenticate(struct ieee80211_work *wk)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+
+	wk->probe_auth.tries++;
+	if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
+		printk(KERN_DEBUG "%s: authentication with AP %pM"
+		       " timed out\n", sdata->name, wk->filter_ta);
+
+		/*
+		 * Most likely AP is not in the range so remove the
+		 * bss struct for that AP.
+		 */
+		ieee80211_remove_auth_bss(local, wk);
+
+		/*
+		 * We might have a pending scan which had no chance to run yet
+		 * due to work needing to be done. Hence, queue the STAs work
+		 * again for that.
+		 */
+		ieee80211_queue_work(&local->hw, &local->work_work);
+		return WORK_ACT_TIMEOUT;
+	}
+
+	printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
+	       sdata->name, wk->filter_ta, wk->probe_auth.tries);
+
+	ieee80211_send_auth(sdata, 1, wk->probe_auth.algorithm, wk->ie,
+			    wk->ie_len, wk->filter_ta, NULL, 0, 0);
+	wk->probe_auth.transaction = 2;
+
+	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+	run_again(local, wk->timeout);
+
+	return WORK_ACT_NONE;
+}
+
+static enum work_action __must_check
+ieee80211_associate(struct ieee80211_work *wk)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+
+	wk->assoc.tries++;
+	if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) {
+		printk(KERN_DEBUG "%s: association with AP %pM"
+		       " timed out\n",
+		       sdata->name, wk->filter_ta);
+
+		/*
+		 * Most likely AP is not in the range so remove the
+		 * bss struct for that AP.
+		 */
+		if (wk->assoc.bss)
+			cfg80211_unlink_bss(local->hw.wiphy,
+					    &wk->assoc.bss->cbss);
+
+		/*
+		 * We might have a pending scan which had no chance to run yet
+		 * due to work needing to be done. Hence, queue the STAs work
+		 * again for that.
+		 */
+		ieee80211_queue_work(&local->hw, &local->work_work);
+		return WORK_ACT_TIMEOUT;
+	}
+
+	printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
+	       sdata->name, wk->filter_ta, wk->assoc.tries);
+	ieee80211_send_assoc(sdata, wk);
+
+	wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
+	run_again(local, wk->timeout);
+
+	return WORK_ACT_NONE;
+}
+
+static void ieee80211_auth_challenge(struct ieee80211_work *wk,
+				     struct ieee80211_mgmt *mgmt,
+				     size_t len)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	u8 *pos;
+	struct ieee802_11_elems elems;
+
+	pos = mgmt->u.auth.variable;
+	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+	if (!elems.challenge)
+		return;
+	ieee80211_send_auth(sdata, 3, wk->probe_auth.algorithm,
+			    elems.challenge - 2, elems.challenge_len + 2,
+			    wk->filter_ta, wk->probe_auth.key,
+			    wk->probe_auth.key_len, wk->probe_auth.key_idx);
+	wk->probe_auth.transaction = 4;
+}
+
+static enum work_action __must_check
+ieee80211_rx_mgmt_auth(struct ieee80211_work *wk,
+		       struct ieee80211_mgmt *mgmt, size_t len)
+{
+	u16 auth_alg, auth_transaction, status_code;
+
+	if (wk->type != IEEE80211_WORK_AUTH)
+		return WORK_ACT_NONE;
+
+	if (len < 24 + 6)
+		return WORK_ACT_NONE;
+
+	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
+	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
+	status_code = le16_to_cpu(mgmt->u.auth.status_code);
+
+	if (auth_alg != wk->probe_auth.algorithm ||
+	    auth_transaction != wk->probe_auth.transaction)
+		return WORK_ACT_NONE;
+
+	if (status_code != WLAN_STATUS_SUCCESS) {
+		printk(KERN_DEBUG "%s: %pM denied authentication (status %d)\n",
+		       wk->sdata->name, mgmt->sa, status_code);
+		return WORK_ACT_DONE;
+	}
+
+	switch (wk->probe_auth.algorithm) {
+	case WLAN_AUTH_OPEN:
+	case WLAN_AUTH_LEAP:
+	case WLAN_AUTH_FT:
+		break;
+	case WLAN_AUTH_SHARED_KEY:
+		if (wk->probe_auth.transaction != 4) {
+			ieee80211_auth_challenge(wk, mgmt, len);
+			/* need another frame */
+			return WORK_ACT_NONE;
+		}
+		break;
+	default:
+		WARN_ON(1);
+		return WORK_ACT_NONE;
+	}
+
+ 	printk(KERN_DEBUG "%s: authenticated\n", wk->sdata->name);
+ 	return WORK_ACT_DONE;
+}
+
+static enum work_action __must_check
+ieee80211_rx_mgmt_assoc_resp(struct ieee80211_work *wk,
+			     struct ieee80211_mgmt *mgmt, size_t len,
+			     bool reassoc)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+	u16 capab_info, status_code, aid;
+	struct ieee802_11_elems elems;
+	u8 *pos;
+
+	/*
+	 * AssocResp and ReassocResp have identical structure, so process both
+	 * of them in this function.
+	 */
+
+	if (len < 24 + 6)
+		return WORK_ACT_NONE;
+
+	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
+	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+	aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+
+	printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
+	       "status=%d aid=%d)\n",
+	       sdata->name, reassoc ? "Rea" : "A", mgmt->sa,
+	       capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
+
+	pos = mgmt->u.assoc_resp.variable;
+	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+
+	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
+	    elems.timeout_int && elems.timeout_int_len == 5 &&
+	    elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
+		u32 tu, ms;
+		tu = get_unaligned_le32(elems.timeout_int + 1);
+		ms = tu * 1024 / 1000;
+		printk(KERN_DEBUG "%s: AP rejected association temporarily; "
+		       "comeback duration %u TU (%u ms)\n",
+		       sdata->name, tu, ms);
+		wk->timeout = jiffies + msecs_to_jiffies(ms);
+		if (ms > IEEE80211_ASSOC_TIMEOUT)
+			run_again(local, wk->timeout);
+		return WORK_ACT_NONE;
+	}
+
+	if (status_code != WLAN_STATUS_SUCCESS)
+		printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
+		       sdata->name, status_code);
+	else
+		printk(KERN_DEBUG "%s: associated\n", sdata->name);
+
+	return WORK_ACT_DONE;
+}
+
+static enum work_action __must_check
+ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk,
+			     struct ieee80211_mgmt *mgmt, size_t len,
+			     struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+	size_t baselen;
+
+	ASSERT_WORK_MTX(local);
+
+	baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
+	if (baselen > len)
+		return WORK_ACT_NONE;
+
+	printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name);
+	return WORK_ACT_DONE;
+}
+
+static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
+					  struct sk_buff *skb)
+{
+	struct ieee80211_rx_status *rx_status;
+	struct ieee80211_mgmt *mgmt;
+	struct ieee80211_work *wk;
+	enum work_action rma = WORK_ACT_NONE;
+	u16 fc;
+
+	rx_status = (struct ieee80211_rx_status *) skb->cb;
+	mgmt = (struct ieee80211_mgmt *) skb->data;
+	fc = le16_to_cpu(mgmt->frame_control);
+
+	mutex_lock(&local->work_mtx);
+
+	list_for_each_entry(wk, &local->work_list, list) {
+		const u8 *bssid = NULL;
+
+		switch (wk->type) {
+		case IEEE80211_WORK_DIRECT_PROBE:
+		case IEEE80211_WORK_AUTH:
+		case IEEE80211_WORK_ASSOC:
+			bssid = wk->filter_ta;
+			break;
+		default:
+			continue;
+		}
+
+		/*
+		 * Before queuing, we already verified mgmt->sa,
+		 * so this is needed just for matching.
+		 */
+		if (compare_ether_addr(bssid, mgmt->bssid))
+			continue;
+
+		switch (fc & IEEE80211_FCTL_STYPE) {
+		case IEEE80211_STYPE_PROBE_RESP:
+			rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len,
+							   rx_status);
+			break;
+		case IEEE80211_STYPE_AUTH:
+			rma = ieee80211_rx_mgmt_auth(wk, mgmt, skb->len);
+			break;
+		case IEEE80211_STYPE_ASSOC_RESP:
+			rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt,
+							   skb->len, false);
+			break;
+		case IEEE80211_STYPE_REASSOC_RESP:
+			rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt,
+							   skb->len, true);
+			break;
+		default:
+			WARN_ON(1);
+		}
+		/*
+		 * We've processed this frame for that work, so it can't
+		 * belong to another work struct.
+		 * NB: this is also required for correctness for 'rma'!
+		 */
+		break;
+	}
+
+	switch (rma) {
+	case WORK_ACT_NONE:
+		break;
+	case WORK_ACT_DONE:
+		list_del_rcu(&wk->list);
+		break;
+	default:
+		WARN(1, "unexpected: %d", rma);
+	}
+
+	mutex_unlock(&local->work_mtx);
+
+	if (rma != WORK_ACT_DONE)
+		goto out;
+
+	switch (wk->done(wk, skb)) {
+	case WORK_DONE_DESTROY:
+		free_work(wk);
+		break;
+	case WORK_DONE_REQUEUE:
+		synchronize_rcu();
+		wk->timeout = jiffies; /* run again directly */
+		mutex_lock(&local->work_mtx);
+		list_add_tail(&wk->list, &local->work_list);
+		mutex_unlock(&local->work_mtx);
+	}
+
+ out:
+	kfree_skb(skb);
+}
+
+static void ieee80211_work_timer(unsigned long data)
+{
+	struct ieee80211_local *local = (void *) data;
+
+	if (local->quiescing)
+		return;
+
+	ieee80211_queue_work(&local->hw, &local->work_work);
+}
+
+static void ieee80211_work_work(struct work_struct *work)
+{
+	struct ieee80211_local *local =
+		container_of(work, struct ieee80211_local, work_work);
+	struct sk_buff *skb;
+	struct ieee80211_work *wk, *tmp;
+	LIST_HEAD(free_work);
+	enum work_action rma;
+
+	if (local->scanning)
+		return;
+
+	/*
+	 * ieee80211_queue_work() should have picked up most cases,
+	 * here we'll pick the the rest.
+	 */
+	if (WARN(local->suspended, "work scheduled while going to suspend\n"))
+		return;
+
+	/* first process frames to avoid timing out while a frame is pending */
+	while ((skb = skb_dequeue(&local->work_skb_queue)))
+		ieee80211_work_rx_queued_mgmt(local, skb);
+
+	ieee80211_recalc_idle(local);
+
+	mutex_lock(&local->work_mtx);
+
+	list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
+		if (time_is_after_jiffies(wk->timeout)) {
+			/*
+			 * This work item isn't supposed to be worked on
+			 * right now, but take care to adjust the timer
+			 * properly.
+			 */
+			run_again(local, wk->timeout);
+			continue;
+		}
+
+		switch (wk->type) {
+		default:
+			WARN_ON(1);
+			/* nothing */
+			rma = WORK_ACT_NONE;
+			break;
+		case IEEE80211_WORK_DIRECT_PROBE:
+			rma = ieee80211_direct_probe(wk);
+			break;
+		case IEEE80211_WORK_AUTH:
+			rma = ieee80211_authenticate(wk);
+			break;
+		case IEEE80211_WORK_ASSOC:
+			rma = ieee80211_associate(wk);
+			break;
+		}
+
+		switch (rma) {
+		case WORK_ACT_NONE:
+			/* no action required */
+			break;
+		case WORK_ACT_TIMEOUT:
+			list_del_rcu(&wk->list);
+			synchronize_rcu();
+			list_add(&wk->list, &free_work);
+			break;
+		default:
+			WARN(1, "unexpected: %d", rma);
+		}
+	}
+
+	if (list_empty(&local->work_list) && local->scan_req)
+		ieee80211_queue_delayed_work(&local->hw,
+					     &local->scan_work,
+					     round_jiffies_relative(0));
+
+	mutex_unlock(&local->work_mtx);
+
+	list_for_each_entry_safe(wk, tmp, &free_work, list) {
+		wk->done(wk, NULL);
+		list_del(&wk->list);
+		kfree(wk);
+	}
+}
+
+void ieee80211_add_work(struct ieee80211_work *wk)
+{
+	struct ieee80211_local *local;
+
+	if (WARN_ON(!wk->chan))
+		return;
+
+	if (WARN_ON(!wk->sdata))
+		return;
+
+	if (WARN_ON(!wk->done))
+		return;
+
+	wk->timeout = jiffies;
+
+	local = wk->sdata->local;
+	mutex_lock(&local->work_mtx);
+	list_add_tail(&wk->list, &local->work_list);
+	mutex_unlock(&local->work_mtx);
+
+	ieee80211_queue_work(&local->hw, &local->work_work);
+}
+
+void ieee80211_work_init(struct ieee80211_local *local)
+{
+	mutex_init(&local->work_mtx);
+	INIT_LIST_HEAD(&local->work_list);
+	setup_timer(&local->work_timer, ieee80211_work_timer,
+		    (unsigned long)local);
+	INIT_WORK(&local->work_work, ieee80211_work_work);
+	skb_queue_head_init(&local->work_skb_queue);
+}
+
+void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_work *wk, *tmp;
+
+	mutex_lock(&local->work_mtx);
+	list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
+		if (wk->sdata != sdata)
+			continue;
+		list_del(&wk->list);
+		free_work(wk);
+	}
+	mutex_unlock(&local->work_mtx);
+}
+
+ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
+					   struct sk_buff *skb)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_mgmt *mgmt;
+	struct ieee80211_work *wk;
+	u16 fc;
+
+	if (skb->len < 24)
+		return RX_DROP_MONITOR;
+
+	mgmt = (struct ieee80211_mgmt *) skb->data;
+	fc = le16_to_cpu(mgmt->frame_control);
+
+	list_for_each_entry_rcu(wk, &local->work_list, list) {
+		if (sdata != wk->sdata)
+			continue;
+		if (compare_ether_addr(wk->filter_ta, mgmt->sa))
+			continue;
+		if (compare_ether_addr(wk->filter_ta, mgmt->bssid))
+			continue;
+
+		switch (fc & IEEE80211_FCTL_STYPE) {
+		case IEEE80211_STYPE_AUTH:
+		case IEEE80211_STYPE_PROBE_RESP:
+		case IEEE80211_STYPE_ASSOC_RESP:
+		case IEEE80211_STYPE_REASSOC_RESP:
+		case IEEE80211_STYPE_DEAUTH:
+		case IEEE80211_STYPE_DISASSOC:
+			skb_queue_tail(&local->work_skb_queue, skb);
+			ieee80211_queue_work(&local->hw, &local->work_work);
+			return RX_QUEUED;
+		}
+	}
+
+	return RX_CONTINUE;
+}
--- wireless-testing.orig/net/mac80211/debugfs_netdev.c	2009-12-07 15:43:43.000000000 +0100
+++ wireless-testing/net/mac80211/debugfs_netdev.c	2009-12-07 15:44:16.000000000 +0100
@@ -133,7 +133,6 @@ IEEE80211_IF_FILE(max_ratectrl_rateidx, 
 /* STA attributes */
 IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
 IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
-IEEE80211_IF_FILE(capab, u.mgd.capab, HEX);
 
 static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
 			      enum ieee80211_smps_mode smps_mode)
@@ -270,7 +269,6 @@ static void add_sta_files(struct ieee802
 
 	DEBUGFS_ADD(bssid, sta);
 	DEBUGFS_ADD(aid, sta);
-	DEBUGFS_ADD(capab, sta);
 	DEBUGFS_ADD_MODE(smps, 0600);
 }
 
--- wireless-testing.orig/net/mac80211/main.c	2009-12-07 15:43:43.000000000 +0100
+++ wireless-testing/net/mac80211/main.c	2009-12-07 15:49:31.000000000 +0100
@@ -395,6 +395,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(
 
 	INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
 
+	ieee80211_work_init(local);
+
 	INIT_WORK(&local->restart_work, ieee80211_restart_work);
 
 	INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
--- wireless-testing.orig/net/mac80211/rx.c	2009-12-07 15:43:43.000000000 +0100
+++ wireless-testing/net/mac80211/rx.c	2009-12-07 15:44:16.000000000 +0100
@@ -2025,6 +2025,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_
 {
 	struct ieee80211_sub_if_data *sdata = rx->sdata;
 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
+	ieee80211_rx_result rxs;
 
 	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 		return RX_DROP_MONITOR;
@@ -2032,6 +2033,10 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_
 	if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
 		return RX_DROP_MONITOR;
 
+	rxs = ieee80211_work_rx_mgmt(rx->sdata, rx->skb);
+	if (rxs != RX_CONTINUE)
+		return rxs;
+
 	if (ieee80211_vif_is_mesh(&sdata->vif))
 		return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
 



^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2009-12-07 14:54 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-12-02 11:43 [PATCH 0/6] mac80211: mlme work improvements Johannes Berg
2009-12-02 11:43 ` [PATCH 1/6] cfg80211: avoid sending spurious deauth to userspace Johannes Berg
2009-12-02 11:43 ` [PATCH 2/6] mac80211: dont try to use existing sta for AP Johannes Berg
2009-12-02 11:43 ` [PATCH 3/6] mac80211: let cfg80211 manage auth state Johannes Berg
2009-12-02 11:43 ` [PATCH 4/6] mac80211: generalise management work a bit Johannes Berg
2009-12-02 11:43 ` [PATCH 5/6] mac80211: generalise work handling Johannes Berg
2009-12-07 14:53   ` [PATCH 5/6 v2] " Johannes Berg
2009-12-02 11:43 ` [PATCH 6/6] mac80211: rewrite a few work messages Johannes Berg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).