xref: /openbmc/linux/net/wireless/sme.c (revision fd014284)
1b23aa676SSamuel Ortiz /*
2b23aa676SSamuel Ortiz  * SME code for cfg80211's connect emulation.
3b23aa676SSamuel Ortiz  *
4b23aa676SSamuel Ortiz  * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
5b23aa676SSamuel Ortiz  * Copyright (C) 2009   Intel Corporation. All rights reserved.
6b23aa676SSamuel Ortiz  */
7b23aa676SSamuel Ortiz 
8b23aa676SSamuel Ortiz #include <linux/etherdevice.h>
9b23aa676SSamuel Ortiz #include <linux/if_arp.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
11b23aa676SSamuel Ortiz #include <linux/workqueue.h>
12a9a11622SJohannes Berg #include <linux/wireless.h>
13bc3b2d7fSPaul Gortmaker #include <linux/export.h>
14a9a11622SJohannes Berg #include <net/iw_handler.h>
15b23aa676SSamuel Ortiz #include <net/cfg80211.h>
16b23aa676SSamuel Ortiz #include <net/rtnetlink.h>
17b23aa676SSamuel Ortiz #include "nl80211.h"
188b19e6caSLuis R. Rodriguez #include "reg.h"
19b23aa676SSamuel Ortiz 
206829c878SJohannes Berg struct cfg80211_conn {
216829c878SJohannes Berg 	struct cfg80211_connect_params params;
226829c878SJohannes Berg 	/* these are sub-states of the _CONNECTING sme_state */
236829c878SJohannes Berg 	enum {
246829c878SJohannes Berg 		CFG80211_CONN_IDLE,
256829c878SJohannes Berg 		CFG80211_CONN_SCANNING,
266829c878SJohannes Berg 		CFG80211_CONN_SCAN_AGAIN,
276829c878SJohannes Berg 		CFG80211_CONN_AUTHENTICATE_NEXT,
286829c878SJohannes Berg 		CFG80211_CONN_AUTHENTICATING,
296829c878SJohannes Berg 		CFG80211_CONN_ASSOCIATE_NEXT,
306829c878SJohannes Berg 		CFG80211_CONN_ASSOCIATING,
317d930bc3SJohannes Berg 		CFG80211_CONN_DEAUTH_ASSOC_FAIL,
326829c878SJohannes Berg 	} state;
33f401a6f7SJohannes Berg 	u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
346829c878SJohannes Berg 	u8 *ie;
356829c878SJohannes Berg 	size_t ie_len;
36f401a6f7SJohannes Berg 	bool auto_auth, prev_bssid_valid;
376829c878SJohannes Berg };
386829c878SJohannes Berg 
3920925feeSJohn W. Linville static bool cfg80211_is_all_idle(void)
4009d989d1SLuis R. Rodriguez {
4109d989d1SLuis R. Rodriguez 	struct cfg80211_registered_device *rdev;
4209d989d1SLuis R. Rodriguez 	struct wireless_dev *wdev;
4309d989d1SLuis R. Rodriguez 	bool is_all_idle = true;
4409d989d1SLuis R. Rodriguez 
4509d989d1SLuis R. Rodriguez 	mutex_lock(&cfg80211_mutex);
4609d989d1SLuis R. Rodriguez 
4709d989d1SLuis R. Rodriguez 	/*
4809d989d1SLuis R. Rodriguez 	 * All devices must be idle as otherwise if you are actively
4909d989d1SLuis R. Rodriguez 	 * scanning some new beacon hints could be learned and would
5009d989d1SLuis R. Rodriguez 	 * count as new regulatory hints.
5109d989d1SLuis R. Rodriguez 	 */
5209d989d1SLuis R. Rodriguez 	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
5309d989d1SLuis R. Rodriguez 		cfg80211_lock_rdev(rdev);
5489a54e48SJohannes Berg 		list_for_each_entry(wdev, &rdev->wdev_list, list) {
5509d989d1SLuis R. Rodriguez 			wdev_lock(wdev);
5609d989d1SLuis R. Rodriguez 			if (wdev->sme_state != CFG80211_SME_IDLE)
5709d989d1SLuis R. Rodriguez 				is_all_idle = false;
5809d989d1SLuis R. Rodriguez 			wdev_unlock(wdev);
5909d989d1SLuis R. Rodriguez 		}
6009d989d1SLuis R. Rodriguez 		cfg80211_unlock_rdev(rdev);
6109d989d1SLuis R. Rodriguez 	}
6209d989d1SLuis R. Rodriguez 
6309d989d1SLuis R. Rodriguez 	mutex_unlock(&cfg80211_mutex);
6409d989d1SLuis R. Rodriguez 
6509d989d1SLuis R. Rodriguez 	return is_all_idle;
6609d989d1SLuis R. Rodriguez }
6709d989d1SLuis R. Rodriguez 
6809d989d1SLuis R. Rodriguez static void disconnect_work(struct work_struct *work)
6909d989d1SLuis R. Rodriguez {
7009d989d1SLuis R. Rodriguez 	if (!cfg80211_is_all_idle())
7109d989d1SLuis R. Rodriguez 		return;
7209d989d1SLuis R. Rodriguez 
7309d989d1SLuis R. Rodriguez 	regulatory_hint_disconnect();
7409d989d1SLuis R. Rodriguez }
7509d989d1SLuis R. Rodriguez 
7609d989d1SLuis R. Rodriguez static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
776829c878SJohannes Berg 
786829c878SJohannes Berg static int cfg80211_conn_scan(struct wireless_dev *wdev)
796829c878SJohannes Berg {
8079c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
816829c878SJohannes Berg 	struct cfg80211_scan_request *request;
826829c878SJohannes Berg 	int n_channels, err;
836829c878SJohannes Berg 
846829c878SJohannes Berg 	ASSERT_RTNL();
8579c97e97SJohannes Berg 	ASSERT_RDEV_LOCK(rdev);
86667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
876829c878SJohannes Berg 
8879c97e97SJohannes Berg 	if (rdev->scan_req)
896829c878SJohannes Berg 		return -EBUSY;
906829c878SJohannes Berg 
916829c878SJohannes Berg 	if (wdev->conn->params.channel) {
926829c878SJohannes Berg 		n_channels = 1;
936829c878SJohannes Berg 	} else {
946829c878SJohannes Berg 		enum ieee80211_band band;
956829c878SJohannes Berg 		n_channels = 0;
966829c878SJohannes Berg 
976829c878SJohannes Berg 		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
986829c878SJohannes Berg 			if (!wdev->wiphy->bands[band])
996829c878SJohannes Berg 				continue;
1006829c878SJohannes Berg 			n_channels += wdev->wiphy->bands[band]->n_channels;
1016829c878SJohannes Berg 		}
1026829c878SJohannes Berg 	}
1036829c878SJohannes Berg 	request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
1046829c878SJohannes Berg 			  sizeof(request->channels[0]) * n_channels,
1056829c878SJohannes Berg 			  GFP_KERNEL);
1066829c878SJohannes Berg 	if (!request)
1076829c878SJohannes Berg 		return -ENOMEM;
1086829c878SJohannes Berg 
1096829c878SJohannes Berg 	if (wdev->conn->params.channel)
1106829c878SJohannes Berg 		request->channels[0] = wdev->conn->params.channel;
1116829c878SJohannes Berg 	else {
1126829c878SJohannes Berg 		int i = 0, j;
1136829c878SJohannes Berg 		enum ieee80211_band band;
114e3081501SRajkumar Manoharan 		struct ieee80211_supported_band *bands;
115e3081501SRajkumar Manoharan 		struct ieee80211_channel *channel;
1166829c878SJohannes Berg 
1176829c878SJohannes Berg 		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
118e3081501SRajkumar Manoharan 			bands = wdev->wiphy->bands[band];
119e3081501SRajkumar Manoharan 			if (!bands)
1206829c878SJohannes Berg 				continue;
121e3081501SRajkumar Manoharan 			for (j = 0; j < bands->n_channels; j++) {
122e3081501SRajkumar Manoharan 				channel = &bands->channels[j];
123e3081501SRajkumar Manoharan 				if (channel->flags & IEEE80211_CHAN_DISABLED)
124e3081501SRajkumar Manoharan 					continue;
125e3081501SRajkumar Manoharan 				request->channels[i++] = channel;
1266829c878SJohannes Berg 			}
127e3081501SRajkumar Manoharan 			request->rates[band] = (1 << bands->n_bitrates) - 1;
128e3081501SRajkumar Manoharan 		}
129e3081501SRajkumar Manoharan 		n_channels = i;
1306829c878SJohannes Berg 	}
1316829c878SJohannes Berg 	request->n_channels = n_channels;
1325ba63533SJohannes Berg 	request->ssids = (void *)&request->channels[n_channels];
1336829c878SJohannes Berg 	request->n_ssids = 1;
1346829c878SJohannes Berg 
1356829c878SJohannes Berg 	memcpy(request->ssids[0].ssid, wdev->conn->params.ssid,
1366829c878SJohannes Berg 		wdev->conn->params.ssid_len);
1376829c878SJohannes Berg 	request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
1386829c878SJohannes Berg 
139*fd014284SJohannes Berg 	request->wdev = wdev;
14079c97e97SJohannes Berg 	request->wiphy = &rdev->wiphy;
1416829c878SJohannes Berg 
14279c97e97SJohannes Berg 	rdev->scan_req = request;
1436829c878SJohannes Berg 
144*fd014284SJohannes Berg 	err = rdev->ops->scan(wdev->wiphy, request);
1456829c878SJohannes Berg 	if (!err) {
1466829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_SCANNING;
147*fd014284SJohannes Berg 		nl80211_send_scan_start(rdev, wdev);
148463d0183SJohannes Berg 		dev_hold(wdev->netdev);
1496829c878SJohannes Berg 	} else {
15079c97e97SJohannes Berg 		rdev->scan_req = NULL;
1516829c878SJohannes Berg 		kfree(request);
1526829c878SJohannes Berg 	}
1536829c878SJohannes Berg 	return err;
1546829c878SJohannes Berg }
1556829c878SJohannes Berg 
1566829c878SJohannes Berg static int cfg80211_conn_do_work(struct wireless_dev *wdev)
1576829c878SJohannes Berg {
15879c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
15919957bb3SJohannes Berg 	struct cfg80211_connect_params *params;
160f401a6f7SJohannes Berg 	const u8 *prev_bssid = NULL;
16119957bb3SJohannes Berg 	int err;
1626829c878SJohannes Berg 
163667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
164667503ddSJohannes Berg 
1656829c878SJohannes Berg 	if (!wdev->conn)
1666829c878SJohannes Berg 		return 0;
1676829c878SJohannes Berg 
16819957bb3SJohannes Berg 	params = &wdev->conn->params;
16919957bb3SJohannes Berg 
1706829c878SJohannes Berg 	switch (wdev->conn->state) {
1716829c878SJohannes Berg 	case CFG80211_CONN_SCAN_AGAIN:
1726829c878SJohannes Berg 		return cfg80211_conn_scan(wdev);
1736829c878SJohannes Berg 	case CFG80211_CONN_AUTHENTICATE_NEXT:
17479c97e97SJohannes Berg 		BUG_ON(!rdev->ops->auth);
17519957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
17679c97e97SJohannes Berg 		return __cfg80211_mlme_auth(rdev, wdev->netdev,
17719957bb3SJohannes Berg 					    params->channel, params->auth_type,
17819957bb3SJohannes Berg 					    params->bssid,
17919957bb3SJohannes Berg 					    params->ssid, params->ssid_len,
180fffd0934SJohannes Berg 					    NULL, 0,
181fffd0934SJohannes Berg 					    params->key, params->key_len,
18295de817bSJohannes Berg 					    params->key_idx);
1836829c878SJohannes Berg 	case CFG80211_CONN_ASSOCIATE_NEXT:
18479c97e97SJohannes Berg 		BUG_ON(!rdev->ops->assoc);
18519957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
186f401a6f7SJohannes Berg 		if (wdev->conn->prev_bssid_valid)
187f401a6f7SJohannes Berg 			prev_bssid = wdev->conn->prev_bssid;
18879c97e97SJohannes Berg 		err = __cfg80211_mlme_assoc(rdev, wdev->netdev,
189667503ddSJohannes Berg 					    params->channel, params->bssid,
190f401a6f7SJohannes Berg 					    prev_bssid,
19119957bb3SJohannes Berg 					    params->ssid, params->ssid_len,
19219957bb3SJohannes Berg 					    params->ie, params->ie_len,
1937e7c8926SBen Greear 					    false, &params->crypto,
1947e7c8926SBen Greear 					    params->flags, &params->ht_capa,
1957e7c8926SBen Greear 					    &params->ht_capa_mask);
19619957bb3SJohannes Berg 		if (err)
19779c97e97SJohannes Berg 			__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
198667503ddSJohannes Berg 					       NULL, 0,
199d5cdfacbSJouni Malinen 					       WLAN_REASON_DEAUTH_LEAVING,
200d5cdfacbSJouni Malinen 					       false);
20119957bb3SJohannes Berg 		return err;
2027d930bc3SJohannes Berg 	case CFG80211_CONN_DEAUTH_ASSOC_FAIL:
2037d930bc3SJohannes Berg 		__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
2047d930bc3SJohannes Berg 				       NULL, 0,
205d5cdfacbSJouni Malinen 				       WLAN_REASON_DEAUTH_LEAVING, false);
2067d930bc3SJohannes Berg 		/* return an error so that we call __cfg80211_connect_result() */
2077d930bc3SJohannes Berg 		return -EINVAL;
2086829c878SJohannes Berg 	default:
2096829c878SJohannes Berg 		return 0;
2106829c878SJohannes Berg 	}
2116829c878SJohannes Berg }
2126829c878SJohannes Berg 
2136829c878SJohannes Berg void cfg80211_conn_work(struct work_struct *work)
2146829c878SJohannes Berg {
21579c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev =
2166829c878SJohannes Berg 		container_of(work, struct cfg80211_registered_device, conn_work);
2176829c878SJohannes Berg 	struct wireless_dev *wdev;
2187400f42eSJohannes Berg 	u8 bssid_buf[ETH_ALEN], *bssid = NULL;
2196829c878SJohannes Berg 
2206829c878SJohannes Berg 	rtnl_lock();
22179c97e97SJohannes Berg 	cfg80211_lock_rdev(rdev);
22279c97e97SJohannes Berg 	mutex_lock(&rdev->devlist_mtx);
2236829c878SJohannes Berg 
22489a54e48SJohannes Berg 	list_for_each_entry(wdev, &rdev->wdev_list, list) {
225667503ddSJohannes Berg 		wdev_lock(wdev);
226667503ddSJohannes Berg 		if (!netif_running(wdev->netdev)) {
227667503ddSJohannes Berg 			wdev_unlock(wdev);
2286829c878SJohannes Berg 			continue;
229667503ddSJohannes Berg 		}
230667503ddSJohannes Berg 		if (wdev->sme_state != CFG80211_SME_CONNECTING) {
231667503ddSJohannes Berg 			wdev_unlock(wdev);
2326829c878SJohannes Berg 			continue;
233667503ddSJohannes Berg 		}
2347400f42eSJohannes Berg 		if (wdev->conn->params.bssid) {
2357400f42eSJohannes Berg 			memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
2367400f42eSJohannes Berg 			bssid = bssid_buf;
2377400f42eSJohannes Berg 		}
2386829c878SJohannes Berg 		if (cfg80211_conn_do_work(wdev))
239667503ddSJohannes Berg 			__cfg80211_connect_result(
2407d930bc3SJohannes Berg 					wdev->netdev, bssid,
2416829c878SJohannes Berg 					NULL, 0, NULL, 0,
2426829c878SJohannes Berg 					WLAN_STATUS_UNSPECIFIED_FAILURE,
243df7fc0f9SJohannes Berg 					false, NULL);
244667503ddSJohannes Berg 		wdev_unlock(wdev);
2456829c878SJohannes Berg 	}
2466829c878SJohannes Berg 
24779c97e97SJohannes Berg 	mutex_unlock(&rdev->devlist_mtx);
24879c97e97SJohannes Berg 	cfg80211_unlock_rdev(rdev);
2496829c878SJohannes Berg 	rtnl_unlock();
2506829c878SJohannes Berg }
2516829c878SJohannes Berg 
252bbac31f4SJohannes Berg static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
2536829c878SJohannes Berg {
25479c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
2556829c878SJohannes Berg 	struct cfg80211_bss *bss;
2566829c878SJohannes Berg 	u16 capa = WLAN_CAPABILITY_ESS;
2576829c878SJohannes Berg 
258667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
259667503ddSJohannes Berg 
2606829c878SJohannes Berg 	if (wdev->conn->params.privacy)
2616829c878SJohannes Berg 		capa |= WLAN_CAPABILITY_PRIVACY;
2626829c878SJohannes Berg 
263ed9d0102SJouni Malinen 	bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel,
264ed9d0102SJouni Malinen 			       wdev->conn->params.bssid,
2656829c878SJohannes Berg 			       wdev->conn->params.ssid,
2666829c878SJohannes Berg 			       wdev->conn->params.ssid_len,
2676829c878SJohannes Berg 			       WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
2686829c878SJohannes Berg 			       capa);
2696829c878SJohannes Berg 	if (!bss)
270bbac31f4SJohannes Berg 		return NULL;
2716829c878SJohannes Berg 
2726829c878SJohannes Berg 	memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN);
2736829c878SJohannes Berg 	wdev->conn->params.bssid = wdev->conn->bssid;
2746829c878SJohannes Berg 	wdev->conn->params.channel = bss->channel;
2756829c878SJohannes Berg 	wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
27679c97e97SJohannes Berg 	schedule_work(&rdev->conn_work);
2776829c878SJohannes Berg 
278bbac31f4SJohannes Berg 	return bss;
2796829c878SJohannes Berg }
2806829c878SJohannes Berg 
281667503ddSJohannes Berg static void __cfg80211_sme_scan_done(struct net_device *dev)
2826829c878SJohannes Berg {
2836829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
28479c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
285bbac31f4SJohannes Berg 	struct cfg80211_bss *bss;
2866829c878SJohannes Berg 
287667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
288667503ddSJohannes Berg 
2896829c878SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
2906829c878SJohannes Berg 		return;
2916829c878SJohannes Berg 
292d4b1a687SZhu Yi 	if (!wdev->conn)
2936829c878SJohannes Berg 		return;
2946829c878SJohannes Berg 
2956829c878SJohannes Berg 	if (wdev->conn->state != CFG80211_CONN_SCANNING &&
2966829c878SJohannes Berg 	    wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
2976829c878SJohannes Berg 		return;
2986829c878SJohannes Berg 
299bbac31f4SJohannes Berg 	bss = cfg80211_get_conn_bss(wdev);
300bbac31f4SJohannes Berg 	if (bss) {
301bbac31f4SJohannes Berg 		cfg80211_put_bss(bss);
302bbac31f4SJohannes Berg 	} else {
3036829c878SJohannes Berg 		/* not found */
3046829c878SJohannes Berg 		if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
30579c97e97SJohannes Berg 			schedule_work(&rdev->conn_work);
3066829c878SJohannes Berg 		else
307667503ddSJohannes Berg 			__cfg80211_connect_result(
308667503ddSJohannes Berg 					wdev->netdev,
309667503ddSJohannes Berg 					wdev->conn->params.bssid,
3106829c878SJohannes Berg 					NULL, 0, NULL, 0,
3116829c878SJohannes Berg 					WLAN_STATUS_UNSPECIFIED_FAILURE,
312df7fc0f9SJohannes Berg 					false, NULL);
3136829c878SJohannes Berg 	}
3146829c878SJohannes Berg }
3156829c878SJohannes Berg 
316667503ddSJohannes Berg void cfg80211_sme_scan_done(struct net_device *dev)
317667503ddSJohannes Berg {
318667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
319667503ddSJohannes Berg 
32059bbb6f7SJohannes Berg 	mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
321667503ddSJohannes Berg 	wdev_lock(wdev);
322667503ddSJohannes Berg 	__cfg80211_sme_scan_done(dev);
323667503ddSJohannes Berg 	wdev_unlock(wdev);
32459bbb6f7SJohannes Berg 	mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
325667503ddSJohannes Berg }
326667503ddSJohannes Berg 
327667503ddSJohannes Berg void cfg80211_sme_rx_auth(struct net_device *dev,
328667503ddSJohannes Berg 			  const u8 *buf, size_t len)
3296829c878SJohannes Berg {
3306829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
3316829c878SJohannes Berg 	struct wiphy *wiphy = wdev->wiphy;
3326829c878SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3336829c878SJohannes Berg 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
3346829c878SJohannes Berg 	u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
3356829c878SJohannes Berg 
336667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
337667503ddSJohannes Berg 
3386829c878SJohannes Berg 	/* should only RX auth frames when connecting */
3396829c878SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
3406829c878SJohannes Berg 		return;
3416829c878SJohannes Berg 
3426829c878SJohannes Berg 	if (WARN_ON(!wdev->conn))
3436829c878SJohannes Berg 		return;
3446829c878SJohannes Berg 
3456829c878SJohannes Berg 	if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
3466829c878SJohannes Berg 	    wdev->conn->auto_auth &&
3476829c878SJohannes Berg 	    wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
3486829c878SJohannes Berg 		/* select automatically between only open, shared, leap */
3496829c878SJohannes Berg 		switch (wdev->conn->params.auth_type) {
3506829c878SJohannes Berg 		case NL80211_AUTHTYPE_OPEN_SYSTEM:
351fffd0934SJohannes Berg 			if (wdev->connect_keys)
3526829c878SJohannes Berg 				wdev->conn->params.auth_type =
3536829c878SJohannes Berg 					NL80211_AUTHTYPE_SHARED_KEY;
354fffd0934SJohannes Berg 			else
355fffd0934SJohannes Berg 				wdev->conn->params.auth_type =
356fffd0934SJohannes Berg 					NL80211_AUTHTYPE_NETWORK_EAP;
3576829c878SJohannes Berg 			break;
3586829c878SJohannes Berg 		case NL80211_AUTHTYPE_SHARED_KEY:
3596829c878SJohannes Berg 			wdev->conn->params.auth_type =
3606829c878SJohannes Berg 				NL80211_AUTHTYPE_NETWORK_EAP;
3616829c878SJohannes Berg 			break;
3626829c878SJohannes Berg 		default:
3636829c878SJohannes Berg 			/* huh? */
3646829c878SJohannes Berg 			wdev->conn->params.auth_type =
3656829c878SJohannes Berg 				NL80211_AUTHTYPE_OPEN_SYSTEM;
3666829c878SJohannes Berg 			break;
3676829c878SJohannes Berg 		}
3686829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
3696829c878SJohannes Berg 		schedule_work(&rdev->conn_work);
37019957bb3SJohannes Berg 	} else if (status_code != WLAN_STATUS_SUCCESS) {
3714bde0f7dSJohannes Berg 		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
372df7fc0f9SJohannes Berg 					  status_code, false, NULL);
37319957bb3SJohannes Berg 	} else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
3746829c878SJohannes Berg 		 wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
3756829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
3766829c878SJohannes Berg 		schedule_work(&rdev->conn_work);
3776829c878SJohannes Berg 	}
3786829c878SJohannes Berg }
379b23aa676SSamuel Ortiz 
380f401a6f7SJohannes Berg bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev)
381f401a6f7SJohannes Berg {
382f401a6f7SJohannes Berg 	struct wiphy *wiphy = wdev->wiphy;
383f401a6f7SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
384f401a6f7SJohannes Berg 
385f401a6f7SJohannes Berg 	if (WARN_ON(!wdev->conn))
386f401a6f7SJohannes Berg 		return false;
387f401a6f7SJohannes Berg 
388f401a6f7SJohannes Berg 	if (!wdev->conn->prev_bssid_valid)
389f401a6f7SJohannes Berg 		return false;
390f401a6f7SJohannes Berg 
391f401a6f7SJohannes Berg 	/*
392f401a6f7SJohannes Berg 	 * Some stupid APs don't accept reassoc, so we
393f401a6f7SJohannes Berg 	 * need to fall back to trying regular assoc.
394f401a6f7SJohannes Berg 	 */
395f401a6f7SJohannes Berg 	wdev->conn->prev_bssid_valid = false;
396f401a6f7SJohannes Berg 	wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
397f401a6f7SJohannes Berg 	schedule_work(&rdev->conn_work);
398f401a6f7SJohannes Berg 
399f401a6f7SJohannes Berg 	return true;
400f401a6f7SJohannes Berg }
401f401a6f7SJohannes Berg 
4027d930bc3SJohannes Berg void cfg80211_sme_failed_assoc(struct wireless_dev *wdev)
4037d930bc3SJohannes Berg {
4047d930bc3SJohannes Berg 	struct wiphy *wiphy = wdev->wiphy;
4057d930bc3SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4067d930bc3SJohannes Berg 
4077d930bc3SJohannes Berg 	wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL;
4087d930bc3SJohannes Berg 	schedule_work(&rdev->conn_work);
4097d930bc3SJohannes Berg }
4107d930bc3SJohannes Berg 
411667503ddSJohannes Berg void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
412b23aa676SSamuel Ortiz 			       const u8 *req_ie, size_t req_ie_len,
413b23aa676SSamuel Ortiz 			       const u8 *resp_ie, size_t resp_ie_len,
414df7fc0f9SJohannes Berg 			       u16 status, bool wextev,
415df7fc0f9SJohannes Berg 			       struct cfg80211_bss *bss)
416b23aa676SSamuel Ortiz {
417b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
4188b19e6caSLuis R. Rodriguez 	u8 *country_ie;
4193d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
420b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
421b23aa676SSamuel Ortiz #endif
422b23aa676SSamuel Ortiz 
423667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
424667503ddSJohannes Berg 
425074ac8dfSJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
426074ac8dfSJohannes Berg 		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
427b23aa676SSamuel Ortiz 		return;
428b23aa676SSamuel Ortiz 
429f7969969SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
430ea416a79SJohannes Berg 		return;
431ea416a79SJohannes Berg 
432e45cd82aSJohannes Berg 	nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
433e45cd82aSJohannes Berg 				    bssid, req_ie, req_ie_len,
434e45cd82aSJohannes Berg 				    resp_ie, resp_ie_len,
435667503ddSJohannes Berg 				    status, GFP_KERNEL);
436e45cd82aSJohannes Berg 
4373d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
438e45cd82aSJohannes Berg 	if (wextev) {
439e45cd82aSJohannes Berg 		if (req_ie && status == WLAN_STATUS_SUCCESS) {
440e45cd82aSJohannes Berg 			memset(&wrqu, 0, sizeof(wrqu));
441e45cd82aSJohannes Berg 			wrqu.data.length = req_ie_len;
4423409ff77SZhu Yi 			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
443e45cd82aSJohannes Berg 		}
444e45cd82aSJohannes Berg 
445e45cd82aSJohannes Berg 		if (resp_ie && status == WLAN_STATUS_SUCCESS) {
446e45cd82aSJohannes Berg 			memset(&wrqu, 0, sizeof(wrqu));
447e45cd82aSJohannes Berg 			wrqu.data.length = resp_ie_len;
448e45cd82aSJohannes Berg 			wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
449e45cd82aSJohannes Berg 		}
450e45cd82aSJohannes Berg 
451e45cd82aSJohannes Berg 		memset(&wrqu, 0, sizeof(wrqu));
452e45cd82aSJohannes Berg 		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
453f401a6f7SJohannes Berg 		if (bssid && status == WLAN_STATUS_SUCCESS) {
454e45cd82aSJohannes Berg 			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
455f401a6f7SJohannes Berg 			memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
456f401a6f7SJohannes Berg 			wdev->wext.prev_bssid_valid = true;
457f401a6f7SJohannes Berg 		}
458e45cd82aSJohannes Berg 		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
459e45cd82aSJohannes Berg 	}
460e45cd82aSJohannes Berg #endif
461e45cd82aSJohannes Berg 
462df7fc0f9SJohannes Berg 	if (wdev->current_bss) {
463df7fc0f9SJohannes Berg 		cfg80211_unhold_bss(wdev->current_bss);
464df7fc0f9SJohannes Berg 		cfg80211_put_bss(&wdev->current_bss->pub);
465df7fc0f9SJohannes Berg 		wdev->current_bss = NULL;
466df7fc0f9SJohannes Berg 	}
467df7fc0f9SJohannes Berg 
46819957bb3SJohannes Berg 	if (wdev->conn)
46919957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_IDLE;
47019957bb3SJohannes Berg 
471fffd0934SJohannes Berg 	if (status != WLAN_STATUS_SUCCESS) {
472fffd0934SJohannes Berg 		wdev->sme_state = CFG80211_SME_IDLE;
473415ad1efSDavid Kilroy 		if (wdev->conn)
474415ad1efSDavid Kilroy 			kfree(wdev->conn->ie);
475fffd0934SJohannes Berg 		kfree(wdev->conn);
476fffd0934SJohannes Berg 		wdev->conn = NULL;
477fffd0934SJohannes Berg 		kfree(wdev->connect_keys);
478fffd0934SJohannes Berg 		wdev->connect_keys = NULL;
4798dadadb7SJohannes Berg 		wdev->ssid_len = 0;
48095de817bSJohannes Berg 		cfg80211_put_bss(bss);
481fffd0934SJohannes Berg 		return;
482fffd0934SJohannes Berg 	}
483fffd0934SJohannes Berg 
484df7fc0f9SJohannes Berg 	if (!bss)
485ed9d0102SJouni Malinen 		bss = cfg80211_get_bss(wdev->wiphy,
486ed9d0102SJouni Malinen 				       wdev->conn ? wdev->conn->params.channel :
487ed9d0102SJouni Malinen 				       NULL,
488ed9d0102SJouni Malinen 				       bssid,
489b23aa676SSamuel Ortiz 				       wdev->ssid, wdev->ssid_len,
490b23aa676SSamuel Ortiz 				       WLAN_CAPABILITY_ESS,
491b23aa676SSamuel Ortiz 				       WLAN_CAPABILITY_ESS);
492b23aa676SSamuel Ortiz 
493b23aa676SSamuel Ortiz 	if (WARN_ON(!bss))
494b23aa676SSamuel Ortiz 		return;
495b23aa676SSamuel Ortiz 
49619957bb3SJohannes Berg 	cfg80211_hold_bss(bss_from_pub(bss));
49719957bb3SJohannes Berg 	wdev->current_bss = bss_from_pub(bss);
498b23aa676SSamuel Ortiz 
499b23aa676SSamuel Ortiz 	wdev->sme_state = CFG80211_SME_CONNECTED;
500fffd0934SJohannes Berg 	cfg80211_upload_connect_keys(wdev);
5018b19e6caSLuis R. Rodriguez 
5028b19e6caSLuis R. Rodriguez 	country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
5038b19e6caSLuis R. Rodriguez 
5048b19e6caSLuis R. Rodriguez 	if (!country_ie)
5058b19e6caSLuis R. Rodriguez 		return;
5068b19e6caSLuis R. Rodriguez 
5078b19e6caSLuis R. Rodriguez 	/*
5088b19e6caSLuis R. Rodriguez 	 * ieee80211_bss_get_ie() ensures we can access:
5098b19e6caSLuis R. Rodriguez 	 * - country_ie + 2, the start of the country ie data, and
5108b19e6caSLuis R. Rodriguez 	 * - and country_ie[1] which is the IE length
5118b19e6caSLuis R. Rodriguez 	 */
5128b19e6caSLuis R. Rodriguez 	regulatory_hint_11d(wdev->wiphy,
51384920e3eSLuis R. Rodriguez 			    bss->channel->band,
5148b19e6caSLuis R. Rodriguez 			    country_ie + 2,
5158b19e6caSLuis R. Rodriguez 			    country_ie[1]);
516b23aa676SSamuel Ortiz }
517f2129354SJohannes Berg 
518f2129354SJohannes Berg void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
519f2129354SJohannes Berg 			     const u8 *req_ie, size_t req_ie_len,
520f2129354SJohannes Berg 			     const u8 *resp_ie, size_t resp_ie_len,
521f2129354SJohannes Berg 			     u16 status, gfp_t gfp)
522f2129354SJohannes Berg {
523667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
524667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
525667503ddSJohannes Berg 	struct cfg80211_event *ev;
526667503ddSJohannes Berg 	unsigned long flags;
527667503ddSJohannes Berg 
528f7969969SJohannes Berg 	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
529f7969969SJohannes Berg 
530667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
531667503ddSJohannes Berg 	if (!ev)
532667503ddSJohannes Berg 		return;
533667503ddSJohannes Berg 
534667503ddSJohannes Berg 	ev->type = EVENT_CONNECT_RESULT;
53516a832e7SZhu Yi 	if (bssid)
536667503ddSJohannes Berg 		memcpy(ev->cr.bssid, bssid, ETH_ALEN);
5377834704bSNishant Sarmukadam 	if (req_ie_len) {
538667503ddSJohannes Berg 		ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
539667503ddSJohannes Berg 		ev->cr.req_ie_len = req_ie_len;
540667503ddSJohannes Berg 		memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
5417834704bSNishant Sarmukadam 	}
5427834704bSNishant Sarmukadam 	if (resp_ie_len) {
543667503ddSJohannes Berg 		ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
544667503ddSJohannes Berg 		ev->cr.resp_ie_len = resp_ie_len;
545667503ddSJohannes Berg 		memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
5467834704bSNishant Sarmukadam 	}
547667503ddSJohannes Berg 	ev->cr.status = status;
548667503ddSJohannes Berg 
549667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
550667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
551667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
552e60d7443SAlban Browaeys 	queue_work(cfg80211_wq, &rdev->event_work);
553f2129354SJohannes Berg }
554b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_connect_result);
555b23aa676SSamuel Ortiz 
556ed9d0102SJouni Malinen void __cfg80211_roamed(struct wireless_dev *wdev,
557adbde344SVasanthakumar Thiagarajan 		       struct cfg80211_bss *bss,
558b23aa676SSamuel Ortiz 		       const u8 *req_ie, size_t req_ie_len,
559667503ddSJohannes Berg 		       const u8 *resp_ie, size_t resp_ie_len)
560b23aa676SSamuel Ortiz {
5613d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
562b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
563b23aa676SSamuel Ortiz #endif
564667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
565667503ddSJohannes Berg 
566074ac8dfSJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
567074ac8dfSJohannes Berg 		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
568adbde344SVasanthakumar Thiagarajan 		goto out;
569b23aa676SSamuel Ortiz 
570f7969969SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTED)
571adbde344SVasanthakumar Thiagarajan 		goto out;
572b23aa676SSamuel Ortiz 
573b23aa676SSamuel Ortiz 	/* internal error -- how did we get to CONNECTED w/o BSS? */
574b23aa676SSamuel Ortiz 	if (WARN_ON(!wdev->current_bss)) {
575adbde344SVasanthakumar Thiagarajan 		goto out;
576b23aa676SSamuel Ortiz 	}
577b23aa676SSamuel Ortiz 
578b23aa676SSamuel Ortiz 	cfg80211_unhold_bss(wdev->current_bss);
57919957bb3SJohannes Berg 	cfg80211_put_bss(&wdev->current_bss->pub);
580b23aa676SSamuel Ortiz 	wdev->current_bss = NULL;
581b23aa676SSamuel Ortiz 
58219957bb3SJohannes Berg 	cfg80211_hold_bss(bss_from_pub(bss));
58319957bb3SJohannes Berg 	wdev->current_bss = bss_from_pub(bss);
584b23aa676SSamuel Ortiz 
585adbde344SVasanthakumar Thiagarajan 	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid,
586667503ddSJohannes Berg 			    req_ie, req_ie_len, resp_ie, resp_ie_len,
587667503ddSJohannes Berg 			    GFP_KERNEL);
588b23aa676SSamuel Ortiz 
5893d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
590b23aa676SSamuel Ortiz 	if (req_ie) {
591b23aa676SSamuel Ortiz 		memset(&wrqu, 0, sizeof(wrqu));
592b23aa676SSamuel Ortiz 		wrqu.data.length = req_ie_len;
5933409ff77SZhu Yi 		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
594667503ddSJohannes Berg 				    &wrqu, req_ie);
595b23aa676SSamuel Ortiz 	}
596b23aa676SSamuel Ortiz 
597b23aa676SSamuel Ortiz 	if (resp_ie) {
598b23aa676SSamuel Ortiz 		memset(&wrqu, 0, sizeof(wrqu));
599b23aa676SSamuel Ortiz 		wrqu.data.length = resp_ie_len;
600667503ddSJohannes Berg 		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
601667503ddSJohannes Berg 				    &wrqu, resp_ie);
602b23aa676SSamuel Ortiz 	}
603b23aa676SSamuel Ortiz 
604b23aa676SSamuel Ortiz 	memset(&wrqu, 0, sizeof(wrqu));
605b23aa676SSamuel Ortiz 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
606adbde344SVasanthakumar Thiagarajan 	memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN);
607adbde344SVasanthakumar Thiagarajan 	memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN);
608f401a6f7SJohannes Berg 	wdev->wext.prev_bssid_valid = true;
609667503ddSJohannes Berg 	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
610b23aa676SSamuel Ortiz #endif
611adbde344SVasanthakumar Thiagarajan 
612adbde344SVasanthakumar Thiagarajan 	return;
613adbde344SVasanthakumar Thiagarajan out:
614adbde344SVasanthakumar Thiagarajan 	cfg80211_put_bss(bss);
615b23aa676SSamuel Ortiz }
616667503ddSJohannes Berg 
617ed9d0102SJouni Malinen void cfg80211_roamed(struct net_device *dev,
618ed9d0102SJouni Malinen 		     struct ieee80211_channel *channel,
619ed9d0102SJouni Malinen 		     const u8 *bssid,
620667503ddSJohannes Berg 		     const u8 *req_ie, size_t req_ie_len,
621667503ddSJohannes Berg 		     const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
622667503ddSJohannes Berg {
623667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
624adbde344SVasanthakumar Thiagarajan 	struct cfg80211_bss *bss;
625adbde344SVasanthakumar Thiagarajan 
626adbde344SVasanthakumar Thiagarajan 	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
627adbde344SVasanthakumar Thiagarajan 
628adbde344SVasanthakumar Thiagarajan 	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
629adbde344SVasanthakumar Thiagarajan 			       wdev->ssid_len, WLAN_CAPABILITY_ESS,
630adbde344SVasanthakumar Thiagarajan 			       WLAN_CAPABILITY_ESS);
631adbde344SVasanthakumar Thiagarajan 	if (WARN_ON(!bss))
632adbde344SVasanthakumar Thiagarajan 		return;
633adbde344SVasanthakumar Thiagarajan 
634adbde344SVasanthakumar Thiagarajan 	cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie,
635adbde344SVasanthakumar Thiagarajan 			    resp_ie_len, gfp);
636adbde344SVasanthakumar Thiagarajan }
637adbde344SVasanthakumar Thiagarajan EXPORT_SYMBOL(cfg80211_roamed);
638adbde344SVasanthakumar Thiagarajan 
639adbde344SVasanthakumar Thiagarajan void cfg80211_roamed_bss(struct net_device *dev,
640adbde344SVasanthakumar Thiagarajan 			 struct cfg80211_bss *bss, const u8 *req_ie,
641adbde344SVasanthakumar Thiagarajan 			 size_t req_ie_len, const u8 *resp_ie,
642adbde344SVasanthakumar Thiagarajan 			 size_t resp_ie_len, gfp_t gfp)
643adbde344SVasanthakumar Thiagarajan {
644adbde344SVasanthakumar Thiagarajan 	struct wireless_dev *wdev = dev->ieee80211_ptr;
645667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
646667503ddSJohannes Berg 	struct cfg80211_event *ev;
647667503ddSJohannes Berg 	unsigned long flags;
648667503ddSJohannes Berg 
649f7969969SJohannes Berg 	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
650f7969969SJohannes Berg 
651adbde344SVasanthakumar Thiagarajan 	if (WARN_ON(!bss))
652667503ddSJohannes Berg 		return;
653667503ddSJohannes Berg 
654adbde344SVasanthakumar Thiagarajan 	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
655adbde344SVasanthakumar Thiagarajan 	if (!ev) {
656adbde344SVasanthakumar Thiagarajan 		cfg80211_put_bss(bss);
657adbde344SVasanthakumar Thiagarajan 		return;
658adbde344SVasanthakumar Thiagarajan 	}
659adbde344SVasanthakumar Thiagarajan 
660667503ddSJohannes Berg 	ev->type = EVENT_ROAMED;
661667503ddSJohannes Berg 	ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
662667503ddSJohannes Berg 	ev->rm.req_ie_len = req_ie_len;
663667503ddSJohannes Berg 	memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len);
664667503ddSJohannes Berg 	ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
665667503ddSJohannes Berg 	ev->rm.resp_ie_len = resp_ie_len;
666667503ddSJohannes Berg 	memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len);
667adbde344SVasanthakumar Thiagarajan 	ev->rm.bss = bss;
668667503ddSJohannes Berg 
669667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
670667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
671667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
672e60d7443SAlban Browaeys 	queue_work(cfg80211_wq, &rdev->event_work);
673667503ddSJohannes Berg }
674adbde344SVasanthakumar Thiagarajan EXPORT_SYMBOL(cfg80211_roamed_bss);
675b23aa676SSamuel Ortiz 
676667503ddSJohannes Berg void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
6776829c878SJohannes Berg 			     size_t ie_len, u16 reason, bool from_ap)
678b23aa676SSamuel Ortiz {
679b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
680fffd0934SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
681fffd0934SJohannes Berg 	int i;
6823d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
683b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
684b23aa676SSamuel Ortiz #endif
685b23aa676SSamuel Ortiz 
686667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
687667503ddSJohannes Berg 
688074ac8dfSJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
689074ac8dfSJohannes Berg 		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
690b23aa676SSamuel Ortiz 		return;
691b23aa676SSamuel Ortiz 
692f7969969SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTED)
693b23aa676SSamuel Ortiz 		return;
694b23aa676SSamuel Ortiz 
695b23aa676SSamuel Ortiz 	if (wdev->current_bss) {
696b23aa676SSamuel Ortiz 		cfg80211_unhold_bss(wdev->current_bss);
69719957bb3SJohannes Berg 		cfg80211_put_bss(&wdev->current_bss->pub);
698b23aa676SSamuel Ortiz 	}
699b23aa676SSamuel Ortiz 
700b23aa676SSamuel Ortiz 	wdev->current_bss = NULL;
701b23aa676SSamuel Ortiz 	wdev->sme_state = CFG80211_SME_IDLE;
7028dadadb7SJohannes Berg 	wdev->ssid_len = 0;
703b23aa676SSamuel Ortiz 
7046829c878SJohannes Berg 	if (wdev->conn) {
7056829c878SJohannes Berg 		kfree(wdev->conn->ie);
7066829c878SJohannes Berg 		wdev->conn->ie = NULL;
70719957bb3SJohannes Berg 		kfree(wdev->conn);
70819957bb3SJohannes Berg 		wdev->conn = NULL;
7096829c878SJohannes Berg 	}
7106829c878SJohannes Berg 
711fffd0934SJohannes Berg 	nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
712fffd0934SJohannes Berg 
713fffd0934SJohannes Berg 	/*
714fffd0934SJohannes Berg 	 * Delete all the keys ... pairwise keys can't really
715fffd0934SJohannes Berg 	 * exist any more anyway, but default keys might.
716fffd0934SJohannes Berg 	 */
717fffd0934SJohannes Berg 	if (rdev->ops->del_key)
718fffd0934SJohannes Berg 		for (i = 0; i < 6; i++)
719e31b8213SJohannes Berg 			rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL);
720b23aa676SSamuel Ortiz 
7213d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
722b23aa676SSamuel Ortiz 	memset(&wrqu, 0, sizeof(wrqu));
723b23aa676SSamuel Ortiz 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
724b23aa676SSamuel Ortiz 	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
7255f612033SAbhijeet Kolekar 	wdev->wext.connect.ssid_len = 0;
726b23aa676SSamuel Ortiz #endif
72709d989d1SLuis R. Rodriguez 
72809d989d1SLuis R. Rodriguez 	schedule_work(&cfg80211_disconnect_work);
729b23aa676SSamuel Ortiz }
730b23aa676SSamuel Ortiz 
731b23aa676SSamuel Ortiz void cfg80211_disconnected(struct net_device *dev, u16 reason,
732b23aa676SSamuel Ortiz 			   u8 *ie, size_t ie_len, gfp_t gfp)
733b23aa676SSamuel Ortiz {
734667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
735667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
736667503ddSJohannes Berg 	struct cfg80211_event *ev;
737667503ddSJohannes Berg 	unsigned long flags;
738667503ddSJohannes Berg 
739f7969969SJohannes Berg 	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
740f7969969SJohannes Berg 
741667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + ie_len, gfp);
742667503ddSJohannes Berg 	if (!ev)
743667503ddSJohannes Berg 		return;
744667503ddSJohannes Berg 
745667503ddSJohannes Berg 	ev->type = EVENT_DISCONNECTED;
746667503ddSJohannes Berg 	ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
747667503ddSJohannes Berg 	ev->dc.ie_len = ie_len;
748667503ddSJohannes Berg 	memcpy((void *)ev->dc.ie, ie, ie_len);
749667503ddSJohannes Berg 	ev->dc.reason = reason;
750667503ddSJohannes Berg 
751667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
752667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
753667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
754e60d7443SAlban Browaeys 	queue_work(cfg80211_wq, &rdev->event_work);
755b23aa676SSamuel Ortiz }
756b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_disconnected);
757b23aa676SSamuel Ortiz 
758667503ddSJohannes Berg int __cfg80211_connect(struct cfg80211_registered_device *rdev,
759b23aa676SSamuel Ortiz 		       struct net_device *dev,
760fffd0934SJohannes Berg 		       struct cfg80211_connect_params *connect,
761f401a6f7SJohannes Berg 		       struct cfg80211_cached_keys *connkeys,
762f401a6f7SJohannes Berg 		       const u8 *prev_bssid)
763b23aa676SSamuel Ortiz {
764b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
765bbac31f4SJohannes Berg 	struct cfg80211_bss *bss = NULL;
766667503ddSJohannes Berg 	int err;
767667503ddSJohannes Berg 
768667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
769b23aa676SSamuel Ortiz 
770b23aa676SSamuel Ortiz 	if (wdev->sme_state != CFG80211_SME_IDLE)
771b23aa676SSamuel Ortiz 		return -EALREADY;
772b23aa676SSamuel Ortiz 
773fffd0934SJohannes Berg 	if (WARN_ON(wdev->connect_keys)) {
774fffd0934SJohannes Berg 		kfree(wdev->connect_keys);
775fffd0934SJohannes Berg 		wdev->connect_keys = NULL;
776fffd0934SJohannes Berg 	}
777fffd0934SJohannes Berg 
7787e7c8926SBen Greear 	cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
7797e7c8926SBen Greear 				  rdev->wiphy.ht_capa_mod_mask);
7807e7c8926SBen Greear 
781fffd0934SJohannes Berg 	if (connkeys && connkeys->def >= 0) {
782fffd0934SJohannes Berg 		int idx;
783bcba8eaeSSamuel Ortiz 		u32 cipher;
784fffd0934SJohannes Berg 
785fffd0934SJohannes Berg 		idx = connkeys->def;
786bcba8eaeSSamuel Ortiz 		cipher = connkeys->params[idx].cipher;
787fffd0934SJohannes Berg 		/* If given a WEP key we may need it for shared key auth */
788bcba8eaeSSamuel Ortiz 		if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
789bcba8eaeSSamuel Ortiz 		    cipher == WLAN_CIPHER_SUITE_WEP104) {
790fffd0934SJohannes Berg 			connect->key_idx = idx;
791fffd0934SJohannes Berg 			connect->key = connkeys->params[idx].key;
792fffd0934SJohannes Berg 			connect->key_len = connkeys->params[idx].key_len;
793bcba8eaeSSamuel Ortiz 
794bcba8eaeSSamuel Ortiz 			/*
795bcba8eaeSSamuel Ortiz 			 * If ciphers are not set (e.g. when going through
796bcba8eaeSSamuel Ortiz 			 * iwconfig), we have to set them appropriately here.
797bcba8eaeSSamuel Ortiz 			 */
798bcba8eaeSSamuel Ortiz 			if (connect->crypto.cipher_group == 0)
799bcba8eaeSSamuel Ortiz 				connect->crypto.cipher_group = cipher;
800bcba8eaeSSamuel Ortiz 
801bcba8eaeSSamuel Ortiz 			if (connect->crypto.n_ciphers_pairwise == 0) {
802bcba8eaeSSamuel Ortiz 				connect->crypto.n_ciphers_pairwise = 1;
803bcba8eaeSSamuel Ortiz 				connect->crypto.ciphers_pairwise[0] = cipher;
804bcba8eaeSSamuel Ortiz 			}
805fffd0934SJohannes Berg 		}
806fffd0934SJohannes Berg 	}
807fffd0934SJohannes Berg 
808b23aa676SSamuel Ortiz 	if (!rdev->ops->connect) {
8096829c878SJohannes Berg 		if (!rdev->ops->auth || !rdev->ops->assoc)
810b23aa676SSamuel Ortiz 			return -EOPNOTSUPP;
8116829c878SJohannes Berg 
81219957bb3SJohannes Berg 		if (WARN_ON(wdev->conn))
81319957bb3SJohannes Berg 			return -EINPROGRESS;
81419957bb3SJohannes Berg 
8156829c878SJohannes Berg 		wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
8166829c878SJohannes Berg 		if (!wdev->conn)
8176829c878SJohannes Berg 			return -ENOMEM;
8186829c878SJohannes Berg 
8196829c878SJohannes Berg 		/*
8206829c878SJohannes Berg 		 * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
8216829c878SJohannes Berg 		 */
8226829c878SJohannes Berg 		memcpy(&wdev->conn->params, connect, sizeof(*connect));
8236829c878SJohannes Berg 		if (connect->bssid) {
8246829c878SJohannes Berg 			wdev->conn->params.bssid = wdev->conn->bssid;
8256829c878SJohannes Berg 			memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
8266829c878SJohannes Berg 		}
8276829c878SJohannes Berg 
8286829c878SJohannes Berg 		if (connect->ie) {
8296829c878SJohannes Berg 			wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
8306829c878SJohannes Berg 						GFP_KERNEL);
8316829c878SJohannes Berg 			wdev->conn->params.ie = wdev->conn->ie;
83219957bb3SJohannes Berg 			if (!wdev->conn->ie) {
83319957bb3SJohannes Berg 				kfree(wdev->conn);
83419957bb3SJohannes Berg 				wdev->conn = NULL;
8356829c878SJohannes Berg 				return -ENOMEM;
8366829c878SJohannes Berg 			}
83719957bb3SJohannes Berg 		}
8386829c878SJohannes Berg 
8396829c878SJohannes Berg 		if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
8406829c878SJohannes Berg 			wdev->conn->auto_auth = true;
8416829c878SJohannes Berg 			/* start with open system ... should mostly work */
8426829c878SJohannes Berg 			wdev->conn->params.auth_type =
8436829c878SJohannes Berg 				NL80211_AUTHTYPE_OPEN_SYSTEM;
8446829c878SJohannes Berg 		} else {
8456829c878SJohannes Berg 			wdev->conn->auto_auth = false;
8466829c878SJohannes Berg 		}
8476829c878SJohannes Berg 
8486829c878SJohannes Berg 		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
8496829c878SJohannes Berg 		wdev->ssid_len = connect->ssid_len;
8506829c878SJohannes Berg 		wdev->conn->params.ssid = wdev->ssid;
8516829c878SJohannes Berg 		wdev->conn->params.ssid_len = connect->ssid_len;
8526829c878SJohannes Berg 
8538bb89485SJohannes Berg 		/* see if we have the bss already */
854bbac31f4SJohannes Berg 		bss = cfg80211_get_conn_bss(wdev);
8556829c878SJohannes Berg 
8566829c878SJohannes Berg 		wdev->sme_state = CFG80211_SME_CONNECTING;
857fffd0934SJohannes Berg 		wdev->connect_keys = connkeys;
8586829c878SJohannes Berg 
859f401a6f7SJohannes Berg 		if (prev_bssid) {
860f401a6f7SJohannes Berg 			memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
861f401a6f7SJohannes Berg 			wdev->conn->prev_bssid_valid = true;
862f401a6f7SJohannes Berg 		}
863f401a6f7SJohannes Berg 
864bbac31f4SJohannes Berg 		/* we're good if we have a matching bss struct */
865bbac31f4SJohannes Berg 		if (bss) {
8666829c878SJohannes Berg 			wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
8676829c878SJohannes Berg 			err = cfg80211_conn_do_work(wdev);
868bbac31f4SJohannes Berg 			cfg80211_put_bss(bss);
8696829c878SJohannes Berg 		} else {
8706829c878SJohannes Berg 			/* otherwise we'll need to scan for the AP first */
8716829c878SJohannes Berg 			err = cfg80211_conn_scan(wdev);
8726829c878SJohannes Berg 			/*
8736829c878SJohannes Berg 			 * If we can't scan right now, then we need to scan again
8746829c878SJohannes Berg 			 * after the current scan finished, since the parameters
8756829c878SJohannes Berg 			 * changed (unless we find a good AP anyway).
8766829c878SJohannes Berg 			 */
8776829c878SJohannes Berg 			if (err == -EBUSY) {
8786829c878SJohannes Berg 				err = 0;
8796829c878SJohannes Berg 				wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
8806829c878SJohannes Berg 			}
8816829c878SJohannes Berg 		}
88219957bb3SJohannes Berg 		if (err) {
883415ad1efSDavid Kilroy 			kfree(wdev->conn->ie);
88419957bb3SJohannes Berg 			kfree(wdev->conn);
88519957bb3SJohannes Berg 			wdev->conn = NULL;
8866829c878SJohannes Berg 			wdev->sme_state = CFG80211_SME_IDLE;
887fffd0934SJohannes Berg 			wdev->connect_keys = NULL;
8888dadadb7SJohannes Berg 			wdev->ssid_len = 0;
88919957bb3SJohannes Berg 		}
8906829c878SJohannes Berg 
8916829c878SJohannes Berg 		return err;
892b23aa676SSamuel Ortiz 	} else {
893b23aa676SSamuel Ortiz 		wdev->sme_state = CFG80211_SME_CONNECTING;
894fffd0934SJohannes Berg 		wdev->connect_keys = connkeys;
895b23aa676SSamuel Ortiz 		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
896b23aa676SSamuel Ortiz 		if (err) {
897fffd0934SJohannes Berg 			wdev->connect_keys = NULL;
898b23aa676SSamuel Ortiz 			wdev->sme_state = CFG80211_SME_IDLE;
899b23aa676SSamuel Ortiz 			return err;
900b23aa676SSamuel Ortiz 		}
901b23aa676SSamuel Ortiz 
902b23aa676SSamuel Ortiz 		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
903b23aa676SSamuel Ortiz 		wdev->ssid_len = connect->ssid_len;
904b23aa676SSamuel Ortiz 
905b23aa676SSamuel Ortiz 		return 0;
906b23aa676SSamuel Ortiz 	}
9076829c878SJohannes Berg }
908b23aa676SSamuel Ortiz 
909667503ddSJohannes Berg int cfg80211_connect(struct cfg80211_registered_device *rdev,
910667503ddSJohannes Berg 		     struct net_device *dev,
911fffd0934SJohannes Berg 		     struct cfg80211_connect_params *connect,
912fffd0934SJohannes Berg 		     struct cfg80211_cached_keys *connkeys)
913667503ddSJohannes Berg {
914667503ddSJohannes Berg 	int err;
915667503ddSJohannes Berg 
91659bbb6f7SJohannes Berg 	mutex_lock(&rdev->devlist_mtx);
917667503ddSJohannes Berg 	wdev_lock(dev->ieee80211_ptr);
918f401a6f7SJohannes Berg 	err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL);
919667503ddSJohannes Berg 	wdev_unlock(dev->ieee80211_ptr);
92059bbb6f7SJohannes Berg 	mutex_unlock(&rdev->devlist_mtx);
921667503ddSJohannes Berg 
922667503ddSJohannes Berg 	return err;
923667503ddSJohannes Berg }
924667503ddSJohannes Berg 
925667503ddSJohannes Berg int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
926f2129354SJohannes Berg 			  struct net_device *dev, u16 reason, bool wextev)
927b23aa676SSamuel Ortiz {
9286829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
929b23aa676SSamuel Ortiz 	int err;
930b23aa676SSamuel Ortiz 
931667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
932667503ddSJohannes Berg 
9336829c878SJohannes Berg 	if (wdev->sme_state == CFG80211_SME_IDLE)
9346829c878SJohannes Berg 		return -EINVAL;
9356829c878SJohannes Berg 
936fffd0934SJohannes Berg 	kfree(wdev->connect_keys);
937fffd0934SJohannes Berg 	wdev->connect_keys = NULL;
938fffd0934SJohannes Berg 
939b23aa676SSamuel Ortiz 	if (!rdev->ops->disconnect) {
94019957bb3SJohannes Berg 		if (!rdev->ops->deauth)
94119957bb3SJohannes Berg 			return -EOPNOTSUPP;
9426829c878SJohannes Berg 
94319957bb3SJohannes Berg 		/* was it connected by userspace SME? */
94419957bb3SJohannes Berg 		if (!wdev->conn) {
94519957bb3SJohannes Berg 			cfg80211_mlme_down(rdev, dev);
94619957bb3SJohannes Berg 			return 0;
94719957bb3SJohannes Berg 		}
9486829c878SJohannes Berg 
9496829c878SJohannes Berg 		if (wdev->sme_state == CFG80211_SME_CONNECTING &&
9506829c878SJohannes Berg 		    (wdev->conn->state == CFG80211_CONN_SCANNING ||
9516829c878SJohannes Berg 		     wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) {
9526829c878SJohannes Berg 			wdev->sme_state = CFG80211_SME_IDLE;
953415ad1efSDavid Kilroy 			kfree(wdev->conn->ie);
95419957bb3SJohannes Berg 			kfree(wdev->conn);
95519957bb3SJohannes Berg 			wdev->conn = NULL;
9568dadadb7SJohannes Berg 			wdev->ssid_len = 0;
9576829c878SJohannes Berg 			return 0;
9586829c878SJohannes Berg 		}
9596829c878SJohannes Berg 
9606829c878SJohannes Berg 		/* wdev->conn->params.bssid must be set if > SCANNING */
961667503ddSJohannes Berg 		err = __cfg80211_mlme_deauth(rdev, dev,
962667503ddSJohannes Berg 					     wdev->conn->params.bssid,
963d5cdfacbSJouni Malinen 					     NULL, 0, reason, false);
9646829c878SJohannes Berg 		if (err)
9656829c878SJohannes Berg 			return err;
966b23aa676SSamuel Ortiz 	} else {
967b23aa676SSamuel Ortiz 		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
968b23aa676SSamuel Ortiz 		if (err)
969b23aa676SSamuel Ortiz 			return err;
970b23aa676SSamuel Ortiz 	}
971b23aa676SSamuel Ortiz 
9726829c878SJohannes Berg 	if (wdev->sme_state == CFG80211_SME_CONNECTED)
973667503ddSJohannes Berg 		__cfg80211_disconnected(dev, NULL, 0, 0, false);
9746829c878SJohannes Berg 	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
975f2129354SJohannes Berg 		__cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
9766829c878SJohannes Berg 					  WLAN_STATUS_UNSPECIFIED_FAILURE,
977df7fc0f9SJohannes Berg 					  wextev, NULL);
978b23aa676SSamuel Ortiz 
979b23aa676SSamuel Ortiz 	return 0;
980b23aa676SSamuel Ortiz }
98119957bb3SJohannes Berg 
982667503ddSJohannes Berg int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
983667503ddSJohannes Berg 			struct net_device *dev,
984667503ddSJohannes Berg 			u16 reason, bool wextev)
985667503ddSJohannes Berg {
986667503ddSJohannes Berg 	int err;
987667503ddSJohannes Berg 
988667503ddSJohannes Berg 	wdev_lock(dev->ieee80211_ptr);
989667503ddSJohannes Berg 	err = __cfg80211_disconnect(rdev, dev, reason, wextev);
990667503ddSJohannes Berg 	wdev_unlock(dev->ieee80211_ptr);
991667503ddSJohannes Berg 
992667503ddSJohannes Berg 	return err;
993667503ddSJohannes Berg }
994667503ddSJohannes Berg 
99595de817bSJohannes Berg void cfg80211_sme_disassoc(struct net_device *dev,
99695de817bSJohannes Berg 			   struct cfg80211_internal_bss *bss)
99719957bb3SJohannes Berg {
99819957bb3SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
99919957bb3SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
100019957bb3SJohannes Berg 	u8 bssid[ETH_ALEN];
100119957bb3SJohannes Berg 
1002667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
1003667503ddSJohannes Berg 
100419957bb3SJohannes Berg 	if (!wdev->conn)
100519957bb3SJohannes Berg 		return;
100619957bb3SJohannes Berg 
100719957bb3SJohannes Berg 	if (wdev->conn->state == CFG80211_CONN_IDLE)
100819957bb3SJohannes Berg 		return;
100919957bb3SJohannes Berg 
101019957bb3SJohannes Berg 	/*
101119957bb3SJohannes Berg 	 * Ok, so the association was made by this SME -- we don't
101219957bb3SJohannes Berg 	 * want it any more so deauthenticate too.
101319957bb3SJohannes Berg 	 */
101419957bb3SJohannes Berg 
101595de817bSJohannes Berg 	memcpy(bssid, bss->pub.bssid, ETH_ALEN);
101619957bb3SJohannes Berg 
101795de817bSJohannes Berg 	__cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
101895de817bSJohannes Berg 			       WLAN_REASON_DEAUTH_LEAVING, false);
101919957bb3SJohannes Berg }
1020