18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SME code for cfg80211
48c2ecf20Sopenharmony_ci * both driver SME event handling and the SME implementation
58c2ecf20Sopenharmony_ci * (for nl80211's connect() and wext)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
88c2ecf20Sopenharmony_ci * Copyright (C) 2009, 2020 Intel Corporation. All rights reserved.
98c2ecf20Sopenharmony_ci * Copyright 2017	Intel Deutschland GmbH
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
138c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
168c2ecf20Sopenharmony_ci#include <linux/wireless.h>
178c2ecf20Sopenharmony_ci#include <linux/export.h>
188c2ecf20Sopenharmony_ci#include <net/iw_handler.h>
198c2ecf20Sopenharmony_ci#include <net/cfg80211.h>
208c2ecf20Sopenharmony_ci#include <net/rtnetlink.h>
218c2ecf20Sopenharmony_ci#include "nl80211.h"
228c2ecf20Sopenharmony_ci#include "reg.h"
238c2ecf20Sopenharmony_ci#include "rdev-ops.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * Software SME in cfg80211, using auth/assoc/deauth calls to the
278c2ecf20Sopenharmony_ci * driver. This is for implementing nl80211's connect/disconnect
288c2ecf20Sopenharmony_ci * and wireless extensions (if configured.)
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct cfg80211_conn {
328c2ecf20Sopenharmony_ci	struct cfg80211_connect_params params;
338c2ecf20Sopenharmony_ci	/* these are sub-states of the _CONNECTING sme_state */
348c2ecf20Sopenharmony_ci	enum {
358c2ecf20Sopenharmony_ci		CFG80211_CONN_SCANNING,
368c2ecf20Sopenharmony_ci		CFG80211_CONN_SCAN_AGAIN,
378c2ecf20Sopenharmony_ci		CFG80211_CONN_AUTHENTICATE_NEXT,
388c2ecf20Sopenharmony_ci		CFG80211_CONN_AUTHENTICATING,
398c2ecf20Sopenharmony_ci		CFG80211_CONN_AUTH_FAILED_TIMEOUT,
408c2ecf20Sopenharmony_ci		CFG80211_CONN_ASSOCIATE_NEXT,
418c2ecf20Sopenharmony_ci		CFG80211_CONN_ASSOCIATING,
428c2ecf20Sopenharmony_ci		CFG80211_CONN_ASSOC_FAILED,
438c2ecf20Sopenharmony_ci		CFG80211_CONN_ASSOC_FAILED_TIMEOUT,
448c2ecf20Sopenharmony_ci		CFG80211_CONN_DEAUTH,
458c2ecf20Sopenharmony_ci		CFG80211_CONN_ABANDON,
468c2ecf20Sopenharmony_ci		CFG80211_CONN_CONNECTED,
478c2ecf20Sopenharmony_ci	} state;
488c2ecf20Sopenharmony_ci	u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
498c2ecf20Sopenharmony_ci	const u8 *ie;
508c2ecf20Sopenharmony_ci	size_t ie_len;
518c2ecf20Sopenharmony_ci	bool auto_auth, prev_bssid_valid;
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic void cfg80211_sme_free(struct wireless_dev *wdev)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	if (!wdev->conn)
578c2ecf20Sopenharmony_ci		return;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	kfree(wdev->conn->ie);
608c2ecf20Sopenharmony_ci	kfree(wdev->conn);
618c2ecf20Sopenharmony_ci	wdev->conn = NULL;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int cfg80211_conn_scan(struct wireless_dev *wdev)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
678c2ecf20Sopenharmony_ci	struct cfg80211_scan_request *request;
688c2ecf20Sopenharmony_ci	int n_channels, err;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ASSERT_RTNL();
718c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (rdev->scan_req || rdev->scan_msg)
748c2ecf20Sopenharmony_ci		return -EBUSY;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (wdev->conn->params.channel)
778c2ecf20Sopenharmony_ci		n_channels = 1;
788c2ecf20Sopenharmony_ci	else
798c2ecf20Sopenharmony_ci		n_channels = ieee80211_get_num_supported_channels(wdev->wiphy);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
828c2ecf20Sopenharmony_ci			  sizeof(request->channels[0]) * n_channels,
838c2ecf20Sopenharmony_ci			  GFP_KERNEL);
848c2ecf20Sopenharmony_ci	if (!request)
858c2ecf20Sopenharmony_ci		return -ENOMEM;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (wdev->conn->params.channel) {
888c2ecf20Sopenharmony_ci		enum nl80211_band band = wdev->conn->params.channel->band;
898c2ecf20Sopenharmony_ci		struct ieee80211_supported_band *sband =
908c2ecf20Sopenharmony_ci			wdev->wiphy->bands[band];
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci		if (!sband) {
938c2ecf20Sopenharmony_ci			kfree(request);
948c2ecf20Sopenharmony_ci			return -EINVAL;
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci		request->channels[0] = wdev->conn->params.channel;
978c2ecf20Sopenharmony_ci		request->rates[band] = (1 << sband->n_bitrates) - 1;
988c2ecf20Sopenharmony_ci	} else {
998c2ecf20Sopenharmony_ci		int i = 0, j;
1008c2ecf20Sopenharmony_ci		enum nl80211_band band;
1018c2ecf20Sopenharmony_ci		struct ieee80211_supported_band *bands;
1028c2ecf20Sopenharmony_ci		struct ieee80211_channel *channel;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		for (band = 0; band < NUM_NL80211_BANDS; band++) {
1058c2ecf20Sopenharmony_ci			bands = wdev->wiphy->bands[band];
1068c2ecf20Sopenharmony_ci			if (!bands)
1078c2ecf20Sopenharmony_ci				continue;
1088c2ecf20Sopenharmony_ci			for (j = 0; j < bands->n_channels; j++) {
1098c2ecf20Sopenharmony_ci				channel = &bands->channels[j];
1108c2ecf20Sopenharmony_ci				if (channel->flags & IEEE80211_CHAN_DISABLED)
1118c2ecf20Sopenharmony_ci					continue;
1128c2ecf20Sopenharmony_ci				request->channels[i++] = channel;
1138c2ecf20Sopenharmony_ci			}
1148c2ecf20Sopenharmony_ci			request->rates[band] = (1 << bands->n_bitrates) - 1;
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci		n_channels = i;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	request->n_channels = n_channels;
1198c2ecf20Sopenharmony_ci	request->ssids = (void *)&request->channels[n_channels];
1208c2ecf20Sopenharmony_ci	request->n_ssids = 1;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	memcpy(request->ssids[0].ssid, wdev->conn->params.ssid,
1238c2ecf20Sopenharmony_ci		wdev->conn->params.ssid_len);
1248c2ecf20Sopenharmony_ci	request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	eth_broadcast_addr(request->bssid);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	request->wdev = wdev;
1298c2ecf20Sopenharmony_ci	request->wiphy = &rdev->wiphy;
1308c2ecf20Sopenharmony_ci	request->scan_start = jiffies;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	rdev->scan_req = request;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	err = rdev_scan(rdev, request);
1358c2ecf20Sopenharmony_ci	if (!err) {
1368c2ecf20Sopenharmony_ci		wdev->conn->state = CFG80211_CONN_SCANNING;
1378c2ecf20Sopenharmony_ci		nl80211_send_scan_start(rdev, wdev);
1388c2ecf20Sopenharmony_ci		dev_hold(wdev->netdev);
1398c2ecf20Sopenharmony_ci	} else {
1408c2ecf20Sopenharmony_ci		rdev->scan_req = NULL;
1418c2ecf20Sopenharmony_ci		kfree(request);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	return err;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int cfg80211_conn_do_work(struct wireless_dev *wdev,
1478c2ecf20Sopenharmony_ci				 enum nl80211_timeout_reason *treason)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1508c2ecf20Sopenharmony_ci	struct cfg80211_connect_params *params;
1518c2ecf20Sopenharmony_ci	struct cfg80211_assoc_request req = {};
1528c2ecf20Sopenharmony_ci	int err;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (!wdev->conn)
1578c2ecf20Sopenharmony_ci		return 0;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	params = &wdev->conn->params;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	switch (wdev->conn->state) {
1628c2ecf20Sopenharmony_ci	case CFG80211_CONN_SCANNING:
1638c2ecf20Sopenharmony_ci		/* didn't find it during scan ... */
1648c2ecf20Sopenharmony_ci		return -ENOENT;
1658c2ecf20Sopenharmony_ci	case CFG80211_CONN_SCAN_AGAIN:
1668c2ecf20Sopenharmony_ci		return cfg80211_conn_scan(wdev);
1678c2ecf20Sopenharmony_ci	case CFG80211_CONN_AUTHENTICATE_NEXT:
1688c2ecf20Sopenharmony_ci		if (WARN_ON(!rdev->ops->auth))
1698c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
1708c2ecf20Sopenharmony_ci		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
1718c2ecf20Sopenharmony_ci		return cfg80211_mlme_auth(rdev, wdev->netdev,
1728c2ecf20Sopenharmony_ci					  params->channel, params->auth_type,
1738c2ecf20Sopenharmony_ci					  params->bssid,
1748c2ecf20Sopenharmony_ci					  params->ssid, params->ssid_len,
1758c2ecf20Sopenharmony_ci					  NULL, 0,
1768c2ecf20Sopenharmony_ci					  params->key, params->key_len,
1778c2ecf20Sopenharmony_ci					  params->key_idx, NULL, 0);
1788c2ecf20Sopenharmony_ci	case CFG80211_CONN_AUTH_FAILED_TIMEOUT:
1798c2ecf20Sopenharmony_ci		*treason = NL80211_TIMEOUT_AUTH;
1808c2ecf20Sopenharmony_ci		return -ENOTCONN;
1818c2ecf20Sopenharmony_ci	case CFG80211_CONN_ASSOCIATE_NEXT:
1828c2ecf20Sopenharmony_ci		if (WARN_ON(!rdev->ops->assoc))
1838c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
1848c2ecf20Sopenharmony_ci		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
1858c2ecf20Sopenharmony_ci		if (wdev->conn->prev_bssid_valid)
1868c2ecf20Sopenharmony_ci			req.prev_bssid = wdev->conn->prev_bssid;
1878c2ecf20Sopenharmony_ci		req.ie = params->ie;
1888c2ecf20Sopenharmony_ci		req.ie_len = params->ie_len;
1898c2ecf20Sopenharmony_ci		req.use_mfp = params->mfp != NL80211_MFP_NO;
1908c2ecf20Sopenharmony_ci		req.crypto = params->crypto;
1918c2ecf20Sopenharmony_ci		req.flags = params->flags;
1928c2ecf20Sopenharmony_ci		req.ht_capa = params->ht_capa;
1938c2ecf20Sopenharmony_ci		req.ht_capa_mask = params->ht_capa_mask;
1948c2ecf20Sopenharmony_ci		req.vht_capa = params->vht_capa;
1958c2ecf20Sopenharmony_ci		req.vht_capa_mask = params->vht_capa_mask;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		err = cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel,
1988c2ecf20Sopenharmony_ci					  params->bssid, params->ssid,
1998c2ecf20Sopenharmony_ci					  params->ssid_len, &req);
2008c2ecf20Sopenharmony_ci		if (err)
2018c2ecf20Sopenharmony_ci			cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
2028c2ecf20Sopenharmony_ci					     NULL, 0,
2038c2ecf20Sopenharmony_ci					     WLAN_REASON_DEAUTH_LEAVING,
2048c2ecf20Sopenharmony_ci					     false);
2058c2ecf20Sopenharmony_ci		return err;
2068c2ecf20Sopenharmony_ci	case CFG80211_CONN_ASSOC_FAILED_TIMEOUT:
2078c2ecf20Sopenharmony_ci		*treason = NL80211_TIMEOUT_ASSOC;
2088c2ecf20Sopenharmony_ci		fallthrough;
2098c2ecf20Sopenharmony_ci	case CFG80211_CONN_ASSOC_FAILED:
2108c2ecf20Sopenharmony_ci		cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
2118c2ecf20Sopenharmony_ci				     NULL, 0,
2128c2ecf20Sopenharmony_ci				     WLAN_REASON_DEAUTH_LEAVING, false);
2138c2ecf20Sopenharmony_ci		return -ENOTCONN;
2148c2ecf20Sopenharmony_ci	case CFG80211_CONN_DEAUTH:
2158c2ecf20Sopenharmony_ci		cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
2168c2ecf20Sopenharmony_ci				     NULL, 0,
2178c2ecf20Sopenharmony_ci				     WLAN_REASON_DEAUTH_LEAVING, false);
2188c2ecf20Sopenharmony_ci		fallthrough;
2198c2ecf20Sopenharmony_ci	case CFG80211_CONN_ABANDON:
2208c2ecf20Sopenharmony_ci		/* free directly, disconnected event already sent */
2218c2ecf20Sopenharmony_ci		cfg80211_sme_free(wdev);
2228c2ecf20Sopenharmony_ci		return 0;
2238c2ecf20Sopenharmony_ci	default:
2248c2ecf20Sopenharmony_ci		return 0;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_civoid cfg80211_conn_work(struct work_struct *work)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev =
2318c2ecf20Sopenharmony_ci		container_of(work, struct cfg80211_registered_device, conn_work);
2328c2ecf20Sopenharmony_ci	struct wireless_dev *wdev;
2338c2ecf20Sopenharmony_ci	u8 bssid_buf[ETH_ALEN], *bssid = NULL;
2348c2ecf20Sopenharmony_ci	enum nl80211_timeout_reason treason;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	rtnl_lock();
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
2398c2ecf20Sopenharmony_ci		if (!wdev->netdev)
2408c2ecf20Sopenharmony_ci			continue;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci		wdev_lock(wdev);
2438c2ecf20Sopenharmony_ci		if (!netif_running(wdev->netdev)) {
2448c2ecf20Sopenharmony_ci			wdev_unlock(wdev);
2458c2ecf20Sopenharmony_ci			continue;
2468c2ecf20Sopenharmony_ci		}
2478c2ecf20Sopenharmony_ci		if (!wdev->conn ||
2488c2ecf20Sopenharmony_ci		    wdev->conn->state == CFG80211_CONN_CONNECTED) {
2498c2ecf20Sopenharmony_ci			wdev_unlock(wdev);
2508c2ecf20Sopenharmony_ci			continue;
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci		if (wdev->conn->params.bssid) {
2538c2ecf20Sopenharmony_ci			memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
2548c2ecf20Sopenharmony_ci			bssid = bssid_buf;
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci		treason = NL80211_TIMEOUT_UNSPECIFIED;
2578c2ecf20Sopenharmony_ci		if (cfg80211_conn_do_work(wdev, &treason)) {
2588c2ecf20Sopenharmony_ci			struct cfg80211_connect_resp_params cr;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci			memset(&cr, 0, sizeof(cr));
2618c2ecf20Sopenharmony_ci			cr.status = -1;
2628c2ecf20Sopenharmony_ci			cr.bssid = bssid;
2638c2ecf20Sopenharmony_ci			cr.timeout_reason = treason;
2648c2ecf20Sopenharmony_ci			__cfg80211_connect_result(wdev->netdev, &cr, false);
2658c2ecf20Sopenharmony_ci		}
2668c2ecf20Sopenharmony_ci		wdev_unlock(wdev);
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	rtnl_unlock();
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic void cfg80211_step_auth_next(struct cfg80211_conn *conn,
2738c2ecf20Sopenharmony_ci				    struct cfg80211_bss *bss)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	memcpy(conn->bssid, bss->bssid, ETH_ALEN);
2768c2ecf20Sopenharmony_ci	conn->params.bssid = conn->bssid;
2778c2ecf20Sopenharmony_ci	conn->params.channel = bss->channel;
2788c2ecf20Sopenharmony_ci	conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci/* Returned bss is reference counted and must be cleaned up appropriately. */
2828c2ecf20Sopenharmony_cistatic struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
2858c2ecf20Sopenharmony_ci	struct cfg80211_bss *bss;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel,
2908c2ecf20Sopenharmony_ci			       wdev->conn->params.bssid,
2918c2ecf20Sopenharmony_ci			       wdev->conn->params.ssid,
2928c2ecf20Sopenharmony_ci			       wdev->conn->params.ssid_len,
2938c2ecf20Sopenharmony_ci			       wdev->conn_bss_type,
2948c2ecf20Sopenharmony_ci			       IEEE80211_PRIVACY(wdev->conn->params.privacy));
2958c2ecf20Sopenharmony_ci	if (!bss)
2968c2ecf20Sopenharmony_ci		return NULL;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	cfg80211_step_auth_next(wdev->conn, bss);
2998c2ecf20Sopenharmony_ci	schedule_work(&rdev->conn_work);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	return bss;
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic void __cfg80211_sme_scan_done(struct net_device *dev)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
3078c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
3088c2ecf20Sopenharmony_ci	struct cfg80211_bss *bss;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (!wdev->conn)
3138c2ecf20Sopenharmony_ci		return;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (wdev->conn->state != CFG80211_CONN_SCANNING &&
3168c2ecf20Sopenharmony_ci	    wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
3178c2ecf20Sopenharmony_ci		return;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	bss = cfg80211_get_conn_bss(wdev);
3208c2ecf20Sopenharmony_ci	if (bss)
3218c2ecf20Sopenharmony_ci		cfg80211_put_bss(&rdev->wiphy, bss);
3228c2ecf20Sopenharmony_ci	else
3238c2ecf20Sopenharmony_ci		schedule_work(&rdev->conn_work);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_civoid cfg80211_sme_scan_done(struct net_device *dev)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	wdev_lock(wdev);
3318c2ecf20Sopenharmony_ci	__cfg80211_sme_scan_done(dev);
3328c2ecf20Sopenharmony_ci	wdev_unlock(wdev);
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_civoid cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct wiphy *wiphy = wdev->wiphy;
3388c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
3398c2ecf20Sopenharmony_ci	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
3408c2ecf20Sopenharmony_ci	u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED)
3458c2ecf20Sopenharmony_ci		return;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
3488c2ecf20Sopenharmony_ci	    wdev->conn->auto_auth &&
3498c2ecf20Sopenharmony_ci	    wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
3508c2ecf20Sopenharmony_ci		/* select automatically between only open, shared, leap */
3518c2ecf20Sopenharmony_ci		switch (wdev->conn->params.auth_type) {
3528c2ecf20Sopenharmony_ci		case NL80211_AUTHTYPE_OPEN_SYSTEM:
3538c2ecf20Sopenharmony_ci			if (wdev->connect_keys)
3548c2ecf20Sopenharmony_ci				wdev->conn->params.auth_type =
3558c2ecf20Sopenharmony_ci					NL80211_AUTHTYPE_SHARED_KEY;
3568c2ecf20Sopenharmony_ci			else
3578c2ecf20Sopenharmony_ci				wdev->conn->params.auth_type =
3588c2ecf20Sopenharmony_ci					NL80211_AUTHTYPE_NETWORK_EAP;
3598c2ecf20Sopenharmony_ci			break;
3608c2ecf20Sopenharmony_ci		case NL80211_AUTHTYPE_SHARED_KEY:
3618c2ecf20Sopenharmony_ci			wdev->conn->params.auth_type =
3628c2ecf20Sopenharmony_ci				NL80211_AUTHTYPE_NETWORK_EAP;
3638c2ecf20Sopenharmony_ci			break;
3648c2ecf20Sopenharmony_ci		default:
3658c2ecf20Sopenharmony_ci			/* huh? */
3668c2ecf20Sopenharmony_ci			wdev->conn->params.auth_type =
3678c2ecf20Sopenharmony_ci				NL80211_AUTHTYPE_OPEN_SYSTEM;
3688c2ecf20Sopenharmony_ci			break;
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci		wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
3718c2ecf20Sopenharmony_ci		schedule_work(&rdev->conn_work);
3728c2ecf20Sopenharmony_ci	} else if (status_code != WLAN_STATUS_SUCCESS) {
3738c2ecf20Sopenharmony_ci		struct cfg80211_connect_resp_params cr;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		memset(&cr, 0, sizeof(cr));
3768c2ecf20Sopenharmony_ci		cr.status = status_code;
3778c2ecf20Sopenharmony_ci		cr.bssid = mgmt->bssid;
3788c2ecf20Sopenharmony_ci		cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED;
3798c2ecf20Sopenharmony_ci		__cfg80211_connect_result(wdev->netdev, &cr, false);
3808c2ecf20Sopenharmony_ci	} else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
3818c2ecf20Sopenharmony_ci		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
3828c2ecf20Sopenharmony_ci		schedule_work(&rdev->conn_work);
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cibool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (!wdev->conn)
3918c2ecf20Sopenharmony_ci		return false;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (status == WLAN_STATUS_SUCCESS) {
3948c2ecf20Sopenharmony_ci		wdev->conn->state = CFG80211_CONN_CONNECTED;
3958c2ecf20Sopenharmony_ci		return false;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (wdev->conn->prev_bssid_valid) {
3998c2ecf20Sopenharmony_ci		/*
4008c2ecf20Sopenharmony_ci		 * Some stupid APs don't accept reassoc, so we
4018c2ecf20Sopenharmony_ci		 * need to fall back to trying regular assoc;
4028c2ecf20Sopenharmony_ci		 * return true so no event is sent to userspace.
4038c2ecf20Sopenharmony_ci		 */
4048c2ecf20Sopenharmony_ci		wdev->conn->prev_bssid_valid = false;
4058c2ecf20Sopenharmony_ci		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
4068c2ecf20Sopenharmony_ci		schedule_work(&rdev->conn_work);
4078c2ecf20Sopenharmony_ci		return true;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	wdev->conn->state = CFG80211_CONN_ASSOC_FAILED;
4118c2ecf20Sopenharmony_ci	schedule_work(&rdev->conn_work);
4128c2ecf20Sopenharmony_ci	return false;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_civoid cfg80211_sme_deauth(struct wireless_dev *wdev)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	cfg80211_sme_free(wdev);
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_civoid cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (!wdev->conn)
4258c2ecf20Sopenharmony_ci		return;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	wdev->conn->state = CFG80211_CONN_AUTH_FAILED_TIMEOUT;
4288c2ecf20Sopenharmony_ci	schedule_work(&rdev->conn_work);
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_civoid cfg80211_sme_disassoc(struct wireless_dev *wdev)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (!wdev->conn)
4368c2ecf20Sopenharmony_ci		return;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	wdev->conn->state = CFG80211_CONN_DEAUTH;
4398c2ecf20Sopenharmony_ci	schedule_work(&rdev->conn_work);
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_civoid cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (!wdev->conn)
4478c2ecf20Sopenharmony_ci		return;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	wdev->conn->state = CFG80211_CONN_ASSOC_FAILED_TIMEOUT;
4508c2ecf20Sopenharmony_ci	schedule_work(&rdev->conn_work);
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_civoid cfg80211_sme_abandon_assoc(struct wireless_dev *wdev)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (!wdev->conn)
4588c2ecf20Sopenharmony_ci		return;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	wdev->conn->state = CFG80211_CONN_ABANDON;
4618c2ecf20Sopenharmony_ci	schedule_work(&rdev->conn_work);
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev,
4658c2ecf20Sopenharmony_ci				     const u8 *ies, size_t ies_len,
4668c2ecf20Sopenharmony_ci				     const u8 **out_ies, size_t *out_ies_len)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
4698c2ecf20Sopenharmony_ci	u8 *buf;
4708c2ecf20Sopenharmony_ci	size_t offs;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (!rdev->wiphy.extended_capabilities_len ||
4738c2ecf20Sopenharmony_ci	    (ies && cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ies_len))) {
4748c2ecf20Sopenharmony_ci		*out_ies = kmemdup(ies, ies_len, GFP_KERNEL);
4758c2ecf20Sopenharmony_ci		if (!*out_ies)
4768c2ecf20Sopenharmony_ci			return -ENOMEM;
4778c2ecf20Sopenharmony_ci		*out_ies_len = ies_len;
4788c2ecf20Sopenharmony_ci		return 0;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	buf = kmalloc(ies_len + rdev->wiphy.extended_capabilities_len + 2,
4828c2ecf20Sopenharmony_ci		      GFP_KERNEL);
4838c2ecf20Sopenharmony_ci	if (!buf)
4848c2ecf20Sopenharmony_ci		return -ENOMEM;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (ies_len) {
4878c2ecf20Sopenharmony_ci		static const u8 before_extcapa[] = {
4888c2ecf20Sopenharmony_ci			/* not listing IEs expected to be created by driver */
4898c2ecf20Sopenharmony_ci			WLAN_EID_RSN,
4908c2ecf20Sopenharmony_ci			WLAN_EID_QOS_CAPA,
4918c2ecf20Sopenharmony_ci			WLAN_EID_RRM_ENABLED_CAPABILITIES,
4928c2ecf20Sopenharmony_ci			WLAN_EID_MOBILITY_DOMAIN,
4938c2ecf20Sopenharmony_ci			WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
4948c2ecf20Sopenharmony_ci			WLAN_EID_BSS_COEX_2040,
4958c2ecf20Sopenharmony_ci		};
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		offs = ieee80211_ie_split(ies, ies_len, before_extcapa,
4988c2ecf20Sopenharmony_ci					  ARRAY_SIZE(before_extcapa), 0);
4998c2ecf20Sopenharmony_ci		memcpy(buf, ies, offs);
5008c2ecf20Sopenharmony_ci		/* leave a whole for extended capabilities IE */
5018c2ecf20Sopenharmony_ci		memcpy(buf + offs + rdev->wiphy.extended_capabilities_len + 2,
5028c2ecf20Sopenharmony_ci		       ies + offs, ies_len - offs);
5038c2ecf20Sopenharmony_ci	} else {
5048c2ecf20Sopenharmony_ci		offs = 0;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	/* place extended capabilities IE (with only driver capabilities) */
5088c2ecf20Sopenharmony_ci	buf[offs] = WLAN_EID_EXT_CAPABILITY;
5098c2ecf20Sopenharmony_ci	buf[offs + 1] = rdev->wiphy.extended_capabilities_len;
5108c2ecf20Sopenharmony_ci	memcpy(buf + offs + 2,
5118c2ecf20Sopenharmony_ci	       rdev->wiphy.extended_capabilities,
5128c2ecf20Sopenharmony_ci	       rdev->wiphy.extended_capabilities_len);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	*out_ies = buf;
5158c2ecf20Sopenharmony_ci	*out_ies_len = ies_len + rdev->wiphy.extended_capabilities_len + 2;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	return 0;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic int cfg80211_sme_connect(struct wireless_dev *wdev,
5218c2ecf20Sopenharmony_ci				struct cfg80211_connect_params *connect,
5228c2ecf20Sopenharmony_ci				const u8 *prev_bssid)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
5258c2ecf20Sopenharmony_ci	struct cfg80211_bss *bss;
5268c2ecf20Sopenharmony_ci	int err;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (!rdev->ops->auth || !rdev->ops->assoc)
5298c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (wdev->current_bss) {
5328c2ecf20Sopenharmony_ci		cfg80211_unhold_bss(wdev->current_bss);
5338c2ecf20Sopenharmony_ci		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
5348c2ecf20Sopenharmony_ci		wdev->current_bss = NULL;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		cfg80211_sme_free(wdev);
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (wdev->conn)
5408c2ecf20Sopenharmony_ci		return -EINPROGRESS;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
5438c2ecf20Sopenharmony_ci	if (!wdev->conn)
5448c2ecf20Sopenharmony_ci		return -ENOMEM;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	/*
5478c2ecf20Sopenharmony_ci	 * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
5488c2ecf20Sopenharmony_ci	 */
5498c2ecf20Sopenharmony_ci	memcpy(&wdev->conn->params, connect, sizeof(*connect));
5508c2ecf20Sopenharmony_ci	if (connect->bssid) {
5518c2ecf20Sopenharmony_ci		wdev->conn->params.bssid = wdev->conn->bssid;
5528c2ecf20Sopenharmony_ci		memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (cfg80211_sme_get_conn_ies(wdev, connect->ie, connect->ie_len,
5568c2ecf20Sopenharmony_ci				      &wdev->conn->ie,
5578c2ecf20Sopenharmony_ci				      &wdev->conn->params.ie_len)) {
5588c2ecf20Sopenharmony_ci		kfree(wdev->conn);
5598c2ecf20Sopenharmony_ci		wdev->conn = NULL;
5608c2ecf20Sopenharmony_ci		return -ENOMEM;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci	wdev->conn->params.ie = wdev->conn->ie;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
5658c2ecf20Sopenharmony_ci		wdev->conn->auto_auth = true;
5668c2ecf20Sopenharmony_ci		/* start with open system ... should mostly work */
5678c2ecf20Sopenharmony_ci		wdev->conn->params.auth_type =
5688c2ecf20Sopenharmony_ci			NL80211_AUTHTYPE_OPEN_SYSTEM;
5698c2ecf20Sopenharmony_ci	} else {
5708c2ecf20Sopenharmony_ci		wdev->conn->auto_auth = false;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	wdev->conn->params.ssid = wdev->ssid;
5748c2ecf20Sopenharmony_ci	wdev->conn->params.ssid_len = wdev->ssid_len;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* see if we have the bss already */
5778c2ecf20Sopenharmony_ci	bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel,
5788c2ecf20Sopenharmony_ci			       wdev->conn->params.bssid,
5798c2ecf20Sopenharmony_ci			       wdev->conn->params.ssid,
5808c2ecf20Sopenharmony_ci			       wdev->conn->params.ssid_len,
5818c2ecf20Sopenharmony_ci			       wdev->conn_bss_type,
5828c2ecf20Sopenharmony_ci			       IEEE80211_PRIVACY(wdev->conn->params.privacy));
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (prev_bssid) {
5858c2ecf20Sopenharmony_ci		memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
5868c2ecf20Sopenharmony_ci		wdev->conn->prev_bssid_valid = true;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* we're good if we have a matching bss struct */
5908c2ecf20Sopenharmony_ci	if (bss) {
5918c2ecf20Sopenharmony_ci		enum nl80211_timeout_reason treason;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci		cfg80211_step_auth_next(wdev->conn, bss);
5948c2ecf20Sopenharmony_ci		err = cfg80211_conn_do_work(wdev, &treason);
5958c2ecf20Sopenharmony_ci		cfg80211_put_bss(wdev->wiphy, bss);
5968c2ecf20Sopenharmony_ci	} else {
5978c2ecf20Sopenharmony_ci		/* otherwise we'll need to scan for the AP first */
5988c2ecf20Sopenharmony_ci		err = cfg80211_conn_scan(wdev);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		/*
6018c2ecf20Sopenharmony_ci		 * If we can't scan right now, then we need to scan again
6028c2ecf20Sopenharmony_ci		 * after the current scan finished, since the parameters
6038c2ecf20Sopenharmony_ci		 * changed (unless we find a good AP anyway).
6048c2ecf20Sopenharmony_ci		 */
6058c2ecf20Sopenharmony_ci		if (err == -EBUSY) {
6068c2ecf20Sopenharmony_ci			err = 0;
6078c2ecf20Sopenharmony_ci			wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
6088c2ecf20Sopenharmony_ci		}
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (err)
6128c2ecf20Sopenharmony_ci		cfg80211_sme_free(wdev);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return err;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
6208c2ecf20Sopenharmony_ci	int err;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (!wdev->conn)
6238c2ecf20Sopenharmony_ci		return 0;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	if (!rdev->ops->deauth)
6268c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	if (wdev->conn->state == CFG80211_CONN_SCANNING ||
6298c2ecf20Sopenharmony_ci	    wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) {
6308c2ecf20Sopenharmony_ci		err = 0;
6318c2ecf20Sopenharmony_ci		goto out;
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	/* wdev->conn->params.bssid must be set if > SCANNING */
6358c2ecf20Sopenharmony_ci	err = cfg80211_mlme_deauth(rdev, wdev->netdev,
6368c2ecf20Sopenharmony_ci				   wdev->conn->params.bssid,
6378c2ecf20Sopenharmony_ci				   NULL, 0, reason, false);
6388c2ecf20Sopenharmony_ci out:
6398c2ecf20Sopenharmony_ci	cfg80211_sme_free(wdev);
6408c2ecf20Sopenharmony_ci	return err;
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci/*
6448c2ecf20Sopenharmony_ci * code shared for in-device and software SME
6458c2ecf20Sopenharmony_ci */
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic bool cfg80211_is_all_idle(void)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev;
6508c2ecf20Sopenharmony_ci	struct wireless_dev *wdev;
6518c2ecf20Sopenharmony_ci	bool is_all_idle = true;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	/*
6548c2ecf20Sopenharmony_ci	 * All devices must be idle as otherwise if you are actively
6558c2ecf20Sopenharmony_ci	 * scanning some new beacon hints could be learned and would
6568c2ecf20Sopenharmony_ci	 * count as new regulatory hints.
6578c2ecf20Sopenharmony_ci	 * Also if there is any other active beaconing interface we
6588c2ecf20Sopenharmony_ci	 * need not issue a disconnect hint and reset any info such
6598c2ecf20Sopenharmony_ci	 * as chan dfs state, etc.
6608c2ecf20Sopenharmony_ci	 */
6618c2ecf20Sopenharmony_ci	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
6628c2ecf20Sopenharmony_ci		list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
6638c2ecf20Sopenharmony_ci			wdev_lock(wdev);
6648c2ecf20Sopenharmony_ci			if (wdev->conn || wdev->current_bss ||
6658c2ecf20Sopenharmony_ci			    cfg80211_beaconing_iface_active(wdev))
6668c2ecf20Sopenharmony_ci				is_all_idle = false;
6678c2ecf20Sopenharmony_ci			wdev_unlock(wdev);
6688c2ecf20Sopenharmony_ci		}
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	return is_all_idle;
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic void disconnect_work(struct work_struct *work)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	rtnl_lock();
6778c2ecf20Sopenharmony_ci	if (cfg80211_is_all_idle())
6788c2ecf20Sopenharmony_ci		regulatory_hint_disconnect();
6798c2ecf20Sopenharmony_ci	rtnl_unlock();
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ciDECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci/*
6868c2ecf20Sopenharmony_ci * API calls for drivers implementing connect/disconnect and
6878c2ecf20Sopenharmony_ci * SME event handling
6888c2ecf20Sopenharmony_ci */
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci/* This method must consume bss one way or another */
6918c2ecf20Sopenharmony_civoid __cfg80211_connect_result(struct net_device *dev,
6928c2ecf20Sopenharmony_ci			       struct cfg80211_connect_resp_params *cr,
6938c2ecf20Sopenharmony_ci			       bool wextev)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
6968c2ecf20Sopenharmony_ci	const u8 *country_ie;
6978c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT
6988c2ecf20Sopenharmony_ci	union iwreq_data wrqu;
6998c2ecf20Sopenharmony_ci#endif
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
7048c2ecf20Sopenharmony_ci		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) {
7058c2ecf20Sopenharmony_ci		cfg80211_put_bss(wdev->wiphy, cr->bss);
7068c2ecf20Sopenharmony_ci		return;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	wdev->unprot_beacon_reported = 0;
7108c2ecf20Sopenharmony_ci	nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr,
7118c2ecf20Sopenharmony_ci				    GFP_KERNEL);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT
7148c2ecf20Sopenharmony_ci	if (wextev) {
7158c2ecf20Sopenharmony_ci		if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) {
7168c2ecf20Sopenharmony_ci			memset(&wrqu, 0, sizeof(wrqu));
7178c2ecf20Sopenharmony_ci			wrqu.data.length = cr->req_ie_len;
7188c2ecf20Sopenharmony_ci			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu,
7198c2ecf20Sopenharmony_ci					    cr->req_ie);
7208c2ecf20Sopenharmony_ci		}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		if (cr->resp_ie && cr->status == WLAN_STATUS_SUCCESS) {
7238c2ecf20Sopenharmony_ci			memset(&wrqu, 0, sizeof(wrqu));
7248c2ecf20Sopenharmony_ci			wrqu.data.length = cr->resp_ie_len;
7258c2ecf20Sopenharmony_ci			wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu,
7268c2ecf20Sopenharmony_ci					    cr->resp_ie);
7278c2ecf20Sopenharmony_ci		}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci		memset(&wrqu, 0, sizeof(wrqu));
7308c2ecf20Sopenharmony_ci		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
7318c2ecf20Sopenharmony_ci		if (cr->bssid && cr->status == WLAN_STATUS_SUCCESS) {
7328c2ecf20Sopenharmony_ci			memcpy(wrqu.ap_addr.sa_data, cr->bssid, ETH_ALEN);
7338c2ecf20Sopenharmony_ci			memcpy(wdev->wext.prev_bssid, cr->bssid, ETH_ALEN);
7348c2ecf20Sopenharmony_ci			wdev->wext.prev_bssid_valid = true;
7358c2ecf20Sopenharmony_ci		}
7368c2ecf20Sopenharmony_ci		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
7378c2ecf20Sopenharmony_ci	}
7388c2ecf20Sopenharmony_ci#endif
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (!cr->bss && (cr->status == WLAN_STATUS_SUCCESS)) {
7418c2ecf20Sopenharmony_ci		WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect);
7428c2ecf20Sopenharmony_ci		cr->bss = cfg80211_get_bss(wdev->wiphy, NULL, cr->bssid,
7438c2ecf20Sopenharmony_ci					   wdev->ssid, wdev->ssid_len,
7448c2ecf20Sopenharmony_ci					   wdev->conn_bss_type,
7458c2ecf20Sopenharmony_ci					   IEEE80211_PRIVACY_ANY);
7468c2ecf20Sopenharmony_ci		if (cr->bss)
7478c2ecf20Sopenharmony_ci			cfg80211_hold_bss(bss_from_pub(cr->bss));
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (wdev->current_bss) {
7518c2ecf20Sopenharmony_ci		cfg80211_unhold_bss(wdev->current_bss);
7528c2ecf20Sopenharmony_ci		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
7538c2ecf20Sopenharmony_ci		wdev->current_bss = NULL;
7548c2ecf20Sopenharmony_ci	}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	if (cr->status != WLAN_STATUS_SUCCESS) {
7578c2ecf20Sopenharmony_ci		kfree_sensitive(wdev->connect_keys);
7588c2ecf20Sopenharmony_ci		wdev->connect_keys = NULL;
7598c2ecf20Sopenharmony_ci		wdev->ssid_len = 0;
7608c2ecf20Sopenharmony_ci		wdev->conn_owner_nlportid = 0;
7618c2ecf20Sopenharmony_ci		if (cr->bss) {
7628c2ecf20Sopenharmony_ci			cfg80211_unhold_bss(bss_from_pub(cr->bss));
7638c2ecf20Sopenharmony_ci			cfg80211_put_bss(wdev->wiphy, cr->bss);
7648c2ecf20Sopenharmony_ci		}
7658c2ecf20Sopenharmony_ci		cfg80211_sme_free(wdev);
7668c2ecf20Sopenharmony_ci		return;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	if (WARN_ON(!cr->bss))
7708c2ecf20Sopenharmony_ci		return;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	wdev->current_bss = bss_from_pub(cr->bss);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP))
7758c2ecf20Sopenharmony_ci		cfg80211_upload_connect_keys(wdev);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	rcu_read_lock();
7788c2ecf20Sopenharmony_ci	country_ie = ieee80211_bss_get_ie(cr->bss, WLAN_EID_COUNTRY);
7798c2ecf20Sopenharmony_ci	if (!country_ie) {
7808c2ecf20Sopenharmony_ci		rcu_read_unlock();
7818c2ecf20Sopenharmony_ci		return;
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC);
7858c2ecf20Sopenharmony_ci	rcu_read_unlock();
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	if (!country_ie)
7888c2ecf20Sopenharmony_ci		return;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	/*
7918c2ecf20Sopenharmony_ci	 * ieee80211_bss_get_ie() ensures we can access:
7928c2ecf20Sopenharmony_ci	 * - country_ie + 2, the start of the country ie data, and
7938c2ecf20Sopenharmony_ci	 * - and country_ie[1] which is the IE length
7948c2ecf20Sopenharmony_ci	 */
7958c2ecf20Sopenharmony_ci	regulatory_hint_country_ie(wdev->wiphy, cr->bss->channel->band,
7968c2ecf20Sopenharmony_ci				   country_ie + 2, country_ie[1]);
7978c2ecf20Sopenharmony_ci	kfree(country_ie);
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci/* Consumes bss object one way or another */
8018c2ecf20Sopenharmony_civoid cfg80211_connect_done(struct net_device *dev,
8028c2ecf20Sopenharmony_ci			   struct cfg80211_connect_resp_params *params,
8038c2ecf20Sopenharmony_ci			   gfp_t gfp)
8048c2ecf20Sopenharmony_ci{
8058c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
8068c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
8078c2ecf20Sopenharmony_ci	struct cfg80211_event *ev;
8088c2ecf20Sopenharmony_ci	unsigned long flags;
8098c2ecf20Sopenharmony_ci	u8 *next;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	if (params->bss) {
8128c2ecf20Sopenharmony_ci		struct cfg80211_internal_bss *ibss = bss_from_pub(params->bss);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		if (list_empty(&ibss->list)) {
8158c2ecf20Sopenharmony_ci			struct cfg80211_bss *found = NULL, *tmp = params->bss;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci			found = cfg80211_get_bss(wdev->wiphy, NULL,
8188c2ecf20Sopenharmony_ci						 params->bss->bssid,
8198c2ecf20Sopenharmony_ci						 wdev->ssid, wdev->ssid_len,
8208c2ecf20Sopenharmony_ci						 wdev->conn_bss_type,
8218c2ecf20Sopenharmony_ci						 IEEE80211_PRIVACY_ANY);
8228c2ecf20Sopenharmony_ci			if (found) {
8238c2ecf20Sopenharmony_ci				/* The same BSS is already updated so use it
8248c2ecf20Sopenharmony_ci				 * instead, as it has latest info.
8258c2ecf20Sopenharmony_ci				 */
8268c2ecf20Sopenharmony_ci				params->bss = found;
8278c2ecf20Sopenharmony_ci			} else {
8288c2ecf20Sopenharmony_ci				/* Update with BSS provided by driver, it will
8298c2ecf20Sopenharmony_ci				 * be freshly added and ref cnted, we can free
8308c2ecf20Sopenharmony_ci				 * the old one.
8318c2ecf20Sopenharmony_ci				 *
8328c2ecf20Sopenharmony_ci				 * signal_valid can be false, as we are not
8338c2ecf20Sopenharmony_ci				 * expecting the BSS to be found.
8348c2ecf20Sopenharmony_ci				 *
8358c2ecf20Sopenharmony_ci				 * keep the old timestamp to avoid confusion
8368c2ecf20Sopenharmony_ci				 */
8378c2ecf20Sopenharmony_ci				cfg80211_bss_update(rdev, ibss, false,
8388c2ecf20Sopenharmony_ci						    ibss->ts);
8398c2ecf20Sopenharmony_ci			}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci			cfg80211_put_bss(wdev->wiphy, tmp);
8428c2ecf20Sopenharmony_ci		}
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) +
8468c2ecf20Sopenharmony_ci		     params->req_ie_len + params->resp_ie_len +
8478c2ecf20Sopenharmony_ci		     params->fils.kek_len + params->fils.pmk_len +
8488c2ecf20Sopenharmony_ci		     (params->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp);
8498c2ecf20Sopenharmony_ci	if (!ev) {
8508c2ecf20Sopenharmony_ci		cfg80211_put_bss(wdev->wiphy, params->bss);
8518c2ecf20Sopenharmony_ci		return;
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	ev->type = EVENT_CONNECT_RESULT;
8558c2ecf20Sopenharmony_ci	next = ((u8 *)ev) + sizeof(*ev);
8568c2ecf20Sopenharmony_ci	if (params->bssid) {
8578c2ecf20Sopenharmony_ci		ev->cr.bssid = next;
8588c2ecf20Sopenharmony_ci		memcpy((void *)ev->cr.bssid, params->bssid, ETH_ALEN);
8598c2ecf20Sopenharmony_ci		next += ETH_ALEN;
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci	if (params->req_ie_len) {
8628c2ecf20Sopenharmony_ci		ev->cr.req_ie = next;
8638c2ecf20Sopenharmony_ci		ev->cr.req_ie_len = params->req_ie_len;
8648c2ecf20Sopenharmony_ci		memcpy((void *)ev->cr.req_ie, params->req_ie,
8658c2ecf20Sopenharmony_ci		       params->req_ie_len);
8668c2ecf20Sopenharmony_ci		next += params->req_ie_len;
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ci	if (params->resp_ie_len) {
8698c2ecf20Sopenharmony_ci		ev->cr.resp_ie = next;
8708c2ecf20Sopenharmony_ci		ev->cr.resp_ie_len = params->resp_ie_len;
8718c2ecf20Sopenharmony_ci		memcpy((void *)ev->cr.resp_ie, params->resp_ie,
8728c2ecf20Sopenharmony_ci		       params->resp_ie_len);
8738c2ecf20Sopenharmony_ci		next += params->resp_ie_len;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci	if (params->fils.kek_len) {
8768c2ecf20Sopenharmony_ci		ev->cr.fils.kek = next;
8778c2ecf20Sopenharmony_ci		ev->cr.fils.kek_len = params->fils.kek_len;
8788c2ecf20Sopenharmony_ci		memcpy((void *)ev->cr.fils.kek, params->fils.kek,
8798c2ecf20Sopenharmony_ci		       params->fils.kek_len);
8808c2ecf20Sopenharmony_ci		next += params->fils.kek_len;
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci	if (params->fils.pmk_len) {
8838c2ecf20Sopenharmony_ci		ev->cr.fils.pmk = next;
8848c2ecf20Sopenharmony_ci		ev->cr.fils.pmk_len = params->fils.pmk_len;
8858c2ecf20Sopenharmony_ci		memcpy((void *)ev->cr.fils.pmk, params->fils.pmk,
8868c2ecf20Sopenharmony_ci		       params->fils.pmk_len);
8878c2ecf20Sopenharmony_ci		next += params->fils.pmk_len;
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci	if (params->fils.pmkid) {
8908c2ecf20Sopenharmony_ci		ev->cr.fils.pmkid = next;
8918c2ecf20Sopenharmony_ci		memcpy((void *)ev->cr.fils.pmkid, params->fils.pmkid,
8928c2ecf20Sopenharmony_ci		       WLAN_PMKID_LEN);
8938c2ecf20Sopenharmony_ci		next += WLAN_PMKID_LEN;
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci	ev->cr.fils.update_erp_next_seq_num = params->fils.update_erp_next_seq_num;
8968c2ecf20Sopenharmony_ci	if (params->fils.update_erp_next_seq_num)
8978c2ecf20Sopenharmony_ci		ev->cr.fils.erp_next_seq_num = params->fils.erp_next_seq_num;
8988c2ecf20Sopenharmony_ci	if (params->bss)
8998c2ecf20Sopenharmony_ci		cfg80211_hold_bss(bss_from_pub(params->bss));
9008c2ecf20Sopenharmony_ci	ev->cr.bss = params->bss;
9018c2ecf20Sopenharmony_ci	ev->cr.status = params->status;
9028c2ecf20Sopenharmony_ci	ev->cr.timeout_reason = params->timeout_reason;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdev->event_lock, flags);
9058c2ecf20Sopenharmony_ci	list_add_tail(&ev->list, &wdev->event_list);
9068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdev->event_lock, flags);
9078c2ecf20Sopenharmony_ci	queue_work(cfg80211_wq, &rdev->event_work);
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfg80211_connect_done);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci/* Consumes bss object one way or another */
9128c2ecf20Sopenharmony_civoid __cfg80211_roamed(struct wireless_dev *wdev,
9138c2ecf20Sopenharmony_ci		       struct cfg80211_roam_info *info)
9148c2ecf20Sopenharmony_ci{
9158c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT
9168c2ecf20Sopenharmony_ci	union iwreq_data wrqu;
9178c2ecf20Sopenharmony_ci#endif
9188c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
9218c2ecf20Sopenharmony_ci		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
9228c2ecf20Sopenharmony_ci		goto out;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if (WARN_ON(!wdev->current_bss))
9258c2ecf20Sopenharmony_ci		goto out;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	cfg80211_unhold_bss(wdev->current_bss);
9288c2ecf20Sopenharmony_ci	cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
9298c2ecf20Sopenharmony_ci	wdev->current_bss = NULL;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	if (WARN_ON(!info->bss))
9328c2ecf20Sopenharmony_ci		return;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	cfg80211_hold_bss(bss_from_pub(info->bss));
9358c2ecf20Sopenharmony_ci	wdev->current_bss = bss_from_pub(info->bss);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	wdev->unprot_beacon_reported = 0;
9388c2ecf20Sopenharmony_ci	nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy),
9398c2ecf20Sopenharmony_ci			    wdev->netdev, info, GFP_KERNEL);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT
9428c2ecf20Sopenharmony_ci	if (info->req_ie) {
9438c2ecf20Sopenharmony_ci		memset(&wrqu, 0, sizeof(wrqu));
9448c2ecf20Sopenharmony_ci		wrqu.data.length = info->req_ie_len;
9458c2ecf20Sopenharmony_ci		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
9468c2ecf20Sopenharmony_ci				    &wrqu, info->req_ie);
9478c2ecf20Sopenharmony_ci	}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	if (info->resp_ie) {
9508c2ecf20Sopenharmony_ci		memset(&wrqu, 0, sizeof(wrqu));
9518c2ecf20Sopenharmony_ci		wrqu.data.length = info->resp_ie_len;
9528c2ecf20Sopenharmony_ci		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
9538c2ecf20Sopenharmony_ci				    &wrqu, info->resp_ie);
9548c2ecf20Sopenharmony_ci	}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	memset(&wrqu, 0, sizeof(wrqu));
9578c2ecf20Sopenharmony_ci	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
9588c2ecf20Sopenharmony_ci	memcpy(wrqu.ap_addr.sa_data, info->bss->bssid, ETH_ALEN);
9598c2ecf20Sopenharmony_ci	memcpy(wdev->wext.prev_bssid, info->bss->bssid, ETH_ALEN);
9608c2ecf20Sopenharmony_ci	wdev->wext.prev_bssid_valid = true;
9618c2ecf20Sopenharmony_ci	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
9628c2ecf20Sopenharmony_ci#endif
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	return;
9658c2ecf20Sopenharmony_ciout:
9668c2ecf20Sopenharmony_ci	cfg80211_put_bss(wdev->wiphy, info->bss);
9678c2ecf20Sopenharmony_ci}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci/* Consumes info->bss object one way or another */
9708c2ecf20Sopenharmony_civoid cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info,
9718c2ecf20Sopenharmony_ci		     gfp_t gfp)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
9748c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
9758c2ecf20Sopenharmony_ci	struct cfg80211_event *ev;
9768c2ecf20Sopenharmony_ci	unsigned long flags;
9778c2ecf20Sopenharmony_ci	u8 *next;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	if (!info->bss) {
9808c2ecf20Sopenharmony_ci		info->bss = cfg80211_get_bss(wdev->wiphy, info->channel,
9818c2ecf20Sopenharmony_ci					     info->bssid, wdev->ssid,
9828c2ecf20Sopenharmony_ci					     wdev->ssid_len,
9838c2ecf20Sopenharmony_ci					     wdev->conn_bss_type,
9848c2ecf20Sopenharmony_ci					     IEEE80211_PRIVACY_ANY);
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	if (WARN_ON(!info->bss))
9888c2ecf20Sopenharmony_ci		return;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len +
9918c2ecf20Sopenharmony_ci		     info->fils.kek_len + info->fils.pmk_len +
9928c2ecf20Sopenharmony_ci		     (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp);
9938c2ecf20Sopenharmony_ci	if (!ev) {
9948c2ecf20Sopenharmony_ci		cfg80211_put_bss(wdev->wiphy, info->bss);
9958c2ecf20Sopenharmony_ci		return;
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	ev->type = EVENT_ROAMED;
9998c2ecf20Sopenharmony_ci	next = ((u8 *)ev) + sizeof(*ev);
10008c2ecf20Sopenharmony_ci	if (info->req_ie_len) {
10018c2ecf20Sopenharmony_ci		ev->rm.req_ie = next;
10028c2ecf20Sopenharmony_ci		ev->rm.req_ie_len = info->req_ie_len;
10038c2ecf20Sopenharmony_ci		memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len);
10048c2ecf20Sopenharmony_ci		next += info->req_ie_len;
10058c2ecf20Sopenharmony_ci	}
10068c2ecf20Sopenharmony_ci	if (info->resp_ie_len) {
10078c2ecf20Sopenharmony_ci		ev->rm.resp_ie = next;
10088c2ecf20Sopenharmony_ci		ev->rm.resp_ie_len = info->resp_ie_len;
10098c2ecf20Sopenharmony_ci		memcpy((void *)ev->rm.resp_ie, info->resp_ie,
10108c2ecf20Sopenharmony_ci		       info->resp_ie_len);
10118c2ecf20Sopenharmony_ci		next += info->resp_ie_len;
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci	if (info->fils.kek_len) {
10148c2ecf20Sopenharmony_ci		ev->rm.fils.kek = next;
10158c2ecf20Sopenharmony_ci		ev->rm.fils.kek_len = info->fils.kek_len;
10168c2ecf20Sopenharmony_ci		memcpy((void *)ev->rm.fils.kek, info->fils.kek,
10178c2ecf20Sopenharmony_ci		       info->fils.kek_len);
10188c2ecf20Sopenharmony_ci		next += info->fils.kek_len;
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci	if (info->fils.pmk_len) {
10218c2ecf20Sopenharmony_ci		ev->rm.fils.pmk = next;
10228c2ecf20Sopenharmony_ci		ev->rm.fils.pmk_len = info->fils.pmk_len;
10238c2ecf20Sopenharmony_ci		memcpy((void *)ev->rm.fils.pmk, info->fils.pmk,
10248c2ecf20Sopenharmony_ci		       info->fils.pmk_len);
10258c2ecf20Sopenharmony_ci		next += info->fils.pmk_len;
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci	if (info->fils.pmkid) {
10288c2ecf20Sopenharmony_ci		ev->rm.fils.pmkid = next;
10298c2ecf20Sopenharmony_ci		memcpy((void *)ev->rm.fils.pmkid, info->fils.pmkid,
10308c2ecf20Sopenharmony_ci		       WLAN_PMKID_LEN);
10318c2ecf20Sopenharmony_ci		next += WLAN_PMKID_LEN;
10328c2ecf20Sopenharmony_ci	}
10338c2ecf20Sopenharmony_ci	ev->rm.fils.update_erp_next_seq_num = info->fils.update_erp_next_seq_num;
10348c2ecf20Sopenharmony_ci	if (info->fils.update_erp_next_seq_num)
10358c2ecf20Sopenharmony_ci		ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num;
10368c2ecf20Sopenharmony_ci	ev->rm.bss = info->bss;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdev->event_lock, flags);
10398c2ecf20Sopenharmony_ci	list_add_tail(&ev->list, &wdev->event_list);
10408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdev->event_lock, flags);
10418c2ecf20Sopenharmony_ci	queue_work(cfg80211_wq, &rdev->event_work);
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfg80211_roamed);
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_civoid __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
10508c2ecf20Sopenharmony_ci		return;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	if (WARN_ON(!wdev->current_bss) ||
10538c2ecf20Sopenharmony_ci	    WARN_ON(!ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
10548c2ecf20Sopenharmony_ci		return;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	nl80211_send_port_authorized(wiphy_to_rdev(wdev->wiphy), wdev->netdev,
10578c2ecf20Sopenharmony_ci				     bssid);
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_civoid cfg80211_port_authorized(struct net_device *dev, const u8 *bssid,
10618c2ecf20Sopenharmony_ci			      gfp_t gfp)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
10648c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
10658c2ecf20Sopenharmony_ci	struct cfg80211_event *ev;
10668c2ecf20Sopenharmony_ci	unsigned long flags;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	if (WARN_ON(!bssid))
10698c2ecf20Sopenharmony_ci		return;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	ev = kzalloc(sizeof(*ev), gfp);
10728c2ecf20Sopenharmony_ci	if (!ev)
10738c2ecf20Sopenharmony_ci		return;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	ev->type = EVENT_PORT_AUTHORIZED;
10768c2ecf20Sopenharmony_ci	memcpy(ev->pa.bssid, bssid, ETH_ALEN);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	/*
10798c2ecf20Sopenharmony_ci	 * Use the wdev event list so that if there are pending
10808c2ecf20Sopenharmony_ci	 * connected/roamed events, they will be reported first.
10818c2ecf20Sopenharmony_ci	 */
10828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdev->event_lock, flags);
10838c2ecf20Sopenharmony_ci	list_add_tail(&ev->list, &wdev->event_list);
10848c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdev->event_lock, flags);
10858c2ecf20Sopenharmony_ci	queue_work(cfg80211_wq, &rdev->event_work);
10868c2ecf20Sopenharmony_ci}
10878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfg80211_port_authorized);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_civoid __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
10908c2ecf20Sopenharmony_ci			     size_t ie_len, u16 reason, bool from_ap)
10918c2ecf20Sopenharmony_ci{
10928c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
10938c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
10948c2ecf20Sopenharmony_ci	int i;
10958c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT
10968c2ecf20Sopenharmony_ci	union iwreq_data wrqu;
10978c2ecf20Sopenharmony_ci#endif
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
11028c2ecf20Sopenharmony_ci		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
11038c2ecf20Sopenharmony_ci		return;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	if (wdev->current_bss) {
11068c2ecf20Sopenharmony_ci		cfg80211_unhold_bss(wdev->current_bss);
11078c2ecf20Sopenharmony_ci		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
11088c2ecf20Sopenharmony_ci	}
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	wdev->current_bss = NULL;
11118c2ecf20Sopenharmony_ci	wdev->ssid_len = 0;
11128c2ecf20Sopenharmony_ci	wdev->conn_owner_nlportid = 0;
11138c2ecf20Sopenharmony_ci	kfree_sensitive(wdev->connect_keys);
11148c2ecf20Sopenharmony_ci	wdev->connect_keys = NULL;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	/* stop critical protocol if supported */
11198c2ecf20Sopenharmony_ci	if (rdev->ops->crit_proto_stop && rdev->crit_proto_nlportid) {
11208c2ecf20Sopenharmony_ci		rdev->crit_proto_nlportid = 0;
11218c2ecf20Sopenharmony_ci		rdev_crit_proto_stop(rdev, wdev);
11228c2ecf20Sopenharmony_ci	}
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	/*
11258c2ecf20Sopenharmony_ci	 * Delete all the keys ... pairwise keys can't really
11268c2ecf20Sopenharmony_ci	 * exist any more anyway, but default keys might.
11278c2ecf20Sopenharmony_ci	 */
11288c2ecf20Sopenharmony_ci	if (rdev->ops->del_key) {
11298c2ecf20Sopenharmony_ci		int max_key_idx = 5;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci		if (wiphy_ext_feature_isset(
11328c2ecf20Sopenharmony_ci			    wdev->wiphy,
11338c2ecf20Sopenharmony_ci			    NL80211_EXT_FEATURE_BEACON_PROTECTION) ||
11348c2ecf20Sopenharmony_ci		    wiphy_ext_feature_isset(
11358c2ecf20Sopenharmony_ci			    wdev->wiphy,
11368c2ecf20Sopenharmony_ci			    NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
11378c2ecf20Sopenharmony_ci			max_key_idx = 7;
11388c2ecf20Sopenharmony_ci		for (i = 0; i <= max_key_idx; i++)
11398c2ecf20Sopenharmony_ci			rdev_del_key(rdev, dev, i, false, NULL);
11408c2ecf20Sopenharmony_ci	}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	rdev_set_qos_map(rdev, dev, NULL);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT
11458c2ecf20Sopenharmony_ci	memset(&wrqu, 0, sizeof(wrqu));
11468c2ecf20Sopenharmony_ci	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
11478c2ecf20Sopenharmony_ci	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
11488c2ecf20Sopenharmony_ci	wdev->wext.connect.ssid_len = 0;
11498c2ecf20Sopenharmony_ci#endif
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	schedule_work(&cfg80211_disconnect_work);
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_civoid cfg80211_disconnected(struct net_device *dev, u16 reason,
11558c2ecf20Sopenharmony_ci			   const u8 *ie, size_t ie_len,
11568c2ecf20Sopenharmony_ci			   bool locally_generated, gfp_t gfp)
11578c2ecf20Sopenharmony_ci{
11588c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
11598c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
11608c2ecf20Sopenharmony_ci	struct cfg80211_event *ev;
11618c2ecf20Sopenharmony_ci	unsigned long flags;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	ev = kzalloc(sizeof(*ev) + ie_len, gfp);
11648c2ecf20Sopenharmony_ci	if (!ev)
11658c2ecf20Sopenharmony_ci		return;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	ev->type = EVENT_DISCONNECTED;
11688c2ecf20Sopenharmony_ci	ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
11698c2ecf20Sopenharmony_ci	ev->dc.ie_len = ie_len;
11708c2ecf20Sopenharmony_ci	memcpy((void *)ev->dc.ie, ie, ie_len);
11718c2ecf20Sopenharmony_ci	ev->dc.reason = reason;
11728c2ecf20Sopenharmony_ci	ev->dc.locally_generated = locally_generated;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdev->event_lock, flags);
11758c2ecf20Sopenharmony_ci	list_add_tail(&ev->list, &wdev->event_list);
11768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdev->event_lock, flags);
11778c2ecf20Sopenharmony_ci	queue_work(cfg80211_wq, &rdev->event_work);
11788c2ecf20Sopenharmony_ci}
11798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfg80211_disconnected);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci/*
11828c2ecf20Sopenharmony_ci * API calls for nl80211/wext compatibility code
11838c2ecf20Sopenharmony_ci */
11848c2ecf20Sopenharmony_ciint cfg80211_connect(struct cfg80211_registered_device *rdev,
11858c2ecf20Sopenharmony_ci		     struct net_device *dev,
11868c2ecf20Sopenharmony_ci		     struct cfg80211_connect_params *connect,
11878c2ecf20Sopenharmony_ci		     struct cfg80211_cached_keys *connkeys,
11888c2ecf20Sopenharmony_ci		     const u8 *prev_bssid)
11898c2ecf20Sopenharmony_ci{
11908c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
11918c2ecf20Sopenharmony_ci	int err;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	/*
11968c2ecf20Sopenharmony_ci	 * If we have an ssid_len, we're trying to connect or are
11978c2ecf20Sopenharmony_ci	 * already connected, so reject a new SSID unless it's the
11988c2ecf20Sopenharmony_ci	 * same (which is the case for re-association.)
11998c2ecf20Sopenharmony_ci	 */
12008c2ecf20Sopenharmony_ci	if (wdev->ssid_len &&
12018c2ecf20Sopenharmony_ci	    (wdev->ssid_len != connect->ssid_len ||
12028c2ecf20Sopenharmony_ci	     memcmp(wdev->ssid, connect->ssid, wdev->ssid_len)))
12038c2ecf20Sopenharmony_ci		return -EALREADY;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	/*
12068c2ecf20Sopenharmony_ci	 * If connected, reject (re-)association unless prev_bssid
12078c2ecf20Sopenharmony_ci	 * matches the current BSSID.
12088c2ecf20Sopenharmony_ci	 */
12098c2ecf20Sopenharmony_ci	if (wdev->current_bss) {
12108c2ecf20Sopenharmony_ci		if (!prev_bssid)
12118c2ecf20Sopenharmony_ci			return -EALREADY;
12128c2ecf20Sopenharmony_ci		if (!ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid))
12138c2ecf20Sopenharmony_ci			return -ENOTCONN;
12148c2ecf20Sopenharmony_ci	}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	/*
12178c2ecf20Sopenharmony_ci	 * Reject if we're in the process of connecting with WEP,
12188c2ecf20Sopenharmony_ci	 * this case isn't very interesting and trying to handle
12198c2ecf20Sopenharmony_ci	 * it would make the code much more complex.
12208c2ecf20Sopenharmony_ci	 */
12218c2ecf20Sopenharmony_ci	if (wdev->connect_keys)
12228c2ecf20Sopenharmony_ci		return -EINPROGRESS;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
12258c2ecf20Sopenharmony_ci				  rdev->wiphy.ht_capa_mod_mask);
12268c2ecf20Sopenharmony_ci	cfg80211_oper_and_vht_capa(&connect->vht_capa_mask,
12278c2ecf20Sopenharmony_ci				   rdev->wiphy.vht_capa_mod_mask);
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	if (connkeys && connkeys->def >= 0) {
12308c2ecf20Sopenharmony_ci		int idx;
12318c2ecf20Sopenharmony_ci		u32 cipher;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci		idx = connkeys->def;
12348c2ecf20Sopenharmony_ci		cipher = connkeys->params[idx].cipher;
12358c2ecf20Sopenharmony_ci		/* If given a WEP key we may need it for shared key auth */
12368c2ecf20Sopenharmony_ci		if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
12378c2ecf20Sopenharmony_ci		    cipher == WLAN_CIPHER_SUITE_WEP104) {
12388c2ecf20Sopenharmony_ci			connect->key_idx = idx;
12398c2ecf20Sopenharmony_ci			connect->key = connkeys->params[idx].key;
12408c2ecf20Sopenharmony_ci			connect->key_len = connkeys->params[idx].key_len;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci			/*
12438c2ecf20Sopenharmony_ci			 * If ciphers are not set (e.g. when going through
12448c2ecf20Sopenharmony_ci			 * iwconfig), we have to set them appropriately here.
12458c2ecf20Sopenharmony_ci			 */
12468c2ecf20Sopenharmony_ci			if (connect->crypto.cipher_group == 0)
12478c2ecf20Sopenharmony_ci				connect->crypto.cipher_group = cipher;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci			if (connect->crypto.n_ciphers_pairwise == 0) {
12508c2ecf20Sopenharmony_ci				connect->crypto.n_ciphers_pairwise = 1;
12518c2ecf20Sopenharmony_ci				connect->crypto.ciphers_pairwise[0] = cipher;
12528c2ecf20Sopenharmony_ci			}
12538c2ecf20Sopenharmony_ci		}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci		connect->crypto.wep_keys = connkeys->params;
12568c2ecf20Sopenharmony_ci		connect->crypto.wep_tx_key = connkeys->def;
12578c2ecf20Sopenharmony_ci	} else {
12588c2ecf20Sopenharmony_ci		if (WARN_ON(connkeys))
12598c2ecf20Sopenharmony_ci			return -EINVAL;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci		/* connect can point to wdev->wext.connect which
12628c2ecf20Sopenharmony_ci		 * can hold key data from a previous connection
12638c2ecf20Sopenharmony_ci		 */
12648c2ecf20Sopenharmony_ci		connect->key = NULL;
12658c2ecf20Sopenharmony_ci		connect->key_len = 0;
12668c2ecf20Sopenharmony_ci		connect->key_idx = 0;
12678c2ecf20Sopenharmony_ci	}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	wdev->connect_keys = connkeys;
12708c2ecf20Sopenharmony_ci	memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
12718c2ecf20Sopenharmony_ci	wdev->ssid_len = connect->ssid_len;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	wdev->conn_bss_type = connect->pbss ? IEEE80211_BSS_TYPE_PBSS :
12748c2ecf20Sopenharmony_ci					      IEEE80211_BSS_TYPE_ESS;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	if (!rdev->ops->connect)
12778c2ecf20Sopenharmony_ci		err = cfg80211_sme_connect(wdev, connect, prev_bssid);
12788c2ecf20Sopenharmony_ci	else
12798c2ecf20Sopenharmony_ci		err = rdev_connect(rdev, dev, connect);
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	if (err) {
12828c2ecf20Sopenharmony_ci		wdev->connect_keys = NULL;
12838c2ecf20Sopenharmony_ci		/*
12848c2ecf20Sopenharmony_ci		 * This could be reassoc getting refused, don't clear
12858c2ecf20Sopenharmony_ci		 * ssid_len in that case.
12868c2ecf20Sopenharmony_ci		 */
12878c2ecf20Sopenharmony_ci		if (!wdev->current_bss)
12888c2ecf20Sopenharmony_ci			wdev->ssid_len = 0;
12898c2ecf20Sopenharmony_ci		return err;
12908c2ecf20Sopenharmony_ci	}
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	return 0;
12938c2ecf20Sopenharmony_ci}
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ciint cfg80211_disconnect(struct cfg80211_registered_device *rdev,
12968c2ecf20Sopenharmony_ci			struct net_device *dev, u16 reason, bool wextev)
12978c2ecf20Sopenharmony_ci{
12988c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
12998c2ecf20Sopenharmony_ci	int err = 0;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	kfree_sensitive(wdev->connect_keys);
13048c2ecf20Sopenharmony_ci	wdev->connect_keys = NULL;
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	wdev->conn_owner_nlportid = 0;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	if (wdev->conn)
13098c2ecf20Sopenharmony_ci		err = cfg80211_sme_disconnect(wdev, reason);
13108c2ecf20Sopenharmony_ci	else if (!rdev->ops->disconnect)
13118c2ecf20Sopenharmony_ci		cfg80211_mlme_down(rdev, dev);
13128c2ecf20Sopenharmony_ci	else if (wdev->ssid_len)
13138c2ecf20Sopenharmony_ci		err = rdev_disconnect(rdev, dev, reason);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	/*
13168c2ecf20Sopenharmony_ci	 * Clear ssid_len unless we actually were fully connected,
13178c2ecf20Sopenharmony_ci	 * in which case cfg80211_disconnected() will take care of
13188c2ecf20Sopenharmony_ci	 * this later.
13198c2ecf20Sopenharmony_ci	 */
13208c2ecf20Sopenharmony_ci	if (!wdev->current_bss)
13218c2ecf20Sopenharmony_ci		wdev->ssid_len = 0;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	return err;
13248c2ecf20Sopenharmony_ci}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci/*
13278c2ecf20Sopenharmony_ci * Used to clean up after the connection / connection attempt owner socket
13288c2ecf20Sopenharmony_ci * disconnects
13298c2ecf20Sopenharmony_ci */
13308c2ecf20Sopenharmony_civoid cfg80211_autodisconnect_wk(struct work_struct *work)
13318c2ecf20Sopenharmony_ci{
13328c2ecf20Sopenharmony_ci	struct wireless_dev *wdev =
13338c2ecf20Sopenharmony_ci		container_of(work, struct wireless_dev, disconnect_wk);
13348c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	wdev_lock(wdev);
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	if (wdev->conn_owner_nlportid) {
13398c2ecf20Sopenharmony_ci		switch (wdev->iftype) {
13408c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_ADHOC:
13418c2ecf20Sopenharmony_ci			__cfg80211_leave_ibss(rdev, wdev->netdev, false);
13428c2ecf20Sopenharmony_ci			break;
13438c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_AP:
13448c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_P2P_GO:
13458c2ecf20Sopenharmony_ci			__cfg80211_stop_ap(rdev, wdev->netdev, false);
13468c2ecf20Sopenharmony_ci			break;
13478c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_MESH_POINT:
13488c2ecf20Sopenharmony_ci			__cfg80211_leave_mesh(rdev, wdev->netdev);
13498c2ecf20Sopenharmony_ci			break;
13508c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_STATION:
13518c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_P2P_CLIENT:
13528c2ecf20Sopenharmony_ci			/*
13538c2ecf20Sopenharmony_ci			 * Use disconnect_bssid if still connecting and
13548c2ecf20Sopenharmony_ci			 * ops->disconnect not implemented.  Otherwise we can
13558c2ecf20Sopenharmony_ci			 * use cfg80211_disconnect.
13568c2ecf20Sopenharmony_ci			 */
13578c2ecf20Sopenharmony_ci			if (rdev->ops->disconnect || wdev->current_bss)
13588c2ecf20Sopenharmony_ci				cfg80211_disconnect(rdev, wdev->netdev,
13598c2ecf20Sopenharmony_ci						    WLAN_REASON_DEAUTH_LEAVING,
13608c2ecf20Sopenharmony_ci						    true);
13618c2ecf20Sopenharmony_ci			else
13628c2ecf20Sopenharmony_ci				cfg80211_mlme_deauth(rdev, wdev->netdev,
13638c2ecf20Sopenharmony_ci						     wdev->disconnect_bssid,
13648c2ecf20Sopenharmony_ci						     NULL, 0,
13658c2ecf20Sopenharmony_ci						     WLAN_REASON_DEAUTH_LEAVING,
13668c2ecf20Sopenharmony_ci						     false);
13678c2ecf20Sopenharmony_ci			break;
13688c2ecf20Sopenharmony_ci		default:
13698c2ecf20Sopenharmony_ci			break;
13708c2ecf20Sopenharmony_ci		}
13718c2ecf20Sopenharmony_ci	}
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	wdev_unlock(wdev);
13748c2ecf20Sopenharmony_ci}
1375