diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index b34c17f52f3ea5d01a243e0015e8ff5546898274..e496a2daf7efdc2c08e86cafbad35719ec3b454a 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -564,6 +564,9 @@ enum nl80211_commands {
  * @NL80211_ATTR_RESP_IE: (Re)association response information elements as
  *	sent by peer, for ROAM and successful CONNECT events.
  *
+ * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE
+ *	commands to specify using a reassociate frame
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -687,6 +690,8 @@ enum nl80211_attrs {
 	NL80211_ATTR_REQ_IE,
 	NL80211_ATTR_RESP_IE,
 
+	NL80211_ATTR_PREV_BSSID,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index ca986cc91098f7ebb3e7e5951722102923005810..71847d3c264043d3937a8f4e607ac14b116ddfbf 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -664,10 +664,11 @@ struct cfg80211_auth_request {
  * @ie_len: Length of ie buffer in octets
  * @use_mfp: Use management frame protection (IEEE 802.11w) in this association
  * @crypto: crypto settings
+ * @prev_bssid: previous BSSID, if not %NULL use reassociate frame
  */
 struct cfg80211_assoc_request {
 	struct cfg80211_bss *bss;
-	const u8 *ie;
+	const u8 *ie, *prev_bssid;
 	size_t ie_len;
 	struct cfg80211_crypto_settings crypto;
 	bool use_mfp;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 0f29cd0580c9c32b379f3bb98b08f80e0f8a3f66..e6d8860f26f290aee0280849dedeb52890279257 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1256,6 +1256,12 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
 		sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED;
 	}
 
+	if (req->prev_bssid) {
+		sdata->u.mgd.flags |= IEEE80211_STA_PREV_BSSID_SET;
+		memcpy(sdata->u.mgd.prev_bssid, req->prev_bssid, ETH_ALEN);
+	} else
+		sdata->u.mgd.flags &= ~IEEE80211_STA_PREV_BSSID_SET;
+
 	if (req->crypto.control_port)
 		sdata->u.mgd.flags |= IEEE80211_STA_CONTROL_PORT;
 	else
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index aa1829ae431d9d1ee0ba68694526577a5c3edd36..24486455e505102203809e73c6699eed865f35db 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -879,9 +879,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 		ieee80211_rx_bss_put(local, bss);
 	}
 
-	ifmgd->flags |= IEEE80211_STA_PREV_BSSID_SET;
-	memcpy(ifmgd->prev_bssid, sdata->u.mgd.bssid, ETH_ALEN);
-
 	ifmgd->last_probe = jiffies;
 	ieee80211_led_assoc(local, 1);
 
@@ -1470,10 +1467,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
 	if (status_code != WLAN_STATUS_SUCCESS) {
 		printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
 		       sdata->dev->name, status_code);
-		/* if this was a reassociation, ensure we try a "full"
-		 * association next time. This works around some broken APs
-		 * which do not correctly reject reassociation requests. */
-		ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
 		cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len,
 				       GFP_KERNEL);
 		/* Wait for SME to decide what to do next */
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 82918f5896a5122940c4967dc3f93af51997a6a8..4554453c116aecb5ee2bcb4f9b866f5c9c0d78fb 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -202,7 +202,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
 		       const u8 *ie, int ie_len);
 int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
 			struct net_device *dev, struct ieee80211_channel *chan,
-			const u8 *bssid, const u8 *ssid, int ssid_len,
+			const u8 *bssid, const u8 *prev_bssid,
+			const u8 *ssid, int ssid_len,
 			const u8 *ie, int ie_len, bool use_mfp,
 			struct cfg80211_crypto_settings *crypt);
 int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 020f33b38467ac8ee570a61bd76b634c3fda17f9..087d3377958f44a0c9705ea76eb94d427c261c96 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -335,7 +335,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
 
 int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
 			struct net_device *dev, struct ieee80211_channel *chan,
-			const u8 *bssid, const u8 *ssid, int ssid_len,
+			const u8 *bssid, const u8 *prev_bssid,
+			const u8 *ssid, int ssid_len,
 			const u8 *ie, int ie_len, bool use_mfp,
 			struct cfg80211_crypto_settings *crypt)
 {
@@ -353,6 +354,7 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
 	req.ie_len = ie_len;
 	memcpy(&req.crypto, crypt, sizeof(req.crypto));
 	req.use_mfp = use_mfp;
+	req.prev_bssid = prev_bssid;
 	req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
 				   WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
 	if (!req.bss)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 723512b48f2eed2382da3fcd9818a4a1042e01d1..44c520c264fc5a804c015a4f68d8a9f71f8100e6 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -71,6 +71,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
 	[NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
 
 	[NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
+	[NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
 
 	[NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
 				    .len = WLAN_MAX_KEY_LEN },
@@ -3187,7 +3188,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 	struct net_device *dev;
 	struct cfg80211_crypto_settings crypto;
 	struct ieee80211_channel *chan;
-	const u8 *bssid, *ssid, *ie = NULL;
+	const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
 	int err, ssid_len, ie_len = 0;
 	bool use_mfp = false;
 
@@ -3248,10 +3249,13 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 		}
 	}
 
+	if (info->attrs[NL80211_ATTR_PREV_BSSID])
+		prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+
 	err = nl80211_crypto_settings(info, &crypto, 1);
 	if (!err)
-		err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, ssid,
-					  ssid_len, ie, ie_len, use_mfp,
+		err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
+					  ssid, ssid_len, ie, ie_len, use_mfp,
 					  &crypto);
 
 out:
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 412161f7b08e55fcd72173aacc223936544cc79a..066a19ef9d731fffe9176e0424cc7d3e3466f6d0 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -125,8 +125,14 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
 	case CFG80211_CONN_ASSOCIATE_NEXT:
 		BUG_ON(!drv->ops->assoc);
 		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
+		/*
+		 * We could, later, implement roaming here and then actually
+		 * set prev_bssid to non-NULL. But then we need to be aware
+		 * that some APs don't like that -- so we'd need to retry
+		 * the association.
+		 */
 		err = cfg80211_mlme_assoc(drv, wdev->netdev,
-					  params->channel, params->bssid,
+					  params->channel, params->bssid, NULL,
 					  params->ssid, params->ssid_len,
 					  params->ie, params->ie_len,
 					  false, &params->crypto);