xref: /openbmc/linux/net/wireless/sme.c (revision 463d0183)
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>
10b23aa676SSamuel Ortiz #include <linux/workqueue.h>
11b23aa676SSamuel Ortiz #include <net/cfg80211.h>
12b23aa676SSamuel Ortiz #include <net/rtnetlink.h>
13b23aa676SSamuel Ortiz #include "nl80211.h"
14b23aa676SSamuel Ortiz 
156829c878SJohannes Berg struct cfg80211_conn {
166829c878SJohannes Berg 	struct cfg80211_connect_params params;
176829c878SJohannes Berg 	/* these are sub-states of the _CONNECTING sme_state */
186829c878SJohannes Berg 	enum {
196829c878SJohannes Berg 		CFG80211_CONN_IDLE,
206829c878SJohannes Berg 		CFG80211_CONN_SCANNING,
216829c878SJohannes Berg 		CFG80211_CONN_SCAN_AGAIN,
226829c878SJohannes Berg 		CFG80211_CONN_AUTHENTICATE_NEXT,
236829c878SJohannes Berg 		CFG80211_CONN_AUTHENTICATING,
246829c878SJohannes Berg 		CFG80211_CONN_ASSOCIATE_NEXT,
256829c878SJohannes Berg 		CFG80211_CONN_ASSOCIATING,
266829c878SJohannes Berg 	} state;
276829c878SJohannes Berg 	u8 bssid[ETH_ALEN];
286829c878SJohannes Berg 	u8 *ie;
296829c878SJohannes Berg 	size_t ie_len;
306829c878SJohannes Berg 	bool auto_auth;
316829c878SJohannes Berg };
326829c878SJohannes Berg 
336829c878SJohannes Berg 
346829c878SJohannes Berg static int cfg80211_conn_scan(struct wireless_dev *wdev)
356829c878SJohannes Berg {
3679c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
376829c878SJohannes Berg 	struct cfg80211_scan_request *request;
386829c878SJohannes Berg 	int n_channels, err;
396829c878SJohannes Berg 
406829c878SJohannes Berg 	ASSERT_RTNL();
4179c97e97SJohannes Berg 	ASSERT_RDEV_LOCK(rdev);
42667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
436829c878SJohannes Berg 
4479c97e97SJohannes Berg 	if (rdev->scan_req)
456829c878SJohannes Berg 		return -EBUSY;
466829c878SJohannes Berg 
476829c878SJohannes Berg 	if (wdev->conn->params.channel) {
486829c878SJohannes Berg 		n_channels = 1;
496829c878SJohannes Berg 	} else {
506829c878SJohannes Berg 		enum ieee80211_band band;
516829c878SJohannes Berg 		n_channels = 0;
526829c878SJohannes Berg 
536829c878SJohannes Berg 		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
546829c878SJohannes Berg 			if (!wdev->wiphy->bands[band])
556829c878SJohannes Berg 				continue;
566829c878SJohannes Berg 			n_channels += wdev->wiphy->bands[band]->n_channels;
576829c878SJohannes Berg 		}
586829c878SJohannes Berg 	}
596829c878SJohannes Berg 	request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
606829c878SJohannes Berg 			  sizeof(request->channels[0]) * n_channels,
616829c878SJohannes Berg 			  GFP_KERNEL);
626829c878SJohannes Berg 	if (!request)
636829c878SJohannes Berg 		return -ENOMEM;
646829c878SJohannes Berg 
656829c878SJohannes Berg 	request->channels = (void *)((char *)request + sizeof(*request));
666829c878SJohannes Berg 	if (wdev->conn->params.channel)
676829c878SJohannes Berg 		request->channels[0] = wdev->conn->params.channel;
686829c878SJohannes Berg 	else {
696829c878SJohannes Berg 		int i = 0, j;
706829c878SJohannes Berg 		enum ieee80211_band band;
716829c878SJohannes Berg 
726829c878SJohannes Berg 		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
736829c878SJohannes Berg 			if (!wdev->wiphy->bands[band])
746829c878SJohannes Berg 				continue;
756829c878SJohannes Berg 			for (j = 0; j < wdev->wiphy->bands[band]->n_channels;
766829c878SJohannes Berg 			     i++, j++)
776829c878SJohannes Berg 				request->channels[i] =
786829c878SJohannes Berg 					&wdev->wiphy->bands[band]->channels[j];
796829c878SJohannes Berg 		}
806829c878SJohannes Berg 	}
816829c878SJohannes Berg 	request->n_channels = n_channels;
826829c878SJohannes Berg 	request->ssids = (void *)(request->channels + n_channels);
836829c878SJohannes Berg 	request->n_ssids = 1;
846829c878SJohannes Berg 
856829c878SJohannes Berg 	memcpy(request->ssids[0].ssid, wdev->conn->params.ssid,
866829c878SJohannes Berg 		wdev->conn->params.ssid_len);
876829c878SJohannes Berg 	request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
886829c878SJohannes Berg 
89*463d0183SJohannes Berg 	request->dev = wdev->netdev;
9079c97e97SJohannes Berg 	request->wiphy = &rdev->wiphy;
916829c878SJohannes Berg 
9279c97e97SJohannes Berg 	rdev->scan_req = request;
936829c878SJohannes Berg 
9479c97e97SJohannes Berg 	err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request);
956829c878SJohannes Berg 	if (!err) {
966829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_SCANNING;
9779c97e97SJohannes Berg 		nl80211_send_scan_start(rdev, wdev->netdev);
98*463d0183SJohannes Berg 		dev_hold(wdev->netdev);
996829c878SJohannes Berg 	} else {
10079c97e97SJohannes Berg 		rdev->scan_req = NULL;
1016829c878SJohannes Berg 		kfree(request);
1026829c878SJohannes Berg 	}
1036829c878SJohannes Berg 	return err;
1046829c878SJohannes Berg }
1056829c878SJohannes Berg 
1066829c878SJohannes Berg static int cfg80211_conn_do_work(struct wireless_dev *wdev)
1076829c878SJohannes Berg {
10879c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
10919957bb3SJohannes Berg 	struct cfg80211_connect_params *params;
11019957bb3SJohannes Berg 	int err;
1116829c878SJohannes Berg 
112667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
113667503ddSJohannes Berg 
1146829c878SJohannes Berg 	if (!wdev->conn)
1156829c878SJohannes Berg 		return 0;
1166829c878SJohannes Berg 
11719957bb3SJohannes Berg 	params = &wdev->conn->params;
11819957bb3SJohannes Berg 
1196829c878SJohannes Berg 	switch (wdev->conn->state) {
1206829c878SJohannes Berg 	case CFG80211_CONN_SCAN_AGAIN:
1216829c878SJohannes Berg 		return cfg80211_conn_scan(wdev);
1226829c878SJohannes Berg 	case CFG80211_CONN_AUTHENTICATE_NEXT:
12379c97e97SJohannes Berg 		BUG_ON(!rdev->ops->auth);
12419957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
12579c97e97SJohannes Berg 		return __cfg80211_mlme_auth(rdev, wdev->netdev,
12619957bb3SJohannes Berg 					    params->channel, params->auth_type,
12719957bb3SJohannes Berg 					    params->bssid,
12819957bb3SJohannes Berg 					    params->ssid, params->ssid_len,
129fffd0934SJohannes Berg 					    NULL, 0,
130fffd0934SJohannes Berg 					    params->key, params->key_len,
131fffd0934SJohannes Berg 					    params->key_idx);
1326829c878SJohannes Berg 	case CFG80211_CONN_ASSOCIATE_NEXT:
13379c97e97SJohannes Berg 		BUG_ON(!rdev->ops->assoc);
13419957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
1353e5d7649SJohannes Berg 		/*
1363e5d7649SJohannes Berg 		 * We could, later, implement roaming here and then actually
1373e5d7649SJohannes Berg 		 * set prev_bssid to non-NULL. But then we need to be aware
1383e5d7649SJohannes Berg 		 * that some APs don't like that -- so we'd need to retry
1393e5d7649SJohannes Berg 		 * the association.
1403e5d7649SJohannes Berg 		 */
14179c97e97SJohannes Berg 		err = __cfg80211_mlme_assoc(rdev, wdev->netdev,
142667503ddSJohannes Berg 					    params->channel, params->bssid,
143667503ddSJohannes Berg 					    NULL,
14419957bb3SJohannes Berg 					    params->ssid, params->ssid_len,
14519957bb3SJohannes Berg 					    params->ie, params->ie_len,
14619957bb3SJohannes Berg 					    false, &params->crypto);
14719957bb3SJohannes Berg 		if (err)
14879c97e97SJohannes Berg 			__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
149667503ddSJohannes Berg 					       NULL, 0,
150667503ddSJohannes Berg 					       WLAN_REASON_DEAUTH_LEAVING);
15119957bb3SJohannes Berg 		return err;
1526829c878SJohannes Berg 	default:
1536829c878SJohannes Berg 		return 0;
1546829c878SJohannes Berg 	}
1556829c878SJohannes Berg }
1566829c878SJohannes Berg 
1576829c878SJohannes Berg void cfg80211_conn_work(struct work_struct *work)
1586829c878SJohannes Berg {
15979c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev =
1606829c878SJohannes Berg 		container_of(work, struct cfg80211_registered_device, conn_work);
1616829c878SJohannes Berg 	struct wireless_dev *wdev;
1626829c878SJohannes Berg 
1636829c878SJohannes Berg 	rtnl_lock();
16479c97e97SJohannes Berg 	cfg80211_lock_rdev(rdev);
16579c97e97SJohannes Berg 	mutex_lock(&rdev->devlist_mtx);
1666829c878SJohannes Berg 
16779c97e97SJohannes Berg 	list_for_each_entry(wdev, &rdev->netdev_list, list) {
168667503ddSJohannes Berg 		wdev_lock(wdev);
169667503ddSJohannes Berg 		if (!netif_running(wdev->netdev)) {
170667503ddSJohannes Berg 			wdev_unlock(wdev);
1716829c878SJohannes Berg 			continue;
172667503ddSJohannes Berg 		}
173667503ddSJohannes Berg 		if (wdev->sme_state != CFG80211_SME_CONNECTING) {
174667503ddSJohannes Berg 			wdev_unlock(wdev);
1756829c878SJohannes Berg 			continue;
176667503ddSJohannes Berg 		}
1776829c878SJohannes Berg 		if (cfg80211_conn_do_work(wdev))
178667503ddSJohannes Berg 			__cfg80211_connect_result(
179667503ddSJohannes Berg 					wdev->netdev,
1806829c878SJohannes Berg 					wdev->conn->params.bssid,
1816829c878SJohannes Berg 					NULL, 0, NULL, 0,
1826829c878SJohannes Berg 					WLAN_STATUS_UNSPECIFIED_FAILURE,
183667503ddSJohannes Berg 					false);
184667503ddSJohannes Berg 		wdev_unlock(wdev);
1856829c878SJohannes Berg 	}
1866829c878SJohannes Berg 
18779c97e97SJohannes Berg 	mutex_unlock(&rdev->devlist_mtx);
18879c97e97SJohannes Berg 	cfg80211_unlock_rdev(rdev);
1896829c878SJohannes Berg 	rtnl_unlock();
1906829c878SJohannes Berg }
1916829c878SJohannes Berg 
1926829c878SJohannes Berg static bool cfg80211_get_conn_bss(struct wireless_dev *wdev)
1936829c878SJohannes Berg {
19479c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
1956829c878SJohannes Berg 	struct cfg80211_bss *bss;
1966829c878SJohannes Berg 	u16 capa = WLAN_CAPABILITY_ESS;
1976829c878SJohannes Berg 
198667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
199667503ddSJohannes Berg 
2006829c878SJohannes Berg 	if (wdev->conn->params.privacy)
2016829c878SJohannes Berg 		capa |= WLAN_CAPABILITY_PRIVACY;
2026829c878SJohannes Berg 
2036829c878SJohannes Berg 	bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid,
2046829c878SJohannes Berg 			       wdev->conn->params.ssid,
2056829c878SJohannes Berg 			       wdev->conn->params.ssid_len,
2066829c878SJohannes Berg 			       WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
2076829c878SJohannes Berg 			       capa);
2086829c878SJohannes Berg 	if (!bss)
2096829c878SJohannes Berg 		return false;
2106829c878SJohannes Berg 
2116829c878SJohannes Berg 	memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN);
2126829c878SJohannes Berg 	wdev->conn->params.bssid = wdev->conn->bssid;
2136829c878SJohannes Berg 	wdev->conn->params.channel = bss->channel;
2146829c878SJohannes Berg 	wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
21579c97e97SJohannes Berg 	schedule_work(&rdev->conn_work);
2166829c878SJohannes Berg 
2176829c878SJohannes Berg 	cfg80211_put_bss(bss);
2186829c878SJohannes Berg 	return true;
2196829c878SJohannes Berg }
2206829c878SJohannes Berg 
221667503ddSJohannes Berg static void __cfg80211_sme_scan_done(struct net_device *dev)
2226829c878SJohannes Berg {
2236829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
22479c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
2256829c878SJohannes Berg 
226667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
227667503ddSJohannes Berg 
2286829c878SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
2296829c878SJohannes Berg 		return;
2306829c878SJohannes Berg 
231d4b1a687SZhu Yi 	if (!wdev->conn)
2326829c878SJohannes Berg 		return;
2336829c878SJohannes Berg 
2346829c878SJohannes Berg 	if (wdev->conn->state != CFG80211_CONN_SCANNING &&
2356829c878SJohannes Berg 	    wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
2366829c878SJohannes Berg 		return;
2376829c878SJohannes Berg 
2386829c878SJohannes Berg 	if (!cfg80211_get_conn_bss(wdev)) {
2396829c878SJohannes Berg 		/* not found */
2406829c878SJohannes Berg 		if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
24179c97e97SJohannes Berg 			schedule_work(&rdev->conn_work);
2426829c878SJohannes Berg 		else
243667503ddSJohannes Berg 			__cfg80211_connect_result(
244667503ddSJohannes Berg 					wdev->netdev,
245667503ddSJohannes Berg 					wdev->conn->params.bssid,
2466829c878SJohannes Berg 					NULL, 0, NULL, 0,
2476829c878SJohannes Berg 					WLAN_STATUS_UNSPECIFIED_FAILURE,
248667503ddSJohannes Berg 					false);
2496829c878SJohannes Berg 	}
2506829c878SJohannes Berg }
2516829c878SJohannes Berg 
252667503ddSJohannes Berg void cfg80211_sme_scan_done(struct net_device *dev)
253667503ddSJohannes Berg {
254667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
255667503ddSJohannes Berg 
256667503ddSJohannes Berg 	wdev_lock(wdev);
257667503ddSJohannes Berg 	__cfg80211_sme_scan_done(dev);
258667503ddSJohannes Berg 	wdev_unlock(wdev);
259667503ddSJohannes Berg }
260667503ddSJohannes Berg 
261667503ddSJohannes Berg void cfg80211_sme_rx_auth(struct net_device *dev,
262667503ddSJohannes Berg 			  const u8 *buf, size_t len)
2636829c878SJohannes Berg {
2646829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
2656829c878SJohannes Berg 	struct wiphy *wiphy = wdev->wiphy;
2666829c878SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
2676829c878SJohannes Berg 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
2686829c878SJohannes Berg 	u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
2696829c878SJohannes Berg 
270667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
271667503ddSJohannes Berg 
2726829c878SJohannes Berg 	/* should only RX auth frames when connecting */
2736829c878SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
2746829c878SJohannes Berg 		return;
2756829c878SJohannes Berg 
2766829c878SJohannes Berg 	if (WARN_ON(!wdev->conn))
2776829c878SJohannes Berg 		return;
2786829c878SJohannes Berg 
2796829c878SJohannes Berg 	if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
2806829c878SJohannes Berg 	    wdev->conn->auto_auth &&
2816829c878SJohannes Berg 	    wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
2826829c878SJohannes Berg 		/* select automatically between only open, shared, leap */
2836829c878SJohannes Berg 		switch (wdev->conn->params.auth_type) {
2846829c878SJohannes Berg 		case NL80211_AUTHTYPE_OPEN_SYSTEM:
285fffd0934SJohannes Berg 			if (wdev->connect_keys)
2866829c878SJohannes Berg 				wdev->conn->params.auth_type =
2876829c878SJohannes Berg 					NL80211_AUTHTYPE_SHARED_KEY;
288fffd0934SJohannes Berg 			else
289fffd0934SJohannes Berg 				wdev->conn->params.auth_type =
290fffd0934SJohannes Berg 					NL80211_AUTHTYPE_NETWORK_EAP;
2916829c878SJohannes Berg 			break;
2926829c878SJohannes Berg 		case NL80211_AUTHTYPE_SHARED_KEY:
2936829c878SJohannes Berg 			wdev->conn->params.auth_type =
2946829c878SJohannes Berg 				NL80211_AUTHTYPE_NETWORK_EAP;
2956829c878SJohannes Berg 			break;
2966829c878SJohannes Berg 		default:
2976829c878SJohannes Berg 			/* huh? */
2986829c878SJohannes Berg 			wdev->conn->params.auth_type =
2996829c878SJohannes Berg 				NL80211_AUTHTYPE_OPEN_SYSTEM;
3006829c878SJohannes Berg 			break;
3016829c878SJohannes Berg 		}
3026829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
3036829c878SJohannes Berg 		schedule_work(&rdev->conn_work);
30419957bb3SJohannes Berg 	} else if (status_code != WLAN_STATUS_SUCCESS) {
3054bde0f7dSJohannes Berg 		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
3064bde0f7dSJohannes Berg 					  status_code, false);
30719957bb3SJohannes Berg 	} else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
3086829c878SJohannes Berg 		 wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
3096829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
3106829c878SJohannes Berg 		schedule_work(&rdev->conn_work);
3116829c878SJohannes Berg 	}
3126829c878SJohannes Berg }
313b23aa676SSamuel Ortiz 
314667503ddSJohannes Berg void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
315b23aa676SSamuel Ortiz 			       const u8 *req_ie, size_t req_ie_len,
316b23aa676SSamuel Ortiz 			       const u8 *resp_ie, size_t resp_ie_len,
317667503ddSJohannes Berg 			       u16 status, bool wextev)
318b23aa676SSamuel Ortiz {
319b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
320b23aa676SSamuel Ortiz 	struct cfg80211_bss *bss;
321b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT
322b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
323b23aa676SSamuel Ortiz #endif
324b23aa676SSamuel Ortiz 
325667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
326667503ddSJohannes Berg 
327b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
328b23aa676SSamuel Ortiz 		return;
329b23aa676SSamuel Ortiz 
330e45cd82aSJohannes Berg 	if (wdev->sme_state == CFG80211_SME_CONNECTED)
331e45cd82aSJohannes Berg 		nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev,
332e45cd82aSJohannes Berg 				    bssid, req_ie, req_ie_len,
333667503ddSJohannes Berg 				    resp_ie, resp_ie_len, GFP_KERNEL);
334e45cd82aSJohannes Berg 	else
335e45cd82aSJohannes Berg 		nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
336e45cd82aSJohannes Berg 					    bssid, req_ie, req_ie_len,
337e45cd82aSJohannes Berg 					    resp_ie, resp_ie_len,
338667503ddSJohannes Berg 					    status, GFP_KERNEL);
339e45cd82aSJohannes Berg 
340e45cd82aSJohannes Berg #ifdef CONFIG_WIRELESS_EXT
341e45cd82aSJohannes Berg 	if (wextev) {
342e45cd82aSJohannes Berg 		if (req_ie && status == WLAN_STATUS_SUCCESS) {
343e45cd82aSJohannes Berg 			memset(&wrqu, 0, sizeof(wrqu));
344e45cd82aSJohannes Berg 			wrqu.data.length = req_ie_len;
3453409ff77SZhu Yi 			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
346e45cd82aSJohannes Berg 		}
347e45cd82aSJohannes Berg 
348e45cd82aSJohannes Berg 		if (resp_ie && status == WLAN_STATUS_SUCCESS) {
349e45cd82aSJohannes Berg 			memset(&wrqu, 0, sizeof(wrqu));
350e45cd82aSJohannes Berg 			wrqu.data.length = resp_ie_len;
351e45cd82aSJohannes Berg 			wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
352e45cd82aSJohannes Berg 		}
353e45cd82aSJohannes Berg 
354e45cd82aSJohannes Berg 		memset(&wrqu, 0, sizeof(wrqu));
355e45cd82aSJohannes Berg 		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
356e45cd82aSJohannes Berg 		if (bssid && status == WLAN_STATUS_SUCCESS)
357e45cd82aSJohannes Berg 			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
358e45cd82aSJohannes Berg 		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
359e45cd82aSJohannes Berg 	}
360e45cd82aSJohannes Berg #endif
361e45cd82aSJohannes Berg 
362e45cd82aSJohannes Berg 	if (status == WLAN_STATUS_SUCCESS &&
363fffd0934SJohannes Berg 	    wdev->sme_state == CFG80211_SME_IDLE)
364fffd0934SJohannes Berg 		goto success;
365e45cd82aSJohannes Berg 
3666829c878SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
367b23aa676SSamuel Ortiz 		return;
368b23aa676SSamuel Ortiz 
369b23aa676SSamuel Ortiz 	if (wdev->current_bss) {
370b23aa676SSamuel Ortiz 		cfg80211_unhold_bss(wdev->current_bss);
37119957bb3SJohannes Berg 		cfg80211_put_bss(&wdev->current_bss->pub);
372b23aa676SSamuel Ortiz 		wdev->current_bss = NULL;
373b23aa676SSamuel Ortiz 	}
374b23aa676SSamuel Ortiz 
37519957bb3SJohannes Berg 	if (wdev->conn)
37619957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_IDLE;
37719957bb3SJohannes Berg 
378fffd0934SJohannes Berg 	if (status != WLAN_STATUS_SUCCESS) {
379fffd0934SJohannes Berg 		wdev->sme_state = CFG80211_SME_IDLE;
380fffd0934SJohannes Berg 		kfree(wdev->conn);
381fffd0934SJohannes Berg 		wdev->conn = NULL;
382fffd0934SJohannes Berg 		kfree(wdev->connect_keys);
383fffd0934SJohannes Berg 		wdev->connect_keys = NULL;
384fffd0934SJohannes Berg 		return;
385fffd0934SJohannes Berg 	}
386fffd0934SJohannes Berg 
387b23aa676SSamuel Ortiz 	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
388b23aa676SSamuel Ortiz 			       wdev->ssid, wdev->ssid_len,
389b23aa676SSamuel Ortiz 			       WLAN_CAPABILITY_ESS,
390b23aa676SSamuel Ortiz 			       WLAN_CAPABILITY_ESS);
391b23aa676SSamuel Ortiz 
392b23aa676SSamuel Ortiz 	if (WARN_ON(!bss))
393b23aa676SSamuel Ortiz 		return;
394b23aa676SSamuel Ortiz 
39519957bb3SJohannes Berg 	cfg80211_hold_bss(bss_from_pub(bss));
39619957bb3SJohannes Berg 	wdev->current_bss = bss_from_pub(bss);
397b23aa676SSamuel Ortiz 
398fffd0934SJohannes Berg  success:
399b23aa676SSamuel Ortiz 	wdev->sme_state = CFG80211_SME_CONNECTED;
400fffd0934SJohannes Berg 	cfg80211_upload_connect_keys(wdev);
401b23aa676SSamuel Ortiz }
402f2129354SJohannes Berg 
403f2129354SJohannes Berg void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
404f2129354SJohannes Berg 			     const u8 *req_ie, size_t req_ie_len,
405f2129354SJohannes Berg 			     const u8 *resp_ie, size_t resp_ie_len,
406f2129354SJohannes Berg 			     u16 status, gfp_t gfp)
407f2129354SJohannes Berg {
408667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
409667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
410667503ddSJohannes Berg 	struct cfg80211_event *ev;
411667503ddSJohannes Berg 	unsigned long flags;
412667503ddSJohannes Berg 
413667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
414667503ddSJohannes Berg 	if (!ev)
415667503ddSJohannes Berg 		return;
416667503ddSJohannes Berg 
417667503ddSJohannes Berg 	ev->type = EVENT_CONNECT_RESULT;
418667503ddSJohannes Berg 	memcpy(ev->cr.bssid, bssid, ETH_ALEN);
419667503ddSJohannes Berg 	ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
420667503ddSJohannes Berg 	ev->cr.req_ie_len = req_ie_len;
421667503ddSJohannes Berg 	memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
422667503ddSJohannes Berg 	ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
423667503ddSJohannes Berg 	ev->cr.resp_ie_len = resp_ie_len;
424667503ddSJohannes Berg 	memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
425667503ddSJohannes Berg 	ev->cr.status = status;
426667503ddSJohannes Berg 
427667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
428667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
429667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
430667503ddSJohannes Berg 	schedule_work(&rdev->event_work);
431f2129354SJohannes Berg }
432b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_connect_result);
433b23aa676SSamuel Ortiz 
434667503ddSJohannes Berg void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid,
435b23aa676SSamuel Ortiz 		       const u8 *req_ie, size_t req_ie_len,
436667503ddSJohannes Berg 		       const u8 *resp_ie, size_t resp_ie_len)
437b23aa676SSamuel Ortiz {
438b23aa676SSamuel Ortiz 	struct cfg80211_bss *bss;
439b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT
440b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
441b23aa676SSamuel Ortiz #endif
442b23aa676SSamuel Ortiz 
443667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
444667503ddSJohannes Berg 
445b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
446b23aa676SSamuel Ortiz 		return;
447b23aa676SSamuel Ortiz 
448b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
449b23aa676SSamuel Ortiz 		return;
450b23aa676SSamuel Ortiz 
451b23aa676SSamuel Ortiz 	/* internal error -- how did we get to CONNECTED w/o BSS? */
452b23aa676SSamuel Ortiz 	if (WARN_ON(!wdev->current_bss)) {
453b23aa676SSamuel Ortiz 		return;
454b23aa676SSamuel Ortiz 	}
455b23aa676SSamuel Ortiz 
456b23aa676SSamuel Ortiz 	cfg80211_unhold_bss(wdev->current_bss);
45719957bb3SJohannes Berg 	cfg80211_put_bss(&wdev->current_bss->pub);
458b23aa676SSamuel Ortiz 	wdev->current_bss = NULL;
459b23aa676SSamuel Ortiz 
460b23aa676SSamuel Ortiz 	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
461b23aa676SSamuel Ortiz 			       wdev->ssid, wdev->ssid_len,
462b23aa676SSamuel Ortiz 			       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
463b23aa676SSamuel Ortiz 
464b23aa676SSamuel Ortiz 	if (WARN_ON(!bss))
465b23aa676SSamuel Ortiz 		return;
466b23aa676SSamuel Ortiz 
46719957bb3SJohannes Berg 	cfg80211_hold_bss(bss_from_pub(bss));
46819957bb3SJohannes Berg 	wdev->current_bss = bss_from_pub(bss);
469b23aa676SSamuel Ortiz 
470667503ddSJohannes Berg 	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid,
471667503ddSJohannes Berg 			    req_ie, req_ie_len, resp_ie, resp_ie_len,
472667503ddSJohannes Berg 			    GFP_KERNEL);
473b23aa676SSamuel Ortiz 
474b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT
475b23aa676SSamuel Ortiz 	if (req_ie) {
476b23aa676SSamuel Ortiz 		memset(&wrqu, 0, sizeof(wrqu));
477b23aa676SSamuel Ortiz 		wrqu.data.length = req_ie_len;
4783409ff77SZhu Yi 		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
479667503ddSJohannes Berg 				    &wrqu, req_ie);
480b23aa676SSamuel Ortiz 	}
481b23aa676SSamuel Ortiz 
482b23aa676SSamuel Ortiz 	if (resp_ie) {
483b23aa676SSamuel Ortiz 		memset(&wrqu, 0, sizeof(wrqu));
484b23aa676SSamuel Ortiz 		wrqu.data.length = resp_ie_len;
485667503ddSJohannes Berg 		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
486667503ddSJohannes Berg 				    &wrqu, resp_ie);
487b23aa676SSamuel Ortiz 	}
488b23aa676SSamuel Ortiz 
489b23aa676SSamuel Ortiz 	memset(&wrqu, 0, sizeof(wrqu));
490b23aa676SSamuel Ortiz 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
491b23aa676SSamuel Ortiz 	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
492667503ddSJohannes Berg 	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
493b23aa676SSamuel Ortiz #endif
494b23aa676SSamuel Ortiz }
495667503ddSJohannes Berg 
496667503ddSJohannes Berg void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
497667503ddSJohannes Berg 		     const u8 *req_ie, size_t req_ie_len,
498667503ddSJohannes Berg 		     const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
499667503ddSJohannes Berg {
500667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
501667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
502667503ddSJohannes Berg 	struct cfg80211_event *ev;
503667503ddSJohannes Berg 	unsigned long flags;
504667503ddSJohannes Berg 
505667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
506667503ddSJohannes Berg 	if (!ev)
507667503ddSJohannes Berg 		return;
508667503ddSJohannes Berg 
509667503ddSJohannes Berg 	ev->type = EVENT_ROAMED;
510667503ddSJohannes Berg 	memcpy(ev->rm.bssid, bssid, ETH_ALEN);
511667503ddSJohannes Berg 	ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
512667503ddSJohannes Berg 	ev->rm.req_ie_len = req_ie_len;
513667503ddSJohannes Berg 	memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len);
514667503ddSJohannes Berg 	ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
515667503ddSJohannes Berg 	ev->rm.resp_ie_len = resp_ie_len;
516667503ddSJohannes Berg 	memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len);
517667503ddSJohannes Berg 
518667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
519667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
520667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
521667503ddSJohannes Berg 	schedule_work(&rdev->event_work);
522667503ddSJohannes Berg }
523b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_roamed);
524b23aa676SSamuel Ortiz 
525667503ddSJohannes Berg void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
5266829c878SJohannes Berg 			     size_t ie_len, u16 reason, bool from_ap)
527b23aa676SSamuel Ortiz {
528b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
529fffd0934SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
530fffd0934SJohannes Berg 	int i;
531b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT
532b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
533b23aa676SSamuel Ortiz #endif
534b23aa676SSamuel Ortiz 
535667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
536667503ddSJohannes Berg 
537b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
538b23aa676SSamuel Ortiz 		return;
539b23aa676SSamuel Ortiz 
540b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
541b23aa676SSamuel Ortiz 		return;
542b23aa676SSamuel Ortiz 
543b23aa676SSamuel Ortiz 	if (wdev->current_bss) {
544b23aa676SSamuel Ortiz 		cfg80211_unhold_bss(wdev->current_bss);
54519957bb3SJohannes Berg 		cfg80211_put_bss(&wdev->current_bss->pub);
546b23aa676SSamuel Ortiz 	}
547b23aa676SSamuel Ortiz 
548b23aa676SSamuel Ortiz 	wdev->current_bss = NULL;
549b23aa676SSamuel Ortiz 	wdev->sme_state = CFG80211_SME_IDLE;
550b23aa676SSamuel Ortiz 
5516829c878SJohannes Berg 	if (wdev->conn) {
5526829c878SJohannes Berg 		kfree(wdev->conn->ie);
5536829c878SJohannes Berg 		wdev->conn->ie = NULL;
55419957bb3SJohannes Berg 		kfree(wdev->conn);
55519957bb3SJohannes Berg 		wdev->conn = NULL;
5566829c878SJohannes Berg 	}
5576829c878SJohannes Berg 
558fffd0934SJohannes Berg 	nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
559fffd0934SJohannes Berg 
560fffd0934SJohannes Berg 	/*
561fffd0934SJohannes Berg 	 * Delete all the keys ... pairwise keys can't really
562fffd0934SJohannes Berg 	 * exist any more anyway, but default keys might.
563fffd0934SJohannes Berg 	 */
564fffd0934SJohannes Berg 	if (rdev->ops->del_key)
565fffd0934SJohannes Berg 		for (i = 0; i < 6; i++)
566fffd0934SJohannes Berg 			rdev->ops->del_key(wdev->wiphy, dev, i, NULL);
567b23aa676SSamuel Ortiz 
568b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT
569b23aa676SSamuel Ortiz 	memset(&wrqu, 0, sizeof(wrqu));
570b23aa676SSamuel Ortiz 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
571b23aa676SSamuel Ortiz 	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
572b23aa676SSamuel Ortiz #endif
573b23aa676SSamuel Ortiz }
574b23aa676SSamuel Ortiz 
575b23aa676SSamuel Ortiz void cfg80211_disconnected(struct net_device *dev, u16 reason,
576b23aa676SSamuel Ortiz 			   u8 *ie, size_t ie_len, gfp_t gfp)
577b23aa676SSamuel Ortiz {
578667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
579667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
580667503ddSJohannes Berg 	struct cfg80211_event *ev;
581667503ddSJohannes Berg 	unsigned long flags;
582667503ddSJohannes Berg 
583667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + ie_len, gfp);
584667503ddSJohannes Berg 	if (!ev)
585667503ddSJohannes Berg 		return;
586667503ddSJohannes Berg 
587667503ddSJohannes Berg 	ev->type = EVENT_DISCONNECTED;
588667503ddSJohannes Berg 	ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
589667503ddSJohannes Berg 	ev->dc.ie_len = ie_len;
590667503ddSJohannes Berg 	memcpy((void *)ev->dc.ie, ie, ie_len);
591667503ddSJohannes Berg 	ev->dc.reason = reason;
592667503ddSJohannes Berg 
593667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
594667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
595667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
596667503ddSJohannes Berg 	schedule_work(&rdev->event_work);
597b23aa676SSamuel Ortiz }
598b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_disconnected);
599b23aa676SSamuel Ortiz 
600667503ddSJohannes Berg int __cfg80211_connect(struct cfg80211_registered_device *rdev,
601b23aa676SSamuel Ortiz 		       struct net_device *dev,
602fffd0934SJohannes Berg 		       struct cfg80211_connect_params *connect,
603fffd0934SJohannes Berg 		       struct cfg80211_cached_keys *connkeys)
604b23aa676SSamuel Ortiz {
605b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
606667503ddSJohannes Berg 	int err;
607667503ddSJohannes Berg 
608667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
609b23aa676SSamuel Ortiz 
610b23aa676SSamuel Ortiz 	if (wdev->sme_state != CFG80211_SME_IDLE)
611b23aa676SSamuel Ortiz 		return -EALREADY;
612b23aa676SSamuel Ortiz 
613fffd0934SJohannes Berg 	if (WARN_ON(wdev->connect_keys)) {
614fffd0934SJohannes Berg 		kfree(wdev->connect_keys);
615fffd0934SJohannes Berg 		wdev->connect_keys = NULL;
616fffd0934SJohannes Berg 	}
617fffd0934SJohannes Berg 
618fffd0934SJohannes Berg 	if (connkeys && connkeys->def >= 0) {
619fffd0934SJohannes Berg 		int idx;
620fffd0934SJohannes Berg 
621fffd0934SJohannes Berg 		idx = connkeys->def;
622fffd0934SJohannes Berg 		/* If given a WEP key we may need it for shared key auth */
623fffd0934SJohannes Berg 		if (connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP40 ||
624fffd0934SJohannes Berg 		    connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP104) {
625fffd0934SJohannes Berg 			connect->key_idx = idx;
626fffd0934SJohannes Berg 			connect->key = connkeys->params[idx].key;
627fffd0934SJohannes Berg 			connect->key_len = connkeys->params[idx].key_len;
628fffd0934SJohannes Berg 		}
629fffd0934SJohannes Berg 	}
630fffd0934SJohannes Berg 
631b23aa676SSamuel Ortiz 	if (!rdev->ops->connect) {
6326829c878SJohannes Berg 		if (!rdev->ops->auth || !rdev->ops->assoc)
633b23aa676SSamuel Ortiz 			return -EOPNOTSUPP;
6346829c878SJohannes Berg 
63519957bb3SJohannes Berg 		if (WARN_ON(wdev->conn))
63619957bb3SJohannes Berg 			return -EINPROGRESS;
63719957bb3SJohannes Berg 
6386829c878SJohannes Berg 		wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
6396829c878SJohannes Berg 		if (!wdev->conn)
6406829c878SJohannes Berg 			return -ENOMEM;
6416829c878SJohannes Berg 
6426829c878SJohannes Berg 		/*
6436829c878SJohannes Berg 		 * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
6446829c878SJohannes Berg 		 */
6456829c878SJohannes Berg 		memcpy(&wdev->conn->params, connect, sizeof(*connect));
6466829c878SJohannes Berg 		if (connect->bssid) {
6476829c878SJohannes Berg 			wdev->conn->params.bssid = wdev->conn->bssid;
6486829c878SJohannes Berg 			memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
6496829c878SJohannes Berg 		}
6506829c878SJohannes Berg 
6516829c878SJohannes Berg 		if (connect->ie) {
6526829c878SJohannes Berg 			wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
6536829c878SJohannes Berg 						GFP_KERNEL);
6546829c878SJohannes Berg 			wdev->conn->params.ie = wdev->conn->ie;
65519957bb3SJohannes Berg 			if (!wdev->conn->ie) {
65619957bb3SJohannes Berg 				kfree(wdev->conn);
65719957bb3SJohannes Berg 				wdev->conn = NULL;
6586829c878SJohannes Berg 				return -ENOMEM;
6596829c878SJohannes Berg 			}
66019957bb3SJohannes Berg 		}
6616829c878SJohannes Berg 
6626829c878SJohannes Berg 		if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
6636829c878SJohannes Berg 			wdev->conn->auto_auth = true;
6646829c878SJohannes Berg 			/* start with open system ... should mostly work */
6656829c878SJohannes Berg 			wdev->conn->params.auth_type =
6666829c878SJohannes Berg 				NL80211_AUTHTYPE_OPEN_SYSTEM;
6676829c878SJohannes Berg 		} else {
6686829c878SJohannes Berg 			wdev->conn->auto_auth = false;
6696829c878SJohannes Berg 		}
6706829c878SJohannes Berg 
6716829c878SJohannes Berg 		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
6726829c878SJohannes Berg 		wdev->ssid_len = connect->ssid_len;
6736829c878SJohannes Berg 		wdev->conn->params.ssid = wdev->ssid;
6746829c878SJohannes Berg 		wdev->conn->params.ssid_len = connect->ssid_len;
6756829c878SJohannes Berg 
6766829c878SJohannes Berg 		/* don't care about result -- but fill bssid & channel */
6776829c878SJohannes Berg 		if (!wdev->conn->params.bssid || !wdev->conn->params.channel)
6786829c878SJohannes Berg 			cfg80211_get_conn_bss(wdev);
6796829c878SJohannes Berg 
6806829c878SJohannes Berg 		wdev->sme_state = CFG80211_SME_CONNECTING;
681fffd0934SJohannes Berg 		wdev->connect_keys = connkeys;
6826829c878SJohannes Berg 
6836829c878SJohannes Berg 		/* we're good if we have both BSSID and channel */
6846829c878SJohannes Berg 		if (wdev->conn->params.bssid && wdev->conn->params.channel) {
6856829c878SJohannes Berg 			wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
6866829c878SJohannes Berg 			err = cfg80211_conn_do_work(wdev);
6876829c878SJohannes Berg 		} else {
6886829c878SJohannes Berg 			/* otherwise we'll need to scan for the AP first */
6896829c878SJohannes Berg 			err = cfg80211_conn_scan(wdev);
6906829c878SJohannes Berg 			/*
6916829c878SJohannes Berg 			 * If we can't scan right now, then we need to scan again
6926829c878SJohannes Berg 			 * after the current scan finished, since the parameters
6936829c878SJohannes Berg 			 * changed (unless we find a good AP anyway).
6946829c878SJohannes Berg 			 */
6956829c878SJohannes Berg 			if (err == -EBUSY) {
6966829c878SJohannes Berg 				err = 0;
6976829c878SJohannes Berg 				wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
6986829c878SJohannes Berg 			}
6996829c878SJohannes Berg 		}
70019957bb3SJohannes Berg 		if (err) {
70119957bb3SJohannes Berg 			kfree(wdev->conn);
70219957bb3SJohannes Berg 			wdev->conn = NULL;
7036829c878SJohannes Berg 			wdev->sme_state = CFG80211_SME_IDLE;
704fffd0934SJohannes Berg 			wdev->connect_keys = NULL;
70519957bb3SJohannes Berg 		}
7066829c878SJohannes Berg 
7076829c878SJohannes Berg 		return err;
708b23aa676SSamuel Ortiz 	} else {
709b23aa676SSamuel Ortiz 		wdev->sme_state = CFG80211_SME_CONNECTING;
710fffd0934SJohannes Berg 		wdev->connect_keys = connkeys;
711b23aa676SSamuel Ortiz 		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
712b23aa676SSamuel Ortiz 		if (err) {
713fffd0934SJohannes Berg 			wdev->connect_keys = NULL;
714b23aa676SSamuel Ortiz 			wdev->sme_state = CFG80211_SME_IDLE;
715b23aa676SSamuel Ortiz 			return err;
716b23aa676SSamuel Ortiz 		}
717b23aa676SSamuel Ortiz 
718b23aa676SSamuel Ortiz 		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
719b23aa676SSamuel Ortiz 		wdev->ssid_len = connect->ssid_len;
720b23aa676SSamuel Ortiz 
721b23aa676SSamuel Ortiz 		return 0;
722b23aa676SSamuel Ortiz 	}
7236829c878SJohannes Berg }
724b23aa676SSamuel Ortiz 
725667503ddSJohannes Berg int cfg80211_connect(struct cfg80211_registered_device *rdev,
726667503ddSJohannes Berg 		     struct net_device *dev,
727fffd0934SJohannes Berg 		     struct cfg80211_connect_params *connect,
728fffd0934SJohannes Berg 		     struct cfg80211_cached_keys *connkeys)
729667503ddSJohannes Berg {
730667503ddSJohannes Berg 	int err;
731667503ddSJohannes Berg 
732667503ddSJohannes Berg 	wdev_lock(dev->ieee80211_ptr);
733fffd0934SJohannes Berg 	err = __cfg80211_connect(rdev, dev, connect, connkeys);
734667503ddSJohannes Berg 	wdev_unlock(dev->ieee80211_ptr);
735667503ddSJohannes Berg 
736667503ddSJohannes Berg 	return err;
737667503ddSJohannes Berg }
738667503ddSJohannes Berg 
739667503ddSJohannes Berg int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
740f2129354SJohannes Berg 			  struct net_device *dev, u16 reason, bool wextev)
741b23aa676SSamuel Ortiz {
7426829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
743b23aa676SSamuel Ortiz 	int err;
744b23aa676SSamuel Ortiz 
745667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
746667503ddSJohannes Berg 
7476829c878SJohannes Berg 	if (wdev->sme_state == CFG80211_SME_IDLE)
7486829c878SJohannes Berg 		return -EINVAL;
7496829c878SJohannes Berg 
750fffd0934SJohannes Berg 	kfree(wdev->connect_keys);
751fffd0934SJohannes Berg 	wdev->connect_keys = NULL;
752fffd0934SJohannes Berg 
753b23aa676SSamuel Ortiz 	if (!rdev->ops->disconnect) {
75419957bb3SJohannes Berg 		if (!rdev->ops->deauth)
75519957bb3SJohannes Berg 			return -EOPNOTSUPP;
7566829c878SJohannes Berg 
75719957bb3SJohannes Berg 		/* was it connected by userspace SME? */
75819957bb3SJohannes Berg 		if (!wdev->conn) {
75919957bb3SJohannes Berg 			cfg80211_mlme_down(rdev, dev);
76019957bb3SJohannes Berg 			return 0;
76119957bb3SJohannes Berg 		}
7626829c878SJohannes Berg 
7636829c878SJohannes Berg 		if (wdev->sme_state == CFG80211_SME_CONNECTING &&
7646829c878SJohannes Berg 		    (wdev->conn->state == CFG80211_CONN_SCANNING ||
7656829c878SJohannes Berg 		     wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) {
7666829c878SJohannes Berg 			wdev->sme_state = CFG80211_SME_IDLE;
76719957bb3SJohannes Berg 			kfree(wdev->conn);
76819957bb3SJohannes Berg 			wdev->conn = NULL;
7696829c878SJohannes Berg 			return 0;
7706829c878SJohannes Berg 		}
7716829c878SJohannes Berg 
7726829c878SJohannes Berg 		/* wdev->conn->params.bssid must be set if > SCANNING */
773667503ddSJohannes Berg 		err = __cfg80211_mlme_deauth(rdev, dev,
774667503ddSJohannes Berg 					     wdev->conn->params.bssid,
77519957bb3SJohannes Berg 					     NULL, 0, reason);
7766829c878SJohannes Berg 		if (err)
7776829c878SJohannes Berg 			return err;
778b23aa676SSamuel Ortiz 	} else {
779b23aa676SSamuel Ortiz 		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
780b23aa676SSamuel Ortiz 		if (err)
781b23aa676SSamuel Ortiz 			return err;
782b23aa676SSamuel Ortiz 	}
783b23aa676SSamuel Ortiz 
7846829c878SJohannes Berg 	if (wdev->sme_state == CFG80211_SME_CONNECTED)
785667503ddSJohannes Berg 		__cfg80211_disconnected(dev, NULL, 0, 0, false);
7866829c878SJohannes Berg 	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
787f2129354SJohannes Berg 		__cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
7886829c878SJohannes Berg 					  WLAN_STATUS_UNSPECIFIED_FAILURE,
789667503ddSJohannes Berg 					  wextev);
790b23aa676SSamuel Ortiz 
791b23aa676SSamuel Ortiz 	return 0;
792b23aa676SSamuel Ortiz }
79319957bb3SJohannes Berg 
794667503ddSJohannes Berg int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
795667503ddSJohannes Berg 			struct net_device *dev,
796667503ddSJohannes Berg 			u16 reason, bool wextev)
797667503ddSJohannes Berg {
798667503ddSJohannes Berg 	int err;
799667503ddSJohannes Berg 
800667503ddSJohannes Berg 	wdev_lock(dev->ieee80211_ptr);
801667503ddSJohannes Berg 	err = __cfg80211_disconnect(rdev, dev, reason, wextev);
802667503ddSJohannes Berg 	wdev_unlock(dev->ieee80211_ptr);
803667503ddSJohannes Berg 
804667503ddSJohannes Berg 	return err;
805667503ddSJohannes Berg }
806667503ddSJohannes Berg 
80719957bb3SJohannes Berg void cfg80211_sme_disassoc(struct net_device *dev, int idx)
80819957bb3SJohannes Berg {
80919957bb3SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
81019957bb3SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
81119957bb3SJohannes Berg 	u8 bssid[ETH_ALEN];
81219957bb3SJohannes Berg 
813667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
814667503ddSJohannes Berg 
81519957bb3SJohannes Berg 	if (!wdev->conn)
81619957bb3SJohannes Berg 		return;
81719957bb3SJohannes Berg 
81819957bb3SJohannes Berg 	if (wdev->conn->state == CFG80211_CONN_IDLE)
81919957bb3SJohannes Berg 		return;
82019957bb3SJohannes Berg 
82119957bb3SJohannes Berg 	/*
82219957bb3SJohannes Berg 	 * Ok, so the association was made by this SME -- we don't
82319957bb3SJohannes Berg 	 * want it any more so deauthenticate too.
82419957bb3SJohannes Berg 	 */
82519957bb3SJohannes Berg 
82619957bb3SJohannes Berg 	if (!wdev->auth_bsses[idx])
82719957bb3SJohannes Berg 		return;
82819957bb3SJohannes Berg 
82919957bb3SJohannes Berg 	memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN);
830ec3f1490SJohannes Berg 	if (__cfg80211_mlme_deauth(rdev, dev, bssid,
83119957bb3SJohannes Berg 				   NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
83219957bb3SJohannes Berg 		/* whatever -- assume gone anyway */
83319957bb3SJohannes Berg 		cfg80211_unhold_bss(wdev->auth_bsses[idx]);
83419957bb3SJohannes Berg 		cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);
83519957bb3SJohannes Berg 		wdev->auth_bsses[idx] = NULL;
83619957bb3SJohannes Berg 	}
83719957bb3SJohannes Berg }
838