xref: /openbmc/linux/net/wireless/sme.c (revision bcba8eae)
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>
11a9a11622SJohannes Berg #include <linux/wireless.h>
12a9a11622SJohannes Berg #include <net/iw_handler.h>
13b23aa676SSamuel Ortiz #include <net/cfg80211.h>
14b23aa676SSamuel Ortiz #include <net/rtnetlink.h>
15b23aa676SSamuel Ortiz #include "nl80211.h"
168b19e6caSLuis R. Rodriguez #include "reg.h"
17b23aa676SSamuel Ortiz 
186829c878SJohannes Berg struct cfg80211_conn {
196829c878SJohannes Berg 	struct cfg80211_connect_params params;
206829c878SJohannes Berg 	/* these are sub-states of the _CONNECTING sme_state */
216829c878SJohannes Berg 	enum {
226829c878SJohannes Berg 		CFG80211_CONN_IDLE,
236829c878SJohannes Berg 		CFG80211_CONN_SCANNING,
246829c878SJohannes Berg 		CFG80211_CONN_SCAN_AGAIN,
256829c878SJohannes Berg 		CFG80211_CONN_AUTHENTICATE_NEXT,
266829c878SJohannes Berg 		CFG80211_CONN_AUTHENTICATING,
276829c878SJohannes Berg 		CFG80211_CONN_ASSOCIATE_NEXT,
286829c878SJohannes Berg 		CFG80211_CONN_ASSOCIATING,
296829c878SJohannes Berg 	} state;
306829c878SJohannes Berg 	u8 bssid[ETH_ALEN];
316829c878SJohannes Berg 	u8 *ie;
326829c878SJohannes Berg 	size_t ie_len;
336829c878SJohannes Berg 	bool auto_auth;
346829c878SJohannes Berg };
356829c878SJohannes Berg 
366829c878SJohannes Berg 
376829c878SJohannes Berg static int cfg80211_conn_scan(struct wireless_dev *wdev)
386829c878SJohannes Berg {
3979c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
406829c878SJohannes Berg 	struct cfg80211_scan_request *request;
416829c878SJohannes Berg 	int n_channels, err;
426829c878SJohannes Berg 
436829c878SJohannes Berg 	ASSERT_RTNL();
4479c97e97SJohannes Berg 	ASSERT_RDEV_LOCK(rdev);
45667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
466829c878SJohannes Berg 
4779c97e97SJohannes Berg 	if (rdev->scan_req)
486829c878SJohannes Berg 		return -EBUSY;
496829c878SJohannes Berg 
506829c878SJohannes Berg 	if (wdev->conn->params.channel) {
516829c878SJohannes Berg 		n_channels = 1;
526829c878SJohannes Berg 	} else {
536829c878SJohannes Berg 		enum ieee80211_band band;
546829c878SJohannes Berg 		n_channels = 0;
556829c878SJohannes Berg 
566829c878SJohannes Berg 		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
576829c878SJohannes Berg 			if (!wdev->wiphy->bands[band])
586829c878SJohannes Berg 				continue;
596829c878SJohannes Berg 			n_channels += wdev->wiphy->bands[band]->n_channels;
606829c878SJohannes Berg 		}
616829c878SJohannes Berg 	}
626829c878SJohannes Berg 	request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
636829c878SJohannes Berg 			  sizeof(request->channels[0]) * n_channels,
646829c878SJohannes Berg 			  GFP_KERNEL);
656829c878SJohannes Berg 	if (!request)
666829c878SJohannes Berg 		return -ENOMEM;
676829c878SJohannes Berg 
686829c878SJohannes Berg 	request->channels = (void *)((char *)request + sizeof(*request));
696829c878SJohannes Berg 	if (wdev->conn->params.channel)
706829c878SJohannes Berg 		request->channels[0] = wdev->conn->params.channel;
716829c878SJohannes Berg 	else {
726829c878SJohannes Berg 		int i = 0, j;
736829c878SJohannes Berg 		enum ieee80211_band band;
746829c878SJohannes Berg 
756829c878SJohannes Berg 		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
766829c878SJohannes Berg 			if (!wdev->wiphy->bands[band])
776829c878SJohannes Berg 				continue;
786829c878SJohannes Berg 			for (j = 0; j < wdev->wiphy->bands[band]->n_channels;
796829c878SJohannes Berg 			     i++, j++)
806829c878SJohannes Berg 				request->channels[i] =
816829c878SJohannes Berg 					&wdev->wiphy->bands[band]->channels[j];
826829c878SJohannes Berg 		}
836829c878SJohannes Berg 	}
846829c878SJohannes Berg 	request->n_channels = n_channels;
856829c878SJohannes Berg 	request->ssids = (void *)(request->channels + n_channels);
866829c878SJohannes Berg 	request->n_ssids = 1;
876829c878SJohannes Berg 
886829c878SJohannes Berg 	memcpy(request->ssids[0].ssid, wdev->conn->params.ssid,
896829c878SJohannes Berg 		wdev->conn->params.ssid_len);
906829c878SJohannes Berg 	request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
916829c878SJohannes Berg 
92463d0183SJohannes Berg 	request->dev = wdev->netdev;
9379c97e97SJohannes Berg 	request->wiphy = &rdev->wiphy;
946829c878SJohannes Berg 
9579c97e97SJohannes Berg 	rdev->scan_req = request;
966829c878SJohannes Berg 
9779c97e97SJohannes Berg 	err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request);
986829c878SJohannes Berg 	if (!err) {
996829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_SCANNING;
10079c97e97SJohannes Berg 		nl80211_send_scan_start(rdev, wdev->netdev);
101463d0183SJohannes Berg 		dev_hold(wdev->netdev);
1026829c878SJohannes Berg 	} else {
10379c97e97SJohannes Berg 		rdev->scan_req = NULL;
1046829c878SJohannes Berg 		kfree(request);
1056829c878SJohannes Berg 	}
1066829c878SJohannes Berg 	return err;
1076829c878SJohannes Berg }
1086829c878SJohannes Berg 
1096829c878SJohannes Berg static int cfg80211_conn_do_work(struct wireless_dev *wdev)
1106829c878SJohannes Berg {
11179c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
11219957bb3SJohannes Berg 	struct cfg80211_connect_params *params;
11319957bb3SJohannes Berg 	int err;
1146829c878SJohannes Berg 
115667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
116667503ddSJohannes Berg 
1176829c878SJohannes Berg 	if (!wdev->conn)
1186829c878SJohannes Berg 		return 0;
1196829c878SJohannes Berg 
12019957bb3SJohannes Berg 	params = &wdev->conn->params;
12119957bb3SJohannes Berg 
1226829c878SJohannes Berg 	switch (wdev->conn->state) {
1236829c878SJohannes Berg 	case CFG80211_CONN_SCAN_AGAIN:
1246829c878SJohannes Berg 		return cfg80211_conn_scan(wdev);
1256829c878SJohannes Berg 	case CFG80211_CONN_AUTHENTICATE_NEXT:
12679c97e97SJohannes Berg 		BUG_ON(!rdev->ops->auth);
12719957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
12879c97e97SJohannes Berg 		return __cfg80211_mlme_auth(rdev, wdev->netdev,
12919957bb3SJohannes Berg 					    params->channel, params->auth_type,
13019957bb3SJohannes Berg 					    params->bssid,
13119957bb3SJohannes Berg 					    params->ssid, params->ssid_len,
132fffd0934SJohannes Berg 					    NULL, 0,
133fffd0934SJohannes Berg 					    params->key, params->key_len,
134fffd0934SJohannes Berg 					    params->key_idx);
1356829c878SJohannes Berg 	case CFG80211_CONN_ASSOCIATE_NEXT:
13679c97e97SJohannes Berg 		BUG_ON(!rdev->ops->assoc);
13719957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
1383e5d7649SJohannes Berg 		/*
1393e5d7649SJohannes Berg 		 * We could, later, implement roaming here and then actually
1403e5d7649SJohannes Berg 		 * set prev_bssid to non-NULL. But then we need to be aware
1413e5d7649SJohannes Berg 		 * that some APs don't like that -- so we'd need to retry
1423e5d7649SJohannes Berg 		 * the association.
1433e5d7649SJohannes Berg 		 */
14479c97e97SJohannes Berg 		err = __cfg80211_mlme_assoc(rdev, wdev->netdev,
145667503ddSJohannes Berg 					    params->channel, params->bssid,
146667503ddSJohannes Berg 					    NULL,
14719957bb3SJohannes Berg 					    params->ssid, params->ssid_len,
14819957bb3SJohannes Berg 					    params->ie, params->ie_len,
14919957bb3SJohannes Berg 					    false, &params->crypto);
15019957bb3SJohannes Berg 		if (err)
15179c97e97SJohannes Berg 			__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
152667503ddSJohannes Berg 					       NULL, 0,
153667503ddSJohannes Berg 					       WLAN_REASON_DEAUTH_LEAVING);
15419957bb3SJohannes Berg 		return err;
1556829c878SJohannes Berg 	default:
1566829c878SJohannes Berg 		return 0;
1576829c878SJohannes Berg 	}
1586829c878SJohannes Berg }
1596829c878SJohannes Berg 
1606829c878SJohannes Berg void cfg80211_conn_work(struct work_struct *work)
1616829c878SJohannes Berg {
16279c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev =
1636829c878SJohannes Berg 		container_of(work, struct cfg80211_registered_device, conn_work);
1646829c878SJohannes Berg 	struct wireless_dev *wdev;
1656829c878SJohannes Berg 
1666829c878SJohannes Berg 	rtnl_lock();
16779c97e97SJohannes Berg 	cfg80211_lock_rdev(rdev);
16879c97e97SJohannes Berg 	mutex_lock(&rdev->devlist_mtx);
1696829c878SJohannes Berg 
17079c97e97SJohannes Berg 	list_for_each_entry(wdev, &rdev->netdev_list, list) {
171667503ddSJohannes Berg 		wdev_lock(wdev);
172667503ddSJohannes Berg 		if (!netif_running(wdev->netdev)) {
173667503ddSJohannes Berg 			wdev_unlock(wdev);
1746829c878SJohannes Berg 			continue;
175667503ddSJohannes Berg 		}
176667503ddSJohannes Berg 		if (wdev->sme_state != CFG80211_SME_CONNECTING) {
177667503ddSJohannes Berg 			wdev_unlock(wdev);
1786829c878SJohannes Berg 			continue;
179667503ddSJohannes Berg 		}
1806829c878SJohannes Berg 		if (cfg80211_conn_do_work(wdev))
181667503ddSJohannes Berg 			__cfg80211_connect_result(
182667503ddSJohannes Berg 					wdev->netdev,
1836829c878SJohannes Berg 					wdev->conn->params.bssid,
1846829c878SJohannes Berg 					NULL, 0, NULL, 0,
1856829c878SJohannes Berg 					WLAN_STATUS_UNSPECIFIED_FAILURE,
186df7fc0f9SJohannes Berg 					false, NULL);
187667503ddSJohannes Berg 		wdev_unlock(wdev);
1886829c878SJohannes Berg 	}
1896829c878SJohannes Berg 
19079c97e97SJohannes Berg 	mutex_unlock(&rdev->devlist_mtx);
19179c97e97SJohannes Berg 	cfg80211_unlock_rdev(rdev);
1926829c878SJohannes Berg 	rtnl_unlock();
1936829c878SJohannes Berg }
1946829c878SJohannes Berg 
1956829c878SJohannes Berg static bool cfg80211_get_conn_bss(struct wireless_dev *wdev)
1966829c878SJohannes Berg {
19779c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
1986829c878SJohannes Berg 	struct cfg80211_bss *bss;
1996829c878SJohannes Berg 	u16 capa = WLAN_CAPABILITY_ESS;
2006829c878SJohannes Berg 
201667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
202667503ddSJohannes Berg 
2036829c878SJohannes Berg 	if (wdev->conn->params.privacy)
2046829c878SJohannes Berg 		capa |= WLAN_CAPABILITY_PRIVACY;
2056829c878SJohannes Berg 
2066829c878SJohannes Berg 	bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid,
2076829c878SJohannes Berg 			       wdev->conn->params.ssid,
2086829c878SJohannes Berg 			       wdev->conn->params.ssid_len,
2096829c878SJohannes Berg 			       WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
2106829c878SJohannes Berg 			       capa);
2116829c878SJohannes Berg 	if (!bss)
2126829c878SJohannes Berg 		return false;
2136829c878SJohannes Berg 
2146829c878SJohannes Berg 	memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN);
2156829c878SJohannes Berg 	wdev->conn->params.bssid = wdev->conn->bssid;
2166829c878SJohannes Berg 	wdev->conn->params.channel = bss->channel;
2176829c878SJohannes Berg 	wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
21879c97e97SJohannes Berg 	schedule_work(&rdev->conn_work);
2196829c878SJohannes Berg 
2206829c878SJohannes Berg 	cfg80211_put_bss(bss);
2216829c878SJohannes Berg 	return true;
2226829c878SJohannes Berg }
2236829c878SJohannes Berg 
224667503ddSJohannes Berg static void __cfg80211_sme_scan_done(struct net_device *dev)
2256829c878SJohannes Berg {
2266829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
22779c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
2286829c878SJohannes Berg 
229667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
230667503ddSJohannes Berg 
2316829c878SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
2326829c878SJohannes Berg 		return;
2336829c878SJohannes Berg 
234d4b1a687SZhu Yi 	if (!wdev->conn)
2356829c878SJohannes Berg 		return;
2366829c878SJohannes Berg 
2376829c878SJohannes Berg 	if (wdev->conn->state != CFG80211_CONN_SCANNING &&
2386829c878SJohannes Berg 	    wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
2396829c878SJohannes Berg 		return;
2406829c878SJohannes Berg 
2416829c878SJohannes Berg 	if (!cfg80211_get_conn_bss(wdev)) {
2426829c878SJohannes Berg 		/* not found */
2436829c878SJohannes Berg 		if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
24479c97e97SJohannes Berg 			schedule_work(&rdev->conn_work);
2456829c878SJohannes Berg 		else
246667503ddSJohannes Berg 			__cfg80211_connect_result(
247667503ddSJohannes Berg 					wdev->netdev,
248667503ddSJohannes Berg 					wdev->conn->params.bssid,
2496829c878SJohannes Berg 					NULL, 0, NULL, 0,
2506829c878SJohannes Berg 					WLAN_STATUS_UNSPECIFIED_FAILURE,
251df7fc0f9SJohannes Berg 					false, NULL);
2526829c878SJohannes Berg 	}
2536829c878SJohannes Berg }
2546829c878SJohannes Berg 
255667503ddSJohannes Berg void cfg80211_sme_scan_done(struct net_device *dev)
256667503ddSJohannes Berg {
257667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
258667503ddSJohannes Berg 
259667503ddSJohannes Berg 	wdev_lock(wdev);
260667503ddSJohannes Berg 	__cfg80211_sme_scan_done(dev);
261667503ddSJohannes Berg 	wdev_unlock(wdev);
262667503ddSJohannes Berg }
263667503ddSJohannes Berg 
264667503ddSJohannes Berg void cfg80211_sme_rx_auth(struct net_device *dev,
265667503ddSJohannes Berg 			  const u8 *buf, size_t len)
2666829c878SJohannes Berg {
2676829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
2686829c878SJohannes Berg 	struct wiphy *wiphy = wdev->wiphy;
2696829c878SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
2706829c878SJohannes Berg 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
2716829c878SJohannes Berg 	u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
2726829c878SJohannes Berg 
273667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
274667503ddSJohannes Berg 
2756829c878SJohannes Berg 	/* should only RX auth frames when connecting */
2766829c878SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
2776829c878SJohannes Berg 		return;
2786829c878SJohannes Berg 
2796829c878SJohannes Berg 	if (WARN_ON(!wdev->conn))
2806829c878SJohannes Berg 		return;
2816829c878SJohannes Berg 
2826829c878SJohannes Berg 	if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
2836829c878SJohannes Berg 	    wdev->conn->auto_auth &&
2846829c878SJohannes Berg 	    wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
2856829c878SJohannes Berg 		/* select automatically between only open, shared, leap */
2866829c878SJohannes Berg 		switch (wdev->conn->params.auth_type) {
2876829c878SJohannes Berg 		case NL80211_AUTHTYPE_OPEN_SYSTEM:
288fffd0934SJohannes Berg 			if (wdev->connect_keys)
2896829c878SJohannes Berg 				wdev->conn->params.auth_type =
2906829c878SJohannes Berg 					NL80211_AUTHTYPE_SHARED_KEY;
291fffd0934SJohannes Berg 			else
292fffd0934SJohannes Berg 				wdev->conn->params.auth_type =
293fffd0934SJohannes Berg 					NL80211_AUTHTYPE_NETWORK_EAP;
2946829c878SJohannes Berg 			break;
2956829c878SJohannes Berg 		case NL80211_AUTHTYPE_SHARED_KEY:
2966829c878SJohannes Berg 			wdev->conn->params.auth_type =
2976829c878SJohannes Berg 				NL80211_AUTHTYPE_NETWORK_EAP;
2986829c878SJohannes Berg 			break;
2996829c878SJohannes Berg 		default:
3006829c878SJohannes Berg 			/* huh? */
3016829c878SJohannes Berg 			wdev->conn->params.auth_type =
3026829c878SJohannes Berg 				NL80211_AUTHTYPE_OPEN_SYSTEM;
3036829c878SJohannes Berg 			break;
3046829c878SJohannes Berg 		}
3056829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
3066829c878SJohannes Berg 		schedule_work(&rdev->conn_work);
30719957bb3SJohannes Berg 	} else if (status_code != WLAN_STATUS_SUCCESS) {
3084bde0f7dSJohannes Berg 		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
309df7fc0f9SJohannes Berg 					  status_code, false, NULL);
31019957bb3SJohannes Berg 	} else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
3116829c878SJohannes Berg 		 wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
3126829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
3136829c878SJohannes Berg 		schedule_work(&rdev->conn_work);
3146829c878SJohannes Berg 	}
3156829c878SJohannes Berg }
316b23aa676SSamuel Ortiz 
317667503ddSJohannes Berg void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
318b23aa676SSamuel Ortiz 			       const u8 *req_ie, size_t req_ie_len,
319b23aa676SSamuel Ortiz 			       const u8 *resp_ie, size_t resp_ie_len,
320df7fc0f9SJohannes Berg 			       u16 status, bool wextev,
321df7fc0f9SJohannes Berg 			       struct cfg80211_bss *bss)
322b23aa676SSamuel Ortiz {
323b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
3248b19e6caSLuis R. Rodriguez 	u8 *country_ie;
325b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT
326b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
327b23aa676SSamuel Ortiz #endif
328b23aa676SSamuel Ortiz 
329667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
330667503ddSJohannes Berg 
331b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
332b23aa676SSamuel Ortiz 		return;
333b23aa676SSamuel Ortiz 
334e45cd82aSJohannes Berg 	if (wdev->sme_state == CFG80211_SME_CONNECTED)
335e45cd82aSJohannes Berg 		nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev,
336e45cd82aSJohannes Berg 				    bssid, req_ie, req_ie_len,
337667503ddSJohannes Berg 				    resp_ie, resp_ie_len, GFP_KERNEL);
338e45cd82aSJohannes Berg 	else
339e45cd82aSJohannes Berg 		nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
340e45cd82aSJohannes Berg 					    bssid, req_ie, req_ie_len,
341e45cd82aSJohannes Berg 					    resp_ie, resp_ie_len,
342667503ddSJohannes Berg 					    status, GFP_KERNEL);
343e45cd82aSJohannes Berg 
344e45cd82aSJohannes Berg #ifdef CONFIG_WIRELESS_EXT
345e45cd82aSJohannes Berg 	if (wextev) {
346e45cd82aSJohannes Berg 		if (req_ie && status == WLAN_STATUS_SUCCESS) {
347e45cd82aSJohannes Berg 			memset(&wrqu, 0, sizeof(wrqu));
348e45cd82aSJohannes Berg 			wrqu.data.length = req_ie_len;
3493409ff77SZhu Yi 			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
350e45cd82aSJohannes Berg 		}
351e45cd82aSJohannes Berg 
352e45cd82aSJohannes Berg 		if (resp_ie && status == WLAN_STATUS_SUCCESS) {
353e45cd82aSJohannes Berg 			memset(&wrqu, 0, sizeof(wrqu));
354e45cd82aSJohannes Berg 			wrqu.data.length = resp_ie_len;
355e45cd82aSJohannes Berg 			wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
356e45cd82aSJohannes Berg 		}
357e45cd82aSJohannes Berg 
358e45cd82aSJohannes Berg 		memset(&wrqu, 0, sizeof(wrqu));
359e45cd82aSJohannes Berg 		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
360e45cd82aSJohannes Berg 		if (bssid && status == WLAN_STATUS_SUCCESS)
361e45cd82aSJohannes Berg 			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
362e45cd82aSJohannes Berg 		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
363e45cd82aSJohannes Berg 	}
364e45cd82aSJohannes Berg #endif
365e45cd82aSJohannes Berg 
366df7fc0f9SJohannes Berg 	if (wdev->current_bss) {
367df7fc0f9SJohannes Berg 		cfg80211_unhold_bss(wdev->current_bss);
368df7fc0f9SJohannes Berg 		cfg80211_put_bss(&wdev->current_bss->pub);
369df7fc0f9SJohannes Berg 		wdev->current_bss = NULL;
370df7fc0f9SJohannes Berg 	}
371df7fc0f9SJohannes Berg 
372e45cd82aSJohannes Berg 	if (status == WLAN_STATUS_SUCCESS &&
373fffd0934SJohannes Berg 	    wdev->sme_state == CFG80211_SME_IDLE)
374fffd0934SJohannes Berg 		goto success;
375e45cd82aSJohannes Berg 
3766829c878SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
377b23aa676SSamuel Ortiz 		return;
378b23aa676SSamuel Ortiz 
37919957bb3SJohannes Berg 	if (wdev->conn)
38019957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_IDLE;
38119957bb3SJohannes Berg 
382fffd0934SJohannes Berg 	if (status != WLAN_STATUS_SUCCESS) {
383fffd0934SJohannes Berg 		wdev->sme_state = CFG80211_SME_IDLE;
384fffd0934SJohannes Berg 		kfree(wdev->conn);
385fffd0934SJohannes Berg 		wdev->conn = NULL;
386fffd0934SJohannes Berg 		kfree(wdev->connect_keys);
387fffd0934SJohannes Berg 		wdev->connect_keys = NULL;
3888dadadb7SJohannes Berg 		wdev->ssid_len = 0;
389fffd0934SJohannes Berg 		return;
390fffd0934SJohannes Berg 	}
391fffd0934SJohannes Berg 
392df7fc0f9SJohannes Berg  success:
393df7fc0f9SJohannes Berg 	if (!bss)
394b23aa676SSamuel Ortiz 		bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
395b23aa676SSamuel Ortiz 				       wdev->ssid, wdev->ssid_len,
396b23aa676SSamuel Ortiz 				       WLAN_CAPABILITY_ESS,
397b23aa676SSamuel Ortiz 				       WLAN_CAPABILITY_ESS);
398b23aa676SSamuel Ortiz 
399b23aa676SSamuel Ortiz 	if (WARN_ON(!bss))
400b23aa676SSamuel Ortiz 		return;
401b23aa676SSamuel Ortiz 
40219957bb3SJohannes Berg 	cfg80211_hold_bss(bss_from_pub(bss));
40319957bb3SJohannes Berg 	wdev->current_bss = bss_from_pub(bss);
404b23aa676SSamuel Ortiz 
405b23aa676SSamuel Ortiz 	wdev->sme_state = CFG80211_SME_CONNECTED;
406fffd0934SJohannes Berg 	cfg80211_upload_connect_keys(wdev);
4078b19e6caSLuis R. Rodriguez 
4088b19e6caSLuis R. Rodriguez 	country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
4098b19e6caSLuis R. Rodriguez 
4108b19e6caSLuis R. Rodriguez 	if (!country_ie)
4118b19e6caSLuis R. Rodriguez 		return;
4128b19e6caSLuis R. Rodriguez 
4138b19e6caSLuis R. Rodriguez 	/*
4148b19e6caSLuis R. Rodriguez 	 * ieee80211_bss_get_ie() ensures we can access:
4158b19e6caSLuis R. Rodriguez 	 * - country_ie + 2, the start of the country ie data, and
4168b19e6caSLuis R. Rodriguez 	 * - and country_ie[1] which is the IE length
4178b19e6caSLuis R. Rodriguez 	 */
4188b19e6caSLuis R. Rodriguez 	regulatory_hint_11d(wdev->wiphy,
4198b19e6caSLuis R. Rodriguez 			    country_ie + 2,
4208b19e6caSLuis R. Rodriguez 			    country_ie[1]);
421b23aa676SSamuel Ortiz }
422f2129354SJohannes Berg 
423f2129354SJohannes Berg void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
424f2129354SJohannes Berg 			     const u8 *req_ie, size_t req_ie_len,
425f2129354SJohannes Berg 			     const u8 *resp_ie, size_t resp_ie_len,
426f2129354SJohannes Berg 			     u16 status, gfp_t gfp)
427f2129354SJohannes Berg {
428667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
429667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
430667503ddSJohannes Berg 	struct cfg80211_event *ev;
431667503ddSJohannes Berg 	unsigned long flags;
432667503ddSJohannes Berg 
433667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
434667503ddSJohannes Berg 	if (!ev)
435667503ddSJohannes Berg 		return;
436667503ddSJohannes Berg 
437667503ddSJohannes Berg 	ev->type = EVENT_CONNECT_RESULT;
438667503ddSJohannes Berg 	memcpy(ev->cr.bssid, bssid, ETH_ALEN);
439667503ddSJohannes Berg 	ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
440667503ddSJohannes Berg 	ev->cr.req_ie_len = req_ie_len;
441667503ddSJohannes Berg 	memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
442667503ddSJohannes Berg 	ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
443667503ddSJohannes Berg 	ev->cr.resp_ie_len = resp_ie_len;
444667503ddSJohannes Berg 	memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
445667503ddSJohannes Berg 	ev->cr.status = status;
446667503ddSJohannes Berg 
447667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
448667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
449667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
450667503ddSJohannes Berg 	schedule_work(&rdev->event_work);
451f2129354SJohannes Berg }
452b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_connect_result);
453b23aa676SSamuel Ortiz 
454667503ddSJohannes Berg void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid,
455b23aa676SSamuel Ortiz 		       const u8 *req_ie, size_t req_ie_len,
456667503ddSJohannes Berg 		       const u8 *resp_ie, size_t resp_ie_len)
457b23aa676SSamuel Ortiz {
458b23aa676SSamuel Ortiz 	struct cfg80211_bss *bss;
459b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT
460b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
461b23aa676SSamuel Ortiz #endif
462b23aa676SSamuel Ortiz 
463667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
464667503ddSJohannes Berg 
465b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
466b23aa676SSamuel Ortiz 		return;
467b23aa676SSamuel Ortiz 
468b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
469b23aa676SSamuel Ortiz 		return;
470b23aa676SSamuel Ortiz 
471b23aa676SSamuel Ortiz 	/* internal error -- how did we get to CONNECTED w/o BSS? */
472b23aa676SSamuel Ortiz 	if (WARN_ON(!wdev->current_bss)) {
473b23aa676SSamuel Ortiz 		return;
474b23aa676SSamuel Ortiz 	}
475b23aa676SSamuel Ortiz 
476b23aa676SSamuel Ortiz 	cfg80211_unhold_bss(wdev->current_bss);
47719957bb3SJohannes Berg 	cfg80211_put_bss(&wdev->current_bss->pub);
478b23aa676SSamuel Ortiz 	wdev->current_bss = NULL;
479b23aa676SSamuel Ortiz 
480b23aa676SSamuel Ortiz 	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
481b23aa676SSamuel Ortiz 			       wdev->ssid, wdev->ssid_len,
482b23aa676SSamuel Ortiz 			       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
483b23aa676SSamuel Ortiz 
484b23aa676SSamuel Ortiz 	if (WARN_ON(!bss))
485b23aa676SSamuel Ortiz 		return;
486b23aa676SSamuel Ortiz 
48719957bb3SJohannes Berg 	cfg80211_hold_bss(bss_from_pub(bss));
48819957bb3SJohannes Berg 	wdev->current_bss = bss_from_pub(bss);
489b23aa676SSamuel Ortiz 
490667503ddSJohannes Berg 	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid,
491667503ddSJohannes Berg 			    req_ie, req_ie_len, resp_ie, resp_ie_len,
492667503ddSJohannes Berg 			    GFP_KERNEL);
493b23aa676SSamuel Ortiz 
494b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT
495b23aa676SSamuel Ortiz 	if (req_ie) {
496b23aa676SSamuel Ortiz 		memset(&wrqu, 0, sizeof(wrqu));
497b23aa676SSamuel Ortiz 		wrqu.data.length = req_ie_len;
4983409ff77SZhu Yi 		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
499667503ddSJohannes Berg 				    &wrqu, req_ie);
500b23aa676SSamuel Ortiz 	}
501b23aa676SSamuel Ortiz 
502b23aa676SSamuel Ortiz 	if (resp_ie) {
503b23aa676SSamuel Ortiz 		memset(&wrqu, 0, sizeof(wrqu));
504b23aa676SSamuel Ortiz 		wrqu.data.length = resp_ie_len;
505667503ddSJohannes Berg 		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
506667503ddSJohannes Berg 				    &wrqu, resp_ie);
507b23aa676SSamuel Ortiz 	}
508b23aa676SSamuel Ortiz 
509b23aa676SSamuel Ortiz 	memset(&wrqu, 0, sizeof(wrqu));
510b23aa676SSamuel Ortiz 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
511b23aa676SSamuel Ortiz 	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
512667503ddSJohannes Berg 	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
513b23aa676SSamuel Ortiz #endif
514b23aa676SSamuel Ortiz }
515667503ddSJohannes Berg 
516667503ddSJohannes Berg void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
517667503ddSJohannes Berg 		     const u8 *req_ie, size_t req_ie_len,
518667503ddSJohannes Berg 		     const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
519667503ddSJohannes Berg {
520667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
521667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
522667503ddSJohannes Berg 	struct cfg80211_event *ev;
523667503ddSJohannes Berg 	unsigned long flags;
524667503ddSJohannes Berg 
525667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
526667503ddSJohannes Berg 	if (!ev)
527667503ddSJohannes Berg 		return;
528667503ddSJohannes Berg 
529667503ddSJohannes Berg 	ev->type = EVENT_ROAMED;
530667503ddSJohannes Berg 	memcpy(ev->rm.bssid, bssid, ETH_ALEN);
531667503ddSJohannes Berg 	ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
532667503ddSJohannes Berg 	ev->rm.req_ie_len = req_ie_len;
533667503ddSJohannes Berg 	memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len);
534667503ddSJohannes Berg 	ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
535667503ddSJohannes Berg 	ev->rm.resp_ie_len = resp_ie_len;
536667503ddSJohannes Berg 	memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len);
537667503ddSJohannes Berg 
538667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
539667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
540667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
541667503ddSJohannes Berg 	schedule_work(&rdev->event_work);
542667503ddSJohannes Berg }
543b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_roamed);
544b23aa676SSamuel Ortiz 
545667503ddSJohannes Berg void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
5466829c878SJohannes Berg 			     size_t ie_len, u16 reason, bool from_ap)
547b23aa676SSamuel Ortiz {
548b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
549fffd0934SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
550fffd0934SJohannes Berg 	int i;
551b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT
552b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
553b23aa676SSamuel Ortiz #endif
554b23aa676SSamuel Ortiz 
555667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
556667503ddSJohannes Berg 
557b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
558b23aa676SSamuel Ortiz 		return;
559b23aa676SSamuel Ortiz 
560b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
561b23aa676SSamuel Ortiz 		return;
562b23aa676SSamuel Ortiz 
563b23aa676SSamuel Ortiz 	if (wdev->current_bss) {
564b23aa676SSamuel Ortiz 		cfg80211_unhold_bss(wdev->current_bss);
56519957bb3SJohannes Berg 		cfg80211_put_bss(&wdev->current_bss->pub);
566b23aa676SSamuel Ortiz 	}
567b23aa676SSamuel Ortiz 
568b23aa676SSamuel Ortiz 	wdev->current_bss = NULL;
569b23aa676SSamuel Ortiz 	wdev->sme_state = CFG80211_SME_IDLE;
5708dadadb7SJohannes Berg 	wdev->ssid_len = 0;
571b23aa676SSamuel Ortiz 
5726829c878SJohannes Berg 	if (wdev->conn) {
573b6f0b639SJohannes Berg 		const u8 *bssid;
574b6f0b639SJohannes Berg 		int ret;
575b6f0b639SJohannes Berg 
5766829c878SJohannes Berg 		kfree(wdev->conn->ie);
5776829c878SJohannes Berg 		wdev->conn->ie = NULL;
57819957bb3SJohannes Berg 		kfree(wdev->conn);
57919957bb3SJohannes Berg 		wdev->conn = NULL;
580b6f0b639SJohannes Berg 
581b6f0b639SJohannes Berg 		/*
582b6f0b639SJohannes Berg 		 * If this disconnect was due to a disassoc, we
583b6f0b639SJohannes Berg 		 * we might still have an auth BSS around. For
584b6f0b639SJohannes Berg 		 * the userspace SME that's currently expected,
585b6f0b639SJohannes Berg 		 * but for the kernel SME (nl80211 CONNECT or
586b6f0b639SJohannes Berg 		 * wireless extensions) we want to clear up all
587b6f0b639SJohannes Berg 		 * state.
588b6f0b639SJohannes Berg 		 */
589b6f0b639SJohannes Berg 		for (i = 0; i < MAX_AUTH_BSSES; i++) {
590b6f0b639SJohannes Berg 			if (!wdev->auth_bsses[i])
591b6f0b639SJohannes Berg 				continue;
592b6f0b639SJohannes Berg 			bssid = wdev->auth_bsses[i]->pub.bssid;
593b6f0b639SJohannes Berg 			ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
594b6f0b639SJohannes Berg 						WLAN_REASON_DEAUTH_LEAVING);
595b6f0b639SJohannes Berg 			WARN(ret, "deauth failed: %d\n", ret);
596b6f0b639SJohannes Berg 		}
5976829c878SJohannes Berg 	}
5986829c878SJohannes Berg 
599fffd0934SJohannes Berg 	nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
600fffd0934SJohannes Berg 
601fffd0934SJohannes Berg 	/*
602fffd0934SJohannes Berg 	 * Delete all the keys ... pairwise keys can't really
603fffd0934SJohannes Berg 	 * exist any more anyway, but default keys might.
604fffd0934SJohannes Berg 	 */
605fffd0934SJohannes Berg 	if (rdev->ops->del_key)
606fffd0934SJohannes Berg 		for (i = 0; i < 6; i++)
607fffd0934SJohannes Berg 			rdev->ops->del_key(wdev->wiphy, dev, i, NULL);
608b23aa676SSamuel Ortiz 
609b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT
610b23aa676SSamuel Ortiz 	memset(&wrqu, 0, sizeof(wrqu));
611b23aa676SSamuel Ortiz 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
612b23aa676SSamuel Ortiz 	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
613b23aa676SSamuel Ortiz #endif
614b23aa676SSamuel Ortiz }
615b23aa676SSamuel Ortiz 
616b23aa676SSamuel Ortiz void cfg80211_disconnected(struct net_device *dev, u16 reason,
617b23aa676SSamuel Ortiz 			   u8 *ie, size_t ie_len, gfp_t gfp)
618b23aa676SSamuel Ortiz {
619667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
620667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
621667503ddSJohannes Berg 	struct cfg80211_event *ev;
622667503ddSJohannes Berg 	unsigned long flags;
623667503ddSJohannes Berg 
624667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + ie_len, gfp);
625667503ddSJohannes Berg 	if (!ev)
626667503ddSJohannes Berg 		return;
627667503ddSJohannes Berg 
628667503ddSJohannes Berg 	ev->type = EVENT_DISCONNECTED;
629667503ddSJohannes Berg 	ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
630667503ddSJohannes Berg 	ev->dc.ie_len = ie_len;
631667503ddSJohannes Berg 	memcpy((void *)ev->dc.ie, ie, ie_len);
632667503ddSJohannes Berg 	ev->dc.reason = reason;
633667503ddSJohannes Berg 
634667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
635667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
636667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
637667503ddSJohannes Berg 	schedule_work(&rdev->event_work);
638b23aa676SSamuel Ortiz }
639b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_disconnected);
640b23aa676SSamuel Ortiz 
641667503ddSJohannes Berg int __cfg80211_connect(struct cfg80211_registered_device *rdev,
642b23aa676SSamuel Ortiz 		       struct net_device *dev,
643fffd0934SJohannes Berg 		       struct cfg80211_connect_params *connect,
644fffd0934SJohannes Berg 		       struct cfg80211_cached_keys *connkeys)
645b23aa676SSamuel Ortiz {
646b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
647667503ddSJohannes Berg 	int err;
648667503ddSJohannes Berg 
649667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
650b23aa676SSamuel Ortiz 
651b23aa676SSamuel Ortiz 	if (wdev->sme_state != CFG80211_SME_IDLE)
652b23aa676SSamuel Ortiz 		return -EALREADY;
653b23aa676SSamuel Ortiz 
654fffd0934SJohannes Berg 	if (WARN_ON(wdev->connect_keys)) {
655fffd0934SJohannes Berg 		kfree(wdev->connect_keys);
656fffd0934SJohannes Berg 		wdev->connect_keys = NULL;
657fffd0934SJohannes Berg 	}
658fffd0934SJohannes Berg 
659fffd0934SJohannes Berg 	if (connkeys && connkeys->def >= 0) {
660fffd0934SJohannes Berg 		int idx;
661*bcba8eaeSSamuel Ortiz 		u32 cipher;
662fffd0934SJohannes Berg 
663fffd0934SJohannes Berg 		idx = connkeys->def;
664*bcba8eaeSSamuel Ortiz 		cipher = connkeys->params[idx].cipher;
665fffd0934SJohannes Berg 		/* If given a WEP key we may need it for shared key auth */
666*bcba8eaeSSamuel Ortiz 		if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
667*bcba8eaeSSamuel Ortiz 		    cipher == WLAN_CIPHER_SUITE_WEP104) {
668fffd0934SJohannes Berg 			connect->key_idx = idx;
669fffd0934SJohannes Berg 			connect->key = connkeys->params[idx].key;
670fffd0934SJohannes Berg 			connect->key_len = connkeys->params[idx].key_len;
671*bcba8eaeSSamuel Ortiz 
672*bcba8eaeSSamuel Ortiz 			/*
673*bcba8eaeSSamuel Ortiz 			 * If ciphers are not set (e.g. when going through
674*bcba8eaeSSamuel Ortiz 			 * iwconfig), we have to set them appropriately here.
675*bcba8eaeSSamuel Ortiz 			 */
676*bcba8eaeSSamuel Ortiz 			if (connect->crypto.cipher_group == 0)
677*bcba8eaeSSamuel Ortiz 				connect->crypto.cipher_group = cipher;
678*bcba8eaeSSamuel Ortiz 
679*bcba8eaeSSamuel Ortiz 			if (connect->crypto.n_ciphers_pairwise == 0) {
680*bcba8eaeSSamuel Ortiz 				connect->crypto.n_ciphers_pairwise = 1;
681*bcba8eaeSSamuel Ortiz 				connect->crypto.ciphers_pairwise[0] = cipher;
682*bcba8eaeSSamuel Ortiz 			}
683fffd0934SJohannes Berg 		}
684fffd0934SJohannes Berg 	}
685fffd0934SJohannes Berg 
686b23aa676SSamuel Ortiz 	if (!rdev->ops->connect) {
6876829c878SJohannes Berg 		if (!rdev->ops->auth || !rdev->ops->assoc)
688b23aa676SSamuel Ortiz 			return -EOPNOTSUPP;
6896829c878SJohannes Berg 
69019957bb3SJohannes Berg 		if (WARN_ON(wdev->conn))
69119957bb3SJohannes Berg 			return -EINPROGRESS;
69219957bb3SJohannes Berg 
6936829c878SJohannes Berg 		wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
6946829c878SJohannes Berg 		if (!wdev->conn)
6956829c878SJohannes Berg 			return -ENOMEM;
6966829c878SJohannes Berg 
6976829c878SJohannes Berg 		/*
6986829c878SJohannes Berg 		 * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
6996829c878SJohannes Berg 		 */
7006829c878SJohannes Berg 		memcpy(&wdev->conn->params, connect, sizeof(*connect));
7016829c878SJohannes Berg 		if (connect->bssid) {
7026829c878SJohannes Berg 			wdev->conn->params.bssid = wdev->conn->bssid;
7036829c878SJohannes Berg 			memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
7046829c878SJohannes Berg 		}
7056829c878SJohannes Berg 
7066829c878SJohannes Berg 		if (connect->ie) {
7076829c878SJohannes Berg 			wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
7086829c878SJohannes Berg 						GFP_KERNEL);
7096829c878SJohannes Berg 			wdev->conn->params.ie = wdev->conn->ie;
71019957bb3SJohannes Berg 			if (!wdev->conn->ie) {
71119957bb3SJohannes Berg 				kfree(wdev->conn);
71219957bb3SJohannes Berg 				wdev->conn = NULL;
7136829c878SJohannes Berg 				return -ENOMEM;
7146829c878SJohannes Berg 			}
71519957bb3SJohannes Berg 		}
7166829c878SJohannes Berg 
7176829c878SJohannes Berg 		if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
7186829c878SJohannes Berg 			wdev->conn->auto_auth = true;
7196829c878SJohannes Berg 			/* start with open system ... should mostly work */
7206829c878SJohannes Berg 			wdev->conn->params.auth_type =
7216829c878SJohannes Berg 				NL80211_AUTHTYPE_OPEN_SYSTEM;
7226829c878SJohannes Berg 		} else {
7236829c878SJohannes Berg 			wdev->conn->auto_auth = false;
7246829c878SJohannes Berg 		}
7256829c878SJohannes Berg 
7266829c878SJohannes Berg 		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
7276829c878SJohannes Berg 		wdev->ssid_len = connect->ssid_len;
7286829c878SJohannes Berg 		wdev->conn->params.ssid = wdev->ssid;
7296829c878SJohannes Berg 		wdev->conn->params.ssid_len = connect->ssid_len;
7306829c878SJohannes Berg 
7316829c878SJohannes Berg 		/* don't care about result -- but fill bssid & channel */
7326829c878SJohannes Berg 		if (!wdev->conn->params.bssid || !wdev->conn->params.channel)
7336829c878SJohannes Berg 			cfg80211_get_conn_bss(wdev);
7346829c878SJohannes Berg 
7356829c878SJohannes Berg 		wdev->sme_state = CFG80211_SME_CONNECTING;
736fffd0934SJohannes Berg 		wdev->connect_keys = connkeys;
7376829c878SJohannes Berg 
7386829c878SJohannes Berg 		/* we're good if we have both BSSID and channel */
7396829c878SJohannes Berg 		if (wdev->conn->params.bssid && wdev->conn->params.channel) {
7406829c878SJohannes Berg 			wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
7416829c878SJohannes Berg 			err = cfg80211_conn_do_work(wdev);
7426829c878SJohannes Berg 		} else {
7436829c878SJohannes Berg 			/* otherwise we'll need to scan for the AP first */
7446829c878SJohannes Berg 			err = cfg80211_conn_scan(wdev);
7456829c878SJohannes Berg 			/*
7466829c878SJohannes Berg 			 * If we can't scan right now, then we need to scan again
7476829c878SJohannes Berg 			 * after the current scan finished, since the parameters
7486829c878SJohannes Berg 			 * changed (unless we find a good AP anyway).
7496829c878SJohannes Berg 			 */
7506829c878SJohannes Berg 			if (err == -EBUSY) {
7516829c878SJohannes Berg 				err = 0;
7526829c878SJohannes Berg 				wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
7536829c878SJohannes Berg 			}
7546829c878SJohannes Berg 		}
75519957bb3SJohannes Berg 		if (err) {
75619957bb3SJohannes Berg 			kfree(wdev->conn);
75719957bb3SJohannes Berg 			wdev->conn = NULL;
7586829c878SJohannes Berg 			wdev->sme_state = CFG80211_SME_IDLE;
759fffd0934SJohannes Berg 			wdev->connect_keys = NULL;
7608dadadb7SJohannes Berg 			wdev->ssid_len = 0;
76119957bb3SJohannes Berg 		}
7626829c878SJohannes Berg 
7636829c878SJohannes Berg 		return err;
764b23aa676SSamuel Ortiz 	} else {
765b23aa676SSamuel Ortiz 		wdev->sme_state = CFG80211_SME_CONNECTING;
766fffd0934SJohannes Berg 		wdev->connect_keys = connkeys;
767b23aa676SSamuel Ortiz 		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
768b23aa676SSamuel Ortiz 		if (err) {
769fffd0934SJohannes Berg 			wdev->connect_keys = NULL;
770b23aa676SSamuel Ortiz 			wdev->sme_state = CFG80211_SME_IDLE;
771b23aa676SSamuel Ortiz 			return err;
772b23aa676SSamuel Ortiz 		}
773b23aa676SSamuel Ortiz 
774b23aa676SSamuel Ortiz 		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
775b23aa676SSamuel Ortiz 		wdev->ssid_len = connect->ssid_len;
776b23aa676SSamuel Ortiz 
777b23aa676SSamuel Ortiz 		return 0;
778b23aa676SSamuel Ortiz 	}
7796829c878SJohannes Berg }
780b23aa676SSamuel Ortiz 
781667503ddSJohannes Berg int cfg80211_connect(struct cfg80211_registered_device *rdev,
782667503ddSJohannes Berg 		     struct net_device *dev,
783fffd0934SJohannes Berg 		     struct cfg80211_connect_params *connect,
784fffd0934SJohannes Berg 		     struct cfg80211_cached_keys *connkeys)
785667503ddSJohannes Berg {
786667503ddSJohannes Berg 	int err;
787667503ddSJohannes Berg 
788667503ddSJohannes Berg 	wdev_lock(dev->ieee80211_ptr);
789fffd0934SJohannes Berg 	err = __cfg80211_connect(rdev, dev, connect, connkeys);
790667503ddSJohannes Berg 	wdev_unlock(dev->ieee80211_ptr);
791667503ddSJohannes Berg 
792667503ddSJohannes Berg 	return err;
793667503ddSJohannes Berg }
794667503ddSJohannes Berg 
795667503ddSJohannes Berg int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
796f2129354SJohannes Berg 			  struct net_device *dev, u16 reason, bool wextev)
797b23aa676SSamuel Ortiz {
7986829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
799b23aa676SSamuel Ortiz 	int err;
800b23aa676SSamuel Ortiz 
801667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
802667503ddSJohannes Berg 
8036829c878SJohannes Berg 	if (wdev->sme_state == CFG80211_SME_IDLE)
8046829c878SJohannes Berg 		return -EINVAL;
8056829c878SJohannes Berg 
806fffd0934SJohannes Berg 	kfree(wdev->connect_keys);
807fffd0934SJohannes Berg 	wdev->connect_keys = NULL;
808fffd0934SJohannes Berg 
809b23aa676SSamuel Ortiz 	if (!rdev->ops->disconnect) {
81019957bb3SJohannes Berg 		if (!rdev->ops->deauth)
81119957bb3SJohannes Berg 			return -EOPNOTSUPP;
8126829c878SJohannes Berg 
81319957bb3SJohannes Berg 		/* was it connected by userspace SME? */
81419957bb3SJohannes Berg 		if (!wdev->conn) {
81519957bb3SJohannes Berg 			cfg80211_mlme_down(rdev, dev);
81619957bb3SJohannes Berg 			return 0;
81719957bb3SJohannes Berg 		}
8186829c878SJohannes Berg 
8196829c878SJohannes Berg 		if (wdev->sme_state == CFG80211_SME_CONNECTING &&
8206829c878SJohannes Berg 		    (wdev->conn->state == CFG80211_CONN_SCANNING ||
8216829c878SJohannes Berg 		     wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) {
8226829c878SJohannes Berg 			wdev->sme_state = CFG80211_SME_IDLE;
82319957bb3SJohannes Berg 			kfree(wdev->conn);
82419957bb3SJohannes Berg 			wdev->conn = NULL;
8258dadadb7SJohannes Berg 			wdev->ssid_len = 0;
8266829c878SJohannes Berg 			return 0;
8276829c878SJohannes Berg 		}
8286829c878SJohannes Berg 
8296829c878SJohannes Berg 		/* wdev->conn->params.bssid must be set if > SCANNING */
830667503ddSJohannes Berg 		err = __cfg80211_mlme_deauth(rdev, dev,
831667503ddSJohannes Berg 					     wdev->conn->params.bssid,
83219957bb3SJohannes Berg 					     NULL, 0, reason);
8336829c878SJohannes Berg 		if (err)
8346829c878SJohannes Berg 			return err;
835b23aa676SSamuel Ortiz 	} else {
836b23aa676SSamuel Ortiz 		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
837b23aa676SSamuel Ortiz 		if (err)
838b23aa676SSamuel Ortiz 			return err;
839b23aa676SSamuel Ortiz 	}
840b23aa676SSamuel Ortiz 
8416829c878SJohannes Berg 	if (wdev->sme_state == CFG80211_SME_CONNECTED)
842667503ddSJohannes Berg 		__cfg80211_disconnected(dev, NULL, 0, 0, false);
8436829c878SJohannes Berg 	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
844f2129354SJohannes Berg 		__cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
8456829c878SJohannes Berg 					  WLAN_STATUS_UNSPECIFIED_FAILURE,
846df7fc0f9SJohannes Berg 					  wextev, NULL);
847b23aa676SSamuel Ortiz 
848b23aa676SSamuel Ortiz 	return 0;
849b23aa676SSamuel Ortiz }
85019957bb3SJohannes Berg 
851667503ddSJohannes Berg int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
852667503ddSJohannes Berg 			struct net_device *dev,
853667503ddSJohannes Berg 			u16 reason, bool wextev)
854667503ddSJohannes Berg {
855667503ddSJohannes Berg 	int err;
856667503ddSJohannes Berg 
857667503ddSJohannes Berg 	wdev_lock(dev->ieee80211_ptr);
858667503ddSJohannes Berg 	err = __cfg80211_disconnect(rdev, dev, reason, wextev);
859667503ddSJohannes Berg 	wdev_unlock(dev->ieee80211_ptr);
860667503ddSJohannes Berg 
861667503ddSJohannes Berg 	return err;
862667503ddSJohannes Berg }
863667503ddSJohannes Berg 
86419957bb3SJohannes Berg void cfg80211_sme_disassoc(struct net_device *dev, int idx)
86519957bb3SJohannes Berg {
86619957bb3SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
86719957bb3SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
86819957bb3SJohannes Berg 	u8 bssid[ETH_ALEN];
86919957bb3SJohannes Berg 
870667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
871667503ddSJohannes Berg 
87219957bb3SJohannes Berg 	if (!wdev->conn)
87319957bb3SJohannes Berg 		return;
87419957bb3SJohannes Berg 
87519957bb3SJohannes Berg 	if (wdev->conn->state == CFG80211_CONN_IDLE)
87619957bb3SJohannes Berg 		return;
87719957bb3SJohannes Berg 
87819957bb3SJohannes Berg 	/*
87919957bb3SJohannes Berg 	 * Ok, so the association was made by this SME -- we don't
88019957bb3SJohannes Berg 	 * want it any more so deauthenticate too.
88119957bb3SJohannes Berg 	 */
88219957bb3SJohannes Berg 
88319957bb3SJohannes Berg 	if (!wdev->auth_bsses[idx])
88419957bb3SJohannes Berg 		return;
88519957bb3SJohannes Berg 
88619957bb3SJohannes Berg 	memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN);
887ec3f1490SJohannes Berg 	if (__cfg80211_mlme_deauth(rdev, dev, bssid,
88819957bb3SJohannes Berg 				   NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
88919957bb3SJohannes Berg 		/* whatever -- assume gone anyway */
89019957bb3SJohannes Berg 		cfg80211_unhold_bss(wdev->auth_bsses[idx]);
89119957bb3SJohannes Berg 		cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);
89219957bb3SJohannes Berg 		wdev->auth_bsses[idx] = NULL;
89319957bb3SJohannes Berg 	}
89419957bb3SJohannes Berg }
895