mac80211: fix managed mode BSSID handling

Currently, we will ask the driver to configure right away
when somebody changes the desired BSSID. That's totally
strange because then we will configure the driver without
even knowing whether the BSS exists. Change this to only
configure the BSSID when associated, and configure a zero
BSSID when not associated.

As a side effect, this fixes an issue with the iwlwifi
driver which doesn't implement sta_notify properly and
uses the BSSID instead and gets very confused if the
BSSID is cleared before we disassociate, which results
in the warning Marcel posted [1] and iwlwifi bug 1995 [2].

[1] http://thread.gmane.org/gmane.linux.kernel.wireless.general/32598
[2] http://www.intellinuxwireless.org/bugzilla/show_bug.cgi?id=1995

Cc: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 76df5ea..6b7e92e 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -219,18 +219,26 @@
 				      u32 changed)
 {
 	struct ieee80211_local *local = sdata->local;
+	static const u8 zero[ETH_ALEN] = { 0 };
 
 	if (!changed)
 		return;
 
-	if (sdata->vif.type == NL80211_IFTYPE_STATION)
-		sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
-	else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+		/*
+		 * While not associated, claim a BSSID of all-zeroes
+		 * so that drivers don't do any weird things with the
+		 * BSSID at that time.
+		 */
+		if (sdata->vif.bss_conf.assoc)
+			sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
+		else
+			sdata->vif.bss_conf.bssid = zero;
+	} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 		sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
 	else if (sdata->vif.type == NL80211_IFTYPE_AP)
 		sdata->vif.bss_conf.bssid = sdata->dev->dev_addr;
 	else if (ieee80211_vif_is_mesh(&sdata->vif)) {
-		static const u8 zero[ETH_ALEN] = { 0 };
 		sdata->vif.bss_conf.bssid = zero;
 	} else {
 		WARN_ON(1);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 87743e4..b7f9c60 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -977,6 +977,10 @@
 	 * changed or not.
 	 */
 	bss_info_changed |= BSS_CHANGED_BASIC_RATES;
+
+	/* And the BSSID changed - we're associated now */
+	bss_info_changed |= BSS_CHANGED_BSSID;
+
 	ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
 	/* will be same as sdata */
@@ -1176,6 +1180,9 @@
 	}
 
 	ieee80211_hw_config(local, config_changed);
+
+	/* And the BSSID changed -- not very interesting here */
+	changed |= BSS_CHANGED_BSSID;
 	ieee80211_bss_info_change_notify(sdata, changed);
 
 	rcu_read_lock();
@@ -2481,9 +2488,6 @@
 		ifmgd->flags &= ~IEEE80211_STA_BSSID_SET;
 	}
 
-	if (netif_running(sdata->dev))
-		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
-
 	return ieee80211_sta_commit(sdata);
 }