xref: /openbmc/linux/net/wireless/core.c (revision 36db6e8484ed455bbb320d89a119378897ae991c)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2704232c2SJohannes Berg /*
3704232c2SJohannes Berg  * This is the linux wireless configuration interface.
4704232c2SJohannes Berg  *
55f2aa25eSJohannes Berg  * Copyright 2006-2010		Johannes Berg <johannes@sipsolutions.net>
62740f0cfSJohannes Berg  * Copyright 2013-2014  Intel Mobile Communications GmbH
7c4cbaf79SLuca Coelho  * Copyright 2015-2017	Intel Deutschland GmbH
8f0a6fd15SJohannes Berg  * Copyright (C) 2018-2022 Intel Corporation
9704232c2SJohannes Berg  */
10704232c2SJohannes Berg 
11e9c0268fSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12e9c0268fSJoe Perches 
13704232c2SJohannes Berg #include <linux/if.h>
14704232c2SJohannes Berg #include <linux/module.h>
15704232c2SJohannes Berg #include <linux/err.h>
16704232c2SJohannes Berg #include <linux/list.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
18704232c2SJohannes Berg #include <linux/nl80211.h>
19704232c2SJohannes Berg #include <linux/debugfs.h>
20704232c2SJohannes Berg #include <linux/notifier.h>
21704232c2SJohannes Berg #include <linux/device.h>
2216a832e7SZhu Yi #include <linux/etherdevice.h>
231f87f7d3SJohannes Berg #include <linux/rtnetlink.h>
24d43c36dcSAlexey Dobriyan #include <linux/sched.h>
25704232c2SJohannes Berg #include <net/genetlink.h>
26704232c2SJohannes Berg #include <net/cfg80211.h>
2755682965SJohannes Berg #include "nl80211.h"
28704232c2SJohannes Berg #include "core.h"
29704232c2SJohannes Berg #include "sysfs.h"
301ac61302SLuis R. Rodriguez #include "debugfs.h"
31a9a11622SJohannes Berg #include "wext-compat.h"
32e35e4d28SHila Gonen #include "rdev-ops.h"
33704232c2SJohannes Berg 
34704232c2SJohannes Berg /* name for sysfs, %d is appended */
35704232c2SJohannes Berg #define PHY_NAME "phy"
36704232c2SJohannes Berg 
37704232c2SJohannes Berg MODULE_AUTHOR("Johannes Berg");
38704232c2SJohannes Berg MODULE_LICENSE("GPL");
39704232c2SJohannes Berg MODULE_DESCRIPTION("wireless configuration support");
40fb4e1568SMarcel Holtmann MODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME);
41704232c2SJohannes Berg 
425fe231e8SJohannes Berg /* RCU-protected (and RTNL for writers) */
4379c97e97SJohannes Berg LIST_HEAD(cfg80211_rdev_list);
44f5ea9120SJohannes Berg int cfg80211_rdev_list_generation;
45a1794390SLuis R. Rodriguez 
46704232c2SJohannes Berg /* for debugfs */
47704232c2SJohannes Berg static struct dentry *ieee80211_debugfs_dir;
48704232c2SJohannes Berg 
49e60d7443SAlban Browaeys /* for the cleanup, scan and event works */
50e60d7443SAlban Browaeys struct workqueue_struct *cfg80211_wq;
51e60d7443SAlban Browaeys 
5240db6c77SAmitkumar Karwar static bool cfg80211_disable_40mhz_24ghz;
5340db6c77SAmitkumar Karwar module_param(cfg80211_disable_40mhz_24ghz, bool, 0644);
5440db6c77SAmitkumar Karwar MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz,
5540db6c77SAmitkumar Karwar 		 "Disable 40MHz support in the 2.4GHz band");
5640db6c77SAmitkumar Karwar 
cfg80211_rdev_by_wiphy_idx(int wiphy_idx)5779c97e97SJohannes Berg struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
5855682965SJohannes Berg {
5979c97e97SJohannes Berg 	struct cfg80211_registered_device *result = NULL, *rdev;
6055682965SJohannes Berg 
615fe231e8SJohannes Berg 	ASSERT_RTNL();
62761cf7ecSLuis R. Rodriguez 
6379c97e97SJohannes Berg 	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
6479c97e97SJohannes Berg 		if (rdev->wiphy_idx == wiphy_idx) {
6579c97e97SJohannes Berg 			result = rdev;
6655682965SJohannes Berg 			break;
6755682965SJohannes Berg 		}
6855682965SJohannes Berg 	}
6955682965SJohannes Berg 
7055682965SJohannes Berg 	return result;
7155682965SJohannes Berg }
7255682965SJohannes Berg 
get_wiphy_idx(struct wiphy * wiphy)73806a9e39SLuis R. Rodriguez int get_wiphy_idx(struct wiphy *wiphy)
74806a9e39SLuis R. Rodriguez {
75f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
76f4173766SJohannes Berg 
7779c97e97SJohannes Berg 	return rdev->wiphy_idx;
78806a9e39SLuis R. Rodriguez }
79806a9e39SLuis R. Rodriguez 
wiphy_idx_to_wiphy(int wiphy_idx)80806a9e39SLuis R. Rodriguez struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
81806a9e39SLuis R. Rodriguez {
8279c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev;
83806a9e39SLuis R. Rodriguez 
845fe231e8SJohannes Berg 	ASSERT_RTNL();
85806a9e39SLuis R. Rodriguez 
8679c97e97SJohannes Berg 	rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx);
8779c97e97SJohannes Berg 	if (!rdev)
88806a9e39SLuis R. Rodriguez 		return NULL;
8979c97e97SJohannes Berg 	return &rdev->wiphy;
90806a9e39SLuis R. Rodriguez }
91806a9e39SLuis R. Rodriguez 
cfg80211_dev_check_name(struct cfg80211_registered_device * rdev,const char * newname)921998d90aSBen Greear static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev,
931998d90aSBen Greear 				   const char *newname)
9455682965SJohannes Berg {
9579c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev2;
961998d90aSBen Greear 	int wiphy_idx, taken = -1, digits;
9755682965SJohannes Berg 
985fe231e8SJohannes Berg 	ASSERT_RTNL();
992940bb69SEric W. Biederman 
100a7cfebcbSJohannes Berg 	if (strlen(newname) > NL80211_WIPHY_NAME_MAXLEN)
101a7cfebcbSJohannes Berg 		return -EINVAL;
102a7cfebcbSJohannes Berg 
1037623225fSJohannes Berg 	/* prohibit calling the thing phy%d when %d is not its number */
1047623225fSJohannes Berg 	sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
1057623225fSJohannes Berg 	if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) {
1067623225fSJohannes Berg 		/* count number of places needed to print wiphy_idx */
1077623225fSJohannes Berg 		digits = 1;
1087623225fSJohannes Berg 		while (wiphy_idx /= 10)
1097623225fSJohannes Berg 			digits++;
1107623225fSJohannes Berg 		/*
1117623225fSJohannes Berg 		 * deny the name if it is phy<idx> where <idx> is printed
1127623225fSJohannes Berg 		 * without leading zeroes. taken == strlen(newname) here
1137623225fSJohannes Berg 		 */
1147623225fSJohannes Berg 		if (taken == strlen(PHY_NAME) + digits)
1157623225fSJohannes Berg 			return -EINVAL;
1167623225fSJohannes Berg 	}
1177623225fSJohannes Berg 
1182940bb69SEric W. Biederman 	/* Ensure another device does not already have this name. */
11979c97e97SJohannes Berg 	list_for_each_entry(rdev2, &cfg80211_rdev_list, list)
1201998d90aSBen Greear 		if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0)
1217623225fSJohannes Berg 			return -EINVAL;
1222940bb69SEric W. Biederman 
1231998d90aSBen Greear 	return 0;
1241998d90aSBen Greear }
1251998d90aSBen Greear 
cfg80211_dev_rename(struct cfg80211_registered_device * rdev,char * newname)1261998d90aSBen Greear int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
1271998d90aSBen Greear 			char *newname)
1281998d90aSBen Greear {
1291998d90aSBen Greear 	int result;
1301998d90aSBen Greear 
1311998d90aSBen Greear 	ASSERT_RTNL();
1324d45145bSJohannes Berg 	lockdep_assert_wiphy(&rdev->wiphy);
1331998d90aSBen Greear 
1341998d90aSBen Greear 	/* Ignore nop renames */
1351998d90aSBen Greear 	if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0)
1361998d90aSBen Greear 		return 0;
1371998d90aSBen Greear 
1381998d90aSBen Greear 	result = cfg80211_dev_check_name(rdev, newname);
1391998d90aSBen Greear 	if (result < 0)
1401998d90aSBen Greear 		return result;
1411998d90aSBen Greear 
14255682965SJohannes Berg 	result = device_rename(&rdev->wiphy.dev, newname);
14355682965SJohannes Berg 	if (result)
1444bbf4d56SJohannes Berg 		return result;
14555682965SJohannes Berg 
1460bbab5f0SJohannes Berg 	if (!IS_ERR_OR_NULL(rdev->wiphy.debugfsdir))
147d82574a8SGreg Kroah-Hartman 		debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
14855682965SJohannes Berg 			       rdev->wiphy.debugfsdir,
149d82574a8SGreg Kroah-Hartman 			       rdev->wiphy.debugfsdir->d_parent, newname);
15055682965SJohannes Berg 
1513bb20556SJohannes Berg 	nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
15255682965SJohannes Berg 
1534bbf4d56SJohannes Berg 	return 0;
15455682965SJohannes Berg }
15555682965SJohannes Berg 
cfg80211_switch_netns(struct cfg80211_registered_device * rdev,struct net * net)156463d0183SJohannes Berg int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
157463d0183SJohannes Berg 			  struct net *net)
158463d0183SJohannes Berg {
159463d0183SJohannes Berg 	struct wireless_dev *wdev;
160463d0183SJohannes Berg 	int err = 0;
161463d0183SJohannes Berg 
1625be83de5SJohannes Berg 	if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK))
163463d0183SJohannes Berg 		return -EOPNOTSUPP;
164463d0183SJohannes Berg 
16553873f13SJohannes Berg 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
166ba22fb5bSJohannes Berg 		if (!wdev->netdev)
167ba22fb5bSJohannes Berg 			continue;
168463d0183SJohannes Berg 		wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
169463d0183SJohannes Berg 		err = dev_change_net_namespace(wdev->netdev, net, "wlan%d");
170463d0183SJohannes Berg 		if (err)
171463d0183SJohannes Berg 			break;
172463d0183SJohannes Berg 		wdev->netdev->features |= NETIF_F_NETNS_LOCAL;
173463d0183SJohannes Berg 	}
174463d0183SJohannes Berg 
175463d0183SJohannes Berg 	if (err) {
176463d0183SJohannes Berg 		/* failed -- clean up to old netns */
177463d0183SJohannes Berg 		net = wiphy_net(&rdev->wiphy);
178463d0183SJohannes Berg 
17953873f13SJohannes Berg 		list_for_each_entry_continue_reverse(wdev,
18053873f13SJohannes Berg 						     &rdev->wiphy.wdev_list,
181463d0183SJohannes Berg 						     list) {
182ba22fb5bSJohannes Berg 			if (!wdev->netdev)
183ba22fb5bSJohannes Berg 				continue;
184463d0183SJohannes Berg 			wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
185463d0183SJohannes Berg 			err = dev_change_net_namespace(wdev->netdev, net,
186463d0183SJohannes Berg 							"wlan%d");
187463d0183SJohannes Berg 			WARN_ON(err);
188463d0183SJohannes Berg 			wdev->netdev->features |= NETIF_F_NETNS_LOCAL;
189463d0183SJohannes Berg 		}
19004600794SJohannes Berg 
19104600794SJohannes Berg 		return err;
192463d0183SJohannes Berg 	}
193463d0183SJohannes Berg 
194c90b670bSMartin Willi 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
195c90b670bSMartin Willi 		if (!wdev->netdev)
196c90b670bSMartin Willi 			continue;
197c90b670bSMartin Willi 		nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
198c90b670bSMartin Willi 	}
1994d45145bSJohannes Berg 
2004d45145bSJohannes Berg 	wiphy_lock(&rdev->wiphy);
201c90b670bSMartin Willi 	nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
202c90b670bSMartin Willi 
203463d0183SJohannes Berg 	wiphy_net_set(&rdev->wiphy, net);
204463d0183SJohannes Berg 
20504600794SJohannes Berg 	err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev));
20604600794SJohannes Berg 	WARN_ON(err);
20704600794SJohannes Berg 
208c90b670bSMartin Willi 	nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
2094d45145bSJohannes Berg 	wiphy_unlock(&rdev->wiphy);
2104d45145bSJohannes Berg 
211c90b670bSMartin Willi 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
212c90b670bSMartin Willi 		if (!wdev->netdev)
213c90b670bSMartin Willi 			continue;
214c90b670bSMartin Willi 		nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
215c90b670bSMartin Willi 	}
216c90b670bSMartin Willi 
21704600794SJohannes Berg 	return 0;
218463d0183SJohannes Berg }
219463d0183SJohannes Berg 
cfg80211_rfkill_poll(struct rfkill * rfkill,void * data)2201f87f7d3SJohannes Berg static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
2211f87f7d3SJohannes Berg {
22279c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = data;
2231f87f7d3SJohannes Berg 
22435a59d34SJohannes Berg 	wiphy_lock(&rdev->wiphy);
225e35e4d28SHila Gonen 	rdev_rfkill_poll(rdev);
22635a59d34SJohannes Berg 	wiphy_unlock(&rdev->wiphy);
2271f87f7d3SJohannes Berg }
2281f87f7d3SJohannes Berg 
cfg80211_stop_p2p_device(struct cfg80211_registered_device * rdev,struct wireless_dev * wdev)229f9f47529SJohannes Berg void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
230f9f47529SJohannes Berg 			      struct wireless_dev *wdev)
231f9f47529SJohannes Berg {
232a05829a7SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
233f9f47529SJohannes Berg 
234f9f47529SJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE))
235f9f47529SJohannes Berg 		return;
236f9f47529SJohannes Berg 
23773c7da3dSArend Van Spriel 	if (!wdev_running(wdev))
238f9f47529SJohannes Berg 		return;
239f9f47529SJohannes Berg 
240f9f47529SJohannes Berg 	rdev_stop_p2p_device(rdev, wdev);
24173c7da3dSArend Van Spriel 	wdev->is_running = false;
242f9f47529SJohannes Berg 
243f9f47529SJohannes Berg 	rdev->opencount--;
244f9f47529SJohannes Berg 
245a617302cSJohannes Berg 	if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
246c8cb5b85STova Mussai 		if (WARN_ON(!rdev->scan_req->notified &&
247c8cb5b85STova Mussai 			    (!rdev->int_scan_req ||
248c8cb5b85STova Mussai 			     !rdev->int_scan_req->notified)))
2491d76250bSAvraham Stern 			rdev->scan_req->info.aborted = true;
250f9d15d16SJohannes Berg 		___cfg80211_scan_done(rdev, false);
251a617302cSJohannes Berg 	}
252f9f47529SJohannes Berg }
253f9f47529SJohannes Berg 
cfg80211_stop_nan(struct cfg80211_registered_device * rdev,struct wireless_dev * wdev)254cb3b7d87SAyala Beker void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
255cb3b7d87SAyala Beker 		       struct wireless_dev *wdev)
256cb3b7d87SAyala Beker {
257a05829a7SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
258cb3b7d87SAyala Beker 
259cb3b7d87SAyala Beker 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN))
260cb3b7d87SAyala Beker 		return;
261cb3b7d87SAyala Beker 
26273c7da3dSArend Van Spriel 	if (!wdev_running(wdev))
263cb3b7d87SAyala Beker 		return;
264cb3b7d87SAyala Beker 
265cb3b7d87SAyala Beker 	rdev_stop_nan(rdev, wdev);
26673c7da3dSArend Van Spriel 	wdev->is_running = false;
267cb3b7d87SAyala Beker 
268cb3b7d87SAyala Beker 	rdev->opencount--;
269cb3b7d87SAyala Beker }
270cb3b7d87SAyala Beker 
cfg80211_shutdown_all_interfaces(struct wiphy * wiphy)271f6837ba8SJohannes Berg void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
2721f87f7d3SJohannes Berg {
273f6837ba8SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
2741f87f7d3SJohannes Berg 	struct wireless_dev *wdev;
2751f87f7d3SJohannes Berg 
276f6837ba8SJohannes Berg 	ASSERT_RTNL();
277f9f47529SJohannes Berg 
27853873f13SJohannes Berg 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
27998104fdeSJohannes Berg 		if (wdev->netdev) {
2801f87f7d3SJohannes Berg 			dev_close(wdev->netdev);
28198104fdeSJohannes Berg 			continue;
28298104fdeSJohannes Berg 		}
283a05829a7SJohannes Berg 
28498104fdeSJohannes Berg 		/* otherwise, check iftype */
285a05829a7SJohannes Berg 
286a05829a7SJohannes Berg 		wiphy_lock(wiphy);
287a05829a7SJohannes Berg 
28898104fdeSJohannes Berg 		switch (wdev->iftype) {
28998104fdeSJohannes Berg 		case NL80211_IFTYPE_P2P_DEVICE:
290f9f47529SJohannes Berg 			cfg80211_stop_p2p_device(rdev, wdev);
29198104fdeSJohannes Berg 			break;
292cb3b7d87SAyala Beker 		case NL80211_IFTYPE_NAN:
293cb3b7d87SAyala Beker 			cfg80211_stop_nan(rdev, wdev);
294cb3b7d87SAyala Beker 			break;
29598104fdeSJohannes Berg 		default:
29698104fdeSJohannes Berg 			break;
29798104fdeSJohannes Berg 		}
298a05829a7SJohannes Berg 
299a05829a7SJohannes Berg 		wiphy_unlock(wiphy);
30098104fdeSJohannes Berg 	}
301f6837ba8SJohannes Berg }
302f6837ba8SJohannes Berg EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces);
3031f87f7d3SJohannes Berg 
cfg80211_rfkill_set_block(void * data,bool blocked)304f6837ba8SJohannes Berg static int cfg80211_rfkill_set_block(void *data, bool blocked)
305f6837ba8SJohannes Berg {
306f6837ba8SJohannes Berg 	struct cfg80211_registered_device *rdev = data;
307f6837ba8SJohannes Berg 
308f6837ba8SJohannes Berg 	if (!blocked)
309f6837ba8SJohannes Berg 		return 0;
310f6837ba8SJohannes Berg 
311f6837ba8SJohannes Berg 	rtnl_lock();
312f6837ba8SJohannes Berg 	cfg80211_shutdown_all_interfaces(&rdev->wiphy);
3131f87f7d3SJohannes Berg 	rtnl_unlock();
3141f87f7d3SJohannes Berg 
3151f87f7d3SJohannes Berg 	return 0;
3161f87f7d3SJohannes Berg }
3171f87f7d3SJohannes Berg 
cfg80211_rfkill_block_work(struct work_struct * work)3183cfe91c4SJohannes Berg static void cfg80211_rfkill_block_work(struct work_struct *work)
3191f87f7d3SJohannes Berg {
32079c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev;
3211f87f7d3SJohannes Berg 
3223cfe91c4SJohannes Berg 	rdev = container_of(work, struct cfg80211_registered_device,
3233cfe91c4SJohannes Berg 			    rfkill_block);
3243cfe91c4SJohannes Berg 	cfg80211_rfkill_set_block(rdev, true);
3251f87f7d3SJohannes Berg }
3261f87f7d3SJohannes Berg 
cfg80211_event_work(struct work_struct * work)327667503ddSJohannes Berg static void cfg80211_event_work(struct work_struct *work)
328667503ddSJohannes Berg {
329667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev;
330667503ddSJohannes Berg 
331667503ddSJohannes Berg 	rdev = container_of(work, struct cfg80211_registered_device,
332667503ddSJohannes Berg 			    event_work);
333667503ddSJohannes Berg 
334a05829a7SJohannes Berg 	wiphy_lock(&rdev->wiphy);
3353d54d255SJohannes Berg 	cfg80211_process_rdev_events(rdev);
336a05829a7SJohannes Berg 	wiphy_unlock(&rdev->wiphy);
337667503ddSJohannes Berg }
338667503ddSJohannes Berg 
cfg80211_destroy_ifaces(struct cfg80211_registered_device * rdev)33978f22b6aSJohannes Berg void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
34078f22b6aSJohannes Berg {
341ab81007aSJohannes Berg 	struct wireless_dev *wdev, *tmp;
34278f22b6aSJohannes Berg 
34378f22b6aSJohannes Berg 	ASSERT_RTNL();
34478f22b6aSJohannes Berg 
345f0a6fd15SJohannes Berg 	list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) {
346ea6b2098SJohannes Berg 		if (wdev->nl_owner_dead) {
347ea6b2098SJohannes Berg 			if (wdev->netdev)
348ea6b2098SJohannes Berg 				dev_close(wdev->netdev);
349ea6b2098SJohannes Berg 
350ea6b2098SJohannes Berg 			wiphy_lock(&rdev->wiphy);
351ea6b2098SJohannes Berg 			cfg80211_leave(rdev, wdev);
352cdf0a0a8SJohannes Berg 			cfg80211_remove_virtual_intf(rdev, wdev);
353ea6b2098SJohannes Berg 			wiphy_unlock(&rdev->wiphy);
354ea6b2098SJohannes Berg 		}
355f0a6fd15SJohannes Berg 	}
356f0a6fd15SJohannes Berg }
35778f22b6aSJohannes Berg 
cfg80211_destroy_iface_wk(struct work_struct * work)35878f22b6aSJohannes Berg static void cfg80211_destroy_iface_wk(struct work_struct *work)
35978f22b6aSJohannes Berg {
36078f22b6aSJohannes Berg 	struct cfg80211_registered_device *rdev;
36178f22b6aSJohannes Berg 
36278f22b6aSJohannes Berg 	rdev = container_of(work, struct cfg80211_registered_device,
36378f22b6aSJohannes Berg 			    destroy_work);
36478f22b6aSJohannes Berg 
36578f22b6aSJohannes Berg 	rtnl_lock();
36678f22b6aSJohannes Berg 	cfg80211_destroy_ifaces(rdev);
36778f22b6aSJohannes Berg 	rtnl_unlock();
36878f22b6aSJohannes Berg }
36978f22b6aSJohannes Berg 
cfg80211_sched_scan_stop_wk(struct wiphy * wiphy,struct wiphy_work * work)370c88d7178SJohannes Berg static void cfg80211_sched_scan_stop_wk(struct wiphy *wiphy,
371c88d7178SJohannes Berg 					struct wiphy_work *work)
37293a1e86cSJukka Rissanen {
37393a1e86cSJukka Rissanen 	struct cfg80211_registered_device *rdev;
374ca986ad9SArend Van Spriel 	struct cfg80211_sched_scan_request *req, *tmp;
37593a1e86cSJukka Rissanen 
37693a1e86cSJukka Rissanen 	rdev = container_of(work, struct cfg80211_registered_device,
37793a1e86cSJukka Rissanen 			   sched_scan_stop_wk);
37893a1e86cSJukka Rissanen 
379ca986ad9SArend Van Spriel 	list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) {
380ca986ad9SArend Van Spriel 		if (req->nl_owner_dead)
381ca986ad9SArend Van Spriel 			cfg80211_stop_sched_scan_req(rdev, req, false);
382ca986ad9SArend Van Spriel 	}
38393a1e86cSJukka Rissanen }
38493a1e86cSJukka Rissanen 
cfg80211_propagate_radar_detect_wk(struct work_struct * work)38589766727SVasanthakumar Thiagarajan static void cfg80211_propagate_radar_detect_wk(struct work_struct *work)
38689766727SVasanthakumar Thiagarajan {
38789766727SVasanthakumar Thiagarajan 	struct cfg80211_registered_device *rdev;
38889766727SVasanthakumar Thiagarajan 
38989766727SVasanthakumar Thiagarajan 	rdev = container_of(work, struct cfg80211_registered_device,
39089766727SVasanthakumar Thiagarajan 			    propagate_radar_detect_wk);
39189766727SVasanthakumar Thiagarajan 
39289766727SVasanthakumar Thiagarajan 	rtnl_lock();
39389766727SVasanthakumar Thiagarajan 
39489766727SVasanthakumar Thiagarajan 	regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->radar_chandef,
39589766727SVasanthakumar Thiagarajan 				       NL80211_DFS_UNAVAILABLE,
39689766727SVasanthakumar Thiagarajan 				       NL80211_RADAR_DETECTED);
39789766727SVasanthakumar Thiagarajan 
39889766727SVasanthakumar Thiagarajan 	rtnl_unlock();
39989766727SVasanthakumar Thiagarajan }
40089766727SVasanthakumar Thiagarajan 
cfg80211_propagate_cac_done_wk(struct work_struct * work)40189766727SVasanthakumar Thiagarajan static void cfg80211_propagate_cac_done_wk(struct work_struct *work)
40289766727SVasanthakumar Thiagarajan {
40389766727SVasanthakumar Thiagarajan 	struct cfg80211_registered_device *rdev;
40489766727SVasanthakumar Thiagarajan 
40589766727SVasanthakumar Thiagarajan 	rdev = container_of(work, struct cfg80211_registered_device,
40689766727SVasanthakumar Thiagarajan 			    propagate_cac_done_wk);
40789766727SVasanthakumar Thiagarajan 
40889766727SVasanthakumar Thiagarajan 	rtnl_lock();
40989766727SVasanthakumar Thiagarajan 
41089766727SVasanthakumar Thiagarajan 	regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->cac_done_chandef,
41189766727SVasanthakumar Thiagarajan 				       NL80211_DFS_AVAILABLE,
41289766727SVasanthakumar Thiagarajan 				       NL80211_RADAR_CAC_FINISHED);
41389766727SVasanthakumar Thiagarajan 
41489766727SVasanthakumar Thiagarajan 	rtnl_unlock();
41589766727SVasanthakumar Thiagarajan }
41689766727SVasanthakumar Thiagarajan 
cfg80211_wiphy_work(struct work_struct * work)417a3ee4dc8SJohannes Berg static void cfg80211_wiphy_work(struct work_struct *work)
418a3ee4dc8SJohannes Berg {
419a3ee4dc8SJohannes Berg 	struct cfg80211_registered_device *rdev;
420a3ee4dc8SJohannes Berg 	struct wiphy_work *wk;
421a3ee4dc8SJohannes Berg 
422a3ee4dc8SJohannes Berg 	rdev = container_of(work, struct cfg80211_registered_device, wiphy_work);
423a3ee4dc8SJohannes Berg 
424a3ee4dc8SJohannes Berg 	wiphy_lock(&rdev->wiphy);
425a3ee4dc8SJohannes Berg 	if (rdev->suspended)
426a3ee4dc8SJohannes Berg 		goto out;
427a3ee4dc8SJohannes Berg 
428a3ee4dc8SJohannes Berg 	spin_lock_irq(&rdev->wiphy_work_lock);
429a3ee4dc8SJohannes Berg 	wk = list_first_entry_or_null(&rdev->wiphy_work_list,
430a3ee4dc8SJohannes Berg 				      struct wiphy_work, entry);
431a3ee4dc8SJohannes Berg 	if (wk) {
432a3ee4dc8SJohannes Berg 		list_del_init(&wk->entry);
433a3ee4dc8SJohannes Berg 		if (!list_empty(&rdev->wiphy_work_list))
43446b7eff5SJohannes Berg 			queue_work(system_unbound_wq, work);
435a3ee4dc8SJohannes Berg 		spin_unlock_irq(&rdev->wiphy_work_lock);
436a3ee4dc8SJohannes Berg 
437a3ee4dc8SJohannes Berg 		wk->func(&rdev->wiphy, wk);
438a3ee4dc8SJohannes Berg 	} else {
439a3ee4dc8SJohannes Berg 		spin_unlock_irq(&rdev->wiphy_work_lock);
440a3ee4dc8SJohannes Berg 	}
441a3ee4dc8SJohannes Berg out:
442a3ee4dc8SJohannes Berg 	wiphy_unlock(&rdev->wiphy);
443a3ee4dc8SJohannes Berg }
444a3ee4dc8SJohannes Berg 
445704232c2SJohannes Berg /* exported functions */
446704232c2SJohannes Berg 
wiphy_new_nm(const struct cfg80211_ops * ops,int sizeof_priv,const char * requested_name)4471998d90aSBen Greear struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
4481998d90aSBen Greear 			   const char *requested_name)
449704232c2SJohannes Berg {
45073810b77SJohannes Berg 	static atomic_t wiphy_counter = ATOMIC_INIT(0);
4517623225fSJohannes Berg 
4527623225fSJohannes Berg 	struct cfg80211_registered_device *rdev;
453704232c2SJohannes Berg 	int alloc_size;
454704232c2SJohannes Berg 
4550b20633dSJohannes Berg 	WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key));
4560b20633dSJohannes Berg 	WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc));
4570b20633dSJohannes Berg 	WARN_ON(ops->connect && !ops->disconnect);
4580b20633dSJohannes Berg 	WARN_ON(ops->join_ibss && !ops->leave_ibss);
4590b20633dSJohannes Berg 	WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf);
4600b20633dSJohannes Berg 	WARN_ON(ops->add_station && !ops->del_station);
4610b20633dSJohannes Berg 	WARN_ON(ops->add_mpath && !ops->del_mpath);
46229cbe68cSJohannes Berg 	WARN_ON(ops->join_mesh && !ops->leave_mesh);
463de3bb771SOla Olsson 	WARN_ON(ops->start_p2p_device && !ops->stop_p2p_device);
464de3bb771SOla Olsson 	WARN_ON(ops->start_ap && !ops->stop_ap);
465de3bb771SOla Olsson 	WARN_ON(ops->join_ocb && !ops->leave_ocb);
466de3bb771SOla Olsson 	WARN_ON(ops->suspend && !ops->resume);
467de3bb771SOla Olsson 	WARN_ON(ops->sched_scan_start && !ops->sched_scan_stop);
468de3bb771SOla Olsson 	WARN_ON(ops->remain_on_channel && !ops->cancel_remain_on_channel);
469de3bb771SOla Olsson 	WARN_ON(ops->tdls_channel_switch && !ops->tdls_cancel_channel_switch);
470de3bb771SOla Olsson 	WARN_ON(ops->add_tx_ts && !ops->del_tx_ts);
47141ade00fSJohannes Berg 
47279c97e97SJohannes Berg 	alloc_size = sizeof(*rdev) + sizeof_priv;
473704232c2SJohannes Berg 
47479c97e97SJohannes Berg 	rdev = kzalloc(alloc_size, GFP_KERNEL);
47579c97e97SJohannes Berg 	if (!rdev)
476704232c2SJohannes Berg 		return NULL;
477704232c2SJohannes Berg 
47879c97e97SJohannes Berg 	rdev->ops = ops;
479704232c2SJohannes Berg 
48073810b77SJohannes Berg 	rdev->wiphy_idx = atomic_inc_return(&wiphy_counter);
481a4d73ee1SJohannes Berg 
482f4173766SJohannes Berg 	if (unlikely(rdev->wiphy_idx < 0)) {
4837623225fSJohannes Berg 		/* ugh, wrapped! */
48473810b77SJohannes Berg 		atomic_dec(&wiphy_counter);
48579c97e97SJohannes Berg 		kfree(rdev);
486704232c2SJohannes Berg 		return NULL;
487704232c2SJohannes Berg 	}
488704232c2SJohannes Berg 
4899b881963SJohannes Berg 	/* atomic_inc_return makes it start at 1, make it start at 0 */
4909b881963SJohannes Berg 	rdev->wiphy_idx--;
4919b881963SJohannes Berg 
4927623225fSJohannes Berg 	/* give it a proper name */
4931998d90aSBen Greear 	if (requested_name && requested_name[0]) {
4941998d90aSBen Greear 		int rv;
4951998d90aSBen Greear 
4961998d90aSBen Greear 		rtnl_lock();
4971998d90aSBen Greear 		rv = cfg80211_dev_check_name(rdev, requested_name);
4981998d90aSBen Greear 
4991998d90aSBen Greear 		if (rv < 0) {
5001998d90aSBen Greear 			rtnl_unlock();
5011998d90aSBen Greear 			goto use_default_name;
5021998d90aSBen Greear 		}
5031998d90aSBen Greear 
5041998d90aSBen Greear 		rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name);
5051998d90aSBen Greear 		rtnl_unlock();
5061998d90aSBen Greear 		if (rv)
5071998d90aSBen Greear 			goto use_default_name;
5081998d90aSBen Greear 	} else {
50959b179b4SJohannes Berg 		int rv;
51059b179b4SJohannes Berg 
5111998d90aSBen Greear use_default_name:
5121998d90aSBen Greear 		/* NOTE:  This is *probably* safe w/out holding rtnl because of
5131998d90aSBen Greear 		 * the restrictions on phy names.  Probably this call could
5141998d90aSBen Greear 		 * fail if some other part of the kernel (re)named a device
5151998d90aSBen Greear 		 * phyX.  But, might should add some locking and check return
5161998d90aSBen Greear 		 * value, and use a different name if this one exists?
5171998d90aSBen Greear 		 */
51859b179b4SJohannes Berg 		rv = dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
51959b179b4SJohannes Berg 		if (rv < 0) {
52059b179b4SJohannes Berg 			kfree(rdev);
52159b179b4SJohannes Berg 			return NULL;
52259b179b4SJohannes Berg 		}
5231998d90aSBen Greear 	}
5247623225fSJohannes Berg 
525a05829a7SJohannes Berg 	mutex_init(&rdev->wiphy.mtx);
52653873f13SJohannes Berg 	INIT_LIST_HEAD(&rdev->wiphy.wdev_list);
52737c73b5fSBen Greear 	INIT_LIST_HEAD(&rdev->beacon_registrations);
52837c73b5fSBen Greear 	spin_lock_init(&rdev->beacon_registrations_lock);
52979c97e97SJohannes Berg 	spin_lock_init(&rdev->bss_lock);
53079c97e97SJohannes Berg 	INIT_LIST_HEAD(&rdev->bss_list);
531ca986ad9SArend Van Spriel 	INIT_LIST_HEAD(&rdev->sched_scan_req_list);
532fe0af9feSJohannes Berg 	wiphy_work_init(&rdev->scan_done_wk, __cfg80211_scan_done);
53304f39047SSimon Wunderlich 	INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
53404f39047SSimon Wunderlich 			  cfg80211_dfs_channels_update_work);
5353d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
5363d23e349SJohannes Berg 	rdev->wiphy.wext = &cfg80211_wext_handler;
5373d23e349SJohannes Berg #endif
5383d23e349SJohannes Berg 
53979c97e97SJohannes Berg 	device_initialize(&rdev->wiphy.dev);
54079c97e97SJohannes Berg 	rdev->wiphy.dev.class = &ieee80211_class;
54179c97e97SJohannes Berg 	rdev->wiphy.dev.platform_data = rdev;
5429f0e1354SFu, Zhonghui 	device_enable_async_suspend(&rdev->wiphy.dev);
543704232c2SJohannes Berg 
54478f22b6aSJohannes Berg 	INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
545c88d7178SJohannes Berg 	wiphy_work_init(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk);
546b34939b9SArend Van Spriel 	INIT_WORK(&rdev->sched_scan_res_wk, cfg80211_sched_scan_results_wk);
54789766727SVasanthakumar Thiagarajan 	INIT_WORK(&rdev->propagate_radar_detect_wk,
54889766727SVasanthakumar Thiagarajan 		  cfg80211_propagate_radar_detect_wk);
54989766727SVasanthakumar Thiagarajan 	INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk);
55079ea1e12SJohannes Berg 	INIT_WORK(&rdev->mgmt_registrations_update_wk,
55179ea1e12SJohannes Berg 		  cfg80211_mgmt_registrations_update_wk);
55209b1d5dcSJohannes Berg 	spin_lock_init(&rdev->mgmt_registrations_lock);
55378f22b6aSJohannes Berg 
5545be83de5SJohannes Berg #ifdef CONFIG_CFG80211_DEFAULT_PS
5555be83de5SJohannes Berg 	rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
5565be83de5SJohannes Berg #endif
55716cb9d42SJohannes Berg 
558463d0183SJohannes Berg 	wiphy_net_set(&rdev->wiphy, &init_net);
559463d0183SJohannes Berg 
56079c97e97SJohannes Berg 	rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block;
561358ae888SEmmanuel Grumbach 	rdev->wiphy.rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev),
56279c97e97SJohannes Berg 					  &rdev->wiphy.dev, RFKILL_TYPE_WLAN,
56379c97e97SJohannes Berg 					  &rdev->rfkill_ops, rdev);
5641f87f7d3SJohannes Berg 
565358ae888SEmmanuel Grumbach 	if (!rdev->wiphy.rfkill) {
5664f488fbcSEric Biggers 		wiphy_free(&rdev->wiphy);
5671f87f7d3SJohannes Berg 		return NULL;
5681f87f7d3SJohannes Berg 	}
5691f87f7d3SJohannes Berg 
570a3ee4dc8SJohannes Berg 	INIT_WORK(&rdev->wiphy_work, cfg80211_wiphy_work);
571a3ee4dc8SJohannes Berg 	INIT_LIST_HEAD(&rdev->wiphy_work_list);
572a3ee4dc8SJohannes Berg 	spin_lock_init(&rdev->wiphy_work_lock);
5733cfe91c4SJohannes Berg 	INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work);
57479c97e97SJohannes Berg 	INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
57579c97e97SJohannes Berg 	INIT_WORK(&rdev->event_work, cfg80211_event_work);
576a95bfb87SLorenzo Bianconi 	INIT_WORK(&rdev->background_cac_abort_wk,
577a95bfb87SLorenzo Bianconi 		  cfg80211_background_cac_abort_wk);
578a95bfb87SLorenzo Bianconi 	INIT_DELAYED_WORK(&rdev->background_cac_done_wk,
579a95bfb87SLorenzo Bianconi 			  cfg80211_background_cac_done_wk);
5801f87f7d3SJohannes Berg 
581ad002395SJohannes Berg 	init_waitqueue_head(&rdev->dev_wait);
582ad002395SJohannes Berg 
583b9a5f8caSJouni Malinen 	/*
584b9a5f8caSJouni Malinen 	 * Initialize wiphy parameters to IEEE 802.11 MIB default values.
585b9a5f8caSJouni Malinen 	 * Fragmentation and RTS threshold are disabled by default with the
586b9a5f8caSJouni Malinen 	 * special -1 value.
587b9a5f8caSJouni Malinen 	 */
58879c97e97SJohannes Berg 	rdev->wiphy.retry_short = 7;
58979c97e97SJohannes Berg 	rdev->wiphy.retry_long = 4;
59079c97e97SJohannes Berg 	rdev->wiphy.frag_threshold = (u32) -1;
59179c97e97SJohannes Berg 	rdev->wiphy.rts_threshold = (u32) -1;
59281077e82SLukáš Turek 	rdev->wiphy.coverage_class = 0;
593b9a5f8caSJouni Malinen 
5949a774c78SAndrei Otcheretianski 	rdev->wiphy.max_num_csa_counters = 1;
5959a774c78SAndrei Otcheretianski 
5963b06d277SAvraham Stern 	rdev->wiphy.max_sched_scan_plans = 1;
5973b06d277SAvraham Stern 	rdev->wiphy.max_sched_scan_plan_interval = U32_MAX;
5983b06d277SAvraham Stern 
59979c97e97SJohannes Berg 	return &rdev->wiphy;
600704232c2SJohannes Berg }
6011998d90aSBen Greear EXPORT_SYMBOL(wiphy_new_nm);
602704232c2SJohannes Berg 
wiphy_verify_combinations(struct wiphy * wiphy)6037527a782SJohannes Berg static int wiphy_verify_combinations(struct wiphy *wiphy)
6047527a782SJohannes Berg {
6057527a782SJohannes Berg 	const struct ieee80211_iface_combination *c;
6067527a782SJohannes Berg 	int i, j;
6077527a782SJohannes Berg 
6087527a782SJohannes Berg 	for (i = 0; i < wiphy->n_iface_combinations; i++) {
6097527a782SJohannes Berg 		u32 cnt = 0;
6107527a782SJohannes Berg 		u16 all_iftypes = 0;
6117527a782SJohannes Berg 
6127527a782SJohannes Berg 		c = &wiphy->iface_combinations[i];
6137527a782SJohannes Berg 
61411c4a075SSimon Wunderlich 		/*
61511c4a075SSimon Wunderlich 		 * Combinations with just one interface aren't real,
61611c4a075SSimon Wunderlich 		 * however we make an exception for DFS.
61711c4a075SSimon Wunderlich 		 */
61811c4a075SSimon Wunderlich 		if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths))
6197527a782SJohannes Berg 			return -EINVAL;
6207527a782SJohannes Berg 
6217527a782SJohannes Berg 		/* Need at least one channel */
6227527a782SJohannes Berg 		if (WARN_ON(!c->num_different_channels))
6237527a782SJohannes Berg 			return -EINVAL;
6247527a782SJohannes Berg 
62511c4a075SSimon Wunderlich 		/* DFS only works on one channel. */
62611c4a075SSimon Wunderlich 		if (WARN_ON(c->radar_detect_widths &&
62711c4a075SSimon Wunderlich 			    (c->num_different_channels > 1)))
62811c4a075SSimon Wunderlich 			return -EINVAL;
62911c4a075SSimon Wunderlich 
6307527a782SJohannes Berg 		if (WARN_ON(!c->n_limits))
6317527a782SJohannes Berg 			return -EINVAL;
6327527a782SJohannes Berg 
6337527a782SJohannes Berg 		for (j = 0; j < c->n_limits; j++) {
6347527a782SJohannes Berg 			u16 types = c->limits[j].types;
6357527a782SJohannes Berg 
636b6a55015SLuciano Coelho 			/* interface types shouldn't overlap */
6377527a782SJohannes Berg 			if (WARN_ON(types & all_iftypes))
6387527a782SJohannes Berg 				return -EINVAL;
6397527a782SJohannes Berg 			all_iftypes |= types;
6407527a782SJohannes Berg 
6417527a782SJohannes Berg 			if (WARN_ON(!c->limits[j].max))
6427527a782SJohannes Berg 				return -EINVAL;
6437527a782SJohannes Berg 
6447527a782SJohannes Berg 			/* Shouldn't list software iftypes in combinations! */
6457527a782SJohannes Berg 			if (WARN_ON(wiphy->software_iftypes & types))
6467527a782SJohannes Berg 				return -EINVAL;
6477527a782SJohannes Berg 
64898104fdeSJohannes Berg 			/* Only a single P2P_DEVICE can be allowed */
64998104fdeSJohannes Berg 			if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) &&
65098104fdeSJohannes Berg 				    c->limits[j].max > 1))
65198104fdeSJohannes Berg 				return -EINVAL;
65298104fdeSJohannes Berg 
653cb3b7d87SAyala Beker 			/* Only a single NAN can be allowed */
654cb3b7d87SAyala Beker 			if (WARN_ON(types & BIT(NL80211_IFTYPE_NAN) &&
655cb3b7d87SAyala Beker 				    c->limits[j].max > 1))
656cb3b7d87SAyala Beker 				return -EINVAL;
657cb3b7d87SAyala Beker 
65856271da2SJohannes Berg 			/*
65956271da2SJohannes Berg 			 * This isn't well-defined right now. If you have an
66056271da2SJohannes Berg 			 * IBSS interface, then its beacon interval may change
66156271da2SJohannes Berg 			 * by joining other networks, and nothing prevents it
66256271da2SJohannes Berg 			 * from doing that.
66356271da2SJohannes Berg 			 * So technically we probably shouldn't even allow AP
66456271da2SJohannes Berg 			 * and IBSS in the same interface, but it seems that
66556271da2SJohannes Berg 			 * some drivers support that, possibly only with fixed
66656271da2SJohannes Berg 			 * beacon intervals for IBSS.
66756271da2SJohannes Berg 			 */
66856271da2SJohannes Berg 			if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
66956271da2SJohannes Berg 				    c->beacon_int_min_gcd)) {
67056271da2SJohannes Berg 				return -EINVAL;
67156271da2SJohannes Berg 			}
67256271da2SJohannes Berg 
6737527a782SJohannes Berg 			cnt += c->limits[j].max;
6747527a782SJohannes Berg 			/*
6757527a782SJohannes Berg 			 * Don't advertise an unsupported type
6767527a782SJohannes Berg 			 * in a combination.
6777527a782SJohannes Berg 			 */
6787527a782SJohannes Berg 			if (WARN_ON((wiphy->interface_modes & types) != types))
6797527a782SJohannes Berg 				return -EINVAL;
6807527a782SJohannes Berg 		}
6817527a782SJohannes Berg 
6828f205423SJohannes Berg 		if (WARN_ON(all_iftypes & BIT(NL80211_IFTYPE_WDS)))
6838f205423SJohannes Berg 			return -EINVAL;
6848f205423SJohannes Berg 
6857527a782SJohannes Berg 		/* You can't even choose that many! */
6867527a782SJohannes Berg 		if (WARN_ON(cnt < c->max_interfaces))
6877527a782SJohannes Berg 			return -EINVAL;
6887527a782SJohannes Berg 	}
6897527a782SJohannes Berg 
6907527a782SJohannes Berg 	return 0;
6917527a782SJohannes Berg }
6927527a782SJohannes Berg 
wiphy_register(struct wiphy * wiphy)693704232c2SJohannes Berg int wiphy_register(struct wiphy *wiphy)
694704232c2SJohannes Berg {
695f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
696704232c2SJohannes Berg 	int res;
69757fbcce3SJohannes Berg 	enum nl80211_band band;
6988318d78aSJohannes Berg 	struct ieee80211_supported_band *sband;
6998318d78aSJohannes Berg 	bool have_band = false;
7008318d78aSJohannes Berg 	int i;
701f59ac048SLuis R. Rodriguez 	u16 ifmodes = wiphy->interface_modes;
702f59ac048SLuis R. Rodriguez 
703dfb89c56SJohannes Berg #ifdef CONFIG_PM
704964dc9e2SJohannes Berg 	if (WARN_ON(wiphy->wowlan &&
705964dc9e2SJohannes Berg 		    (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
706964dc9e2SJohannes Berg 		    !(wiphy->wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
707964dc9e2SJohannes Berg 		return -EINVAL;
708964dc9e2SJohannes Berg 	if (WARN_ON(wiphy->wowlan &&
709964dc9e2SJohannes Berg 		    !wiphy->wowlan->flags && !wiphy->wowlan->n_patterns &&
710964dc9e2SJohannes Berg 		    !wiphy->wowlan->tcp))
71177dbbb13SJohannes Berg 		return -EINVAL;
712dfb89c56SJohannes Berg #endif
7131057d35eSArik Nemtsov 	if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
7141057d35eSArik Nemtsov 		    (!rdev->ops->tdls_channel_switch ||
7151057d35eSArik Nemtsov 		     !rdev->ops->tdls_cancel_channel_switch)))
7161057d35eSArik Nemtsov 		return -EINVAL;
71777dbbb13SJohannes Berg 
718cb3b7d87SAyala Beker 	if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN)) &&
719a442b761SAyala Beker 		    (!rdev->ops->start_nan || !rdev->ops->stop_nan ||
7208585989dSLuca Coelho 		     !rdev->ops->add_nan_func || !rdev->ops->del_nan_func ||
7218585989dSLuca Coelho 		     !(wiphy->nan_supported_bands & BIT(NL80211_BAND_2GHZ)))))
722cb3b7d87SAyala Beker 		return -EINVAL;
723cb3b7d87SAyala Beker 
7248f205423SJohannes Berg 	if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)))
7258f205423SJohannes Berg 		return -EINVAL;
7268f205423SJohannes Berg 
7279bb7e0f2SJohannes Berg 	if (WARN_ON(wiphy->pmsr_capa && !wiphy->pmsr_capa->ftm.supported))
7289bb7e0f2SJohannes Berg 		return -EINVAL;
7299bb7e0f2SJohannes Berg 
7309bb7e0f2SJohannes Berg 	if (wiphy->pmsr_capa && wiphy->pmsr_capa->ftm.supported) {
7319bb7e0f2SJohannes Berg 		if (WARN_ON(!wiphy->pmsr_capa->ftm.asap &&
7329bb7e0f2SJohannes Berg 			    !wiphy->pmsr_capa->ftm.non_asap))
7339bb7e0f2SJohannes Berg 			return -EINVAL;
7349bb7e0f2SJohannes Berg 		if (WARN_ON(!wiphy->pmsr_capa->ftm.preambles ||
7359bb7e0f2SJohannes Berg 			    !wiphy->pmsr_capa->ftm.bandwidths))
7369bb7e0f2SJohannes Berg 			return -EINVAL;
7379bb7e0f2SJohannes Berg 		if (WARN_ON(wiphy->pmsr_capa->ftm.preambles &
7389bb7e0f2SJohannes Berg 				~(BIT(NL80211_PREAMBLE_LEGACY) |
7399bb7e0f2SJohannes Berg 				  BIT(NL80211_PREAMBLE_HT) |
7409bb7e0f2SJohannes Berg 				  BIT(NL80211_PREAMBLE_VHT) |
741efb5520dSAvraham Stern 				  BIT(NL80211_PREAMBLE_HE) |
7429bb7e0f2SJohannes Berg 				  BIT(NL80211_PREAMBLE_DMG))))
7439bb7e0f2SJohannes Berg 			return -EINVAL;
744efb5520dSAvraham Stern 		if (WARN_ON((wiphy->pmsr_capa->ftm.trigger_based ||
745efb5520dSAvraham Stern 			     wiphy->pmsr_capa->ftm.non_trigger_based) &&
746efb5520dSAvraham Stern 			    !(wiphy->pmsr_capa->ftm.preambles &
747efb5520dSAvraham Stern 			      BIT(NL80211_PREAMBLE_HE))))
748efb5520dSAvraham Stern 			return -EINVAL;
7499bb7e0f2SJohannes Berg 		if (WARN_ON(wiphy->pmsr_capa->ftm.bandwidths &
7509bb7e0f2SJohannes Berg 				~(BIT(NL80211_CHAN_WIDTH_20_NOHT) |
7519bb7e0f2SJohannes Berg 				  BIT(NL80211_CHAN_WIDTH_20) |
7529bb7e0f2SJohannes Berg 				  BIT(NL80211_CHAN_WIDTH_40) |
7539bb7e0f2SJohannes Berg 				  BIT(NL80211_CHAN_WIDTH_80) |
7549bb7e0f2SJohannes Berg 				  BIT(NL80211_CHAN_WIDTH_80P80) |
7559bb7e0f2SJohannes Berg 				  BIT(NL80211_CHAN_WIDTH_160) |
7569bb7e0f2SJohannes Berg 				  BIT(NL80211_CHAN_WIDTH_5) |
7579bb7e0f2SJohannes Berg 				  BIT(NL80211_CHAN_WIDTH_10))))
7589bb7e0f2SJohannes Berg 			return -EINVAL;
7599bb7e0f2SJohannes Berg 	}
7609bb7e0f2SJohannes Berg 
761b0d7aa59SJonathan Doron 	if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) &&
762b0d7aa59SJonathan Doron 		    (wiphy->regulatory_flags &
763b0d7aa59SJonathan Doron 					(REGULATORY_CUSTOM_REG |
764b0d7aa59SJonathan Doron 					 REGULATORY_STRICT_REG |
765b0d7aa59SJonathan Doron 					 REGULATORY_COUNTRY_IE_FOLLOW_POWER |
766b0d7aa59SJonathan Doron 					 REGULATORY_COUNTRY_IE_IGNORE))))
767b0d7aa59SJonathan Doron 		return -EINVAL;
768b0d7aa59SJonathan Doron 
769be29b99aSAmitkumar Karwar 	if (WARN_ON(wiphy->coalesce &&
770be29b99aSAmitkumar Karwar 		    (!wiphy->coalesce->n_rules ||
771be29b99aSAmitkumar Karwar 		     !wiphy->coalesce->n_patterns) &&
772be29b99aSAmitkumar Karwar 		    (!wiphy->coalesce->pattern_min_len ||
773be29b99aSAmitkumar Karwar 		     wiphy->coalesce->pattern_min_len >
774be29b99aSAmitkumar Karwar 			wiphy->coalesce->pattern_max_len)))
775be29b99aSAmitkumar Karwar 		return -EINVAL;
776be29b99aSAmitkumar Karwar 
777562a7480SJohannes Berg 	if (WARN_ON(wiphy->ap_sme_capa &&
778562a7480SJohannes Berg 		    !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
779562a7480SJohannes Berg 		return -EINVAL;
780562a7480SJohannes Berg 
781ef15aac6SJohannes Berg 	if (WARN_ON(wiphy->addresses && !wiphy->n_addresses))
782ef15aac6SJohannes Berg 		return -EINVAL;
783ef15aac6SJohannes Berg 
784ef15aac6SJohannes Berg 	if (WARN_ON(wiphy->addresses &&
785ef15aac6SJohannes Berg 		    !is_zero_ether_addr(wiphy->perm_addr) &&
786ef15aac6SJohannes Berg 		    memcmp(wiphy->perm_addr, wiphy->addresses[0].addr,
787ef15aac6SJohannes Berg 			   ETH_ALEN)))
788ef15aac6SJohannes Berg 		return -EINVAL;
789ef15aac6SJohannes Berg 
79077765eafSVasanthakumar Thiagarajan 	if (WARN_ON(wiphy->max_acl_mac_addrs &&
79177765eafSVasanthakumar Thiagarajan 		    (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) ||
79277765eafSVasanthakumar Thiagarajan 		     !rdev->ops->set_mac_acl)))
79377765eafSVasanthakumar Thiagarajan 		return -EINVAL;
79477765eafSVasanthakumar Thiagarajan 
79538de03d2SArend van Spriel 	/* assure only valid behaviours are flagged by driver
79638de03d2SArend van Spriel 	 * hence subtract 2 as bit 0 is invalid.
79738de03d2SArend van Spriel 	 */
79838de03d2SArend van Spriel 	if (WARN_ON(wiphy->bss_select_support &&
79938de03d2SArend van Spriel 		    (wiphy->bss_select_support & ~(BIT(__NL80211_BSS_SELECT_ATTR_AFTER_LAST) - 2))))
80038de03d2SArend van Spriel 		return -EINVAL;
80138de03d2SArend van Spriel 
8023a00df57SAvraham Stern 	if (WARN_ON(wiphy_ext_feature_isset(&rdev->wiphy,
8033a00df57SAvraham Stern 					    NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X) &&
8043a00df57SAvraham Stern 		    (!rdev->ops->set_pmk || !rdev->ops->del_pmk)))
8053a00df57SAvraham Stern 		return -EINVAL;
8063a00df57SAvraham Stern 
8077f9a3e15SVidyullatha Kanchanapally 	if (WARN_ON(!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
8087f9a3e15SVidyullatha Kanchanapally 		    rdev->ops->update_connect_params))
8097f9a3e15SVidyullatha Kanchanapally 		return -EINVAL;
8107f9a3e15SVidyullatha Kanchanapally 
811ef15aac6SJohannes Berg 	if (wiphy->addresses)
812ef15aac6SJohannes Berg 		memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
813ef15aac6SJohannes Berg 
814f59ac048SLuis R. Rodriguez 	/* sanity check ifmodes */
815f59ac048SLuis R. Rodriguez 	WARN_ON(!ifmodes);
8162e161f78SJohannes Berg 	ifmodes &= ((1 << NUM_NL80211_IFTYPES) - 1) & ~1;
817f59ac048SLuis R. Rodriguez 	if (WARN_ON(ifmodes != wiphy->interface_modes))
818f59ac048SLuis R. Rodriguez 		wiphy->interface_modes = ifmodes;
8198318d78aSJohannes Berg 
8207527a782SJohannes Berg 	res = wiphy_verify_combinations(wiphy);
8217527a782SJohannes Berg 	if (res)
8227527a782SJohannes Berg 		return res;
8237527a782SJohannes Berg 
8248318d78aSJohannes Berg 	/* sanity check supported bands/channels */
82557fbcce3SJohannes Berg 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
826c4cbaf79SLuca Coelho 		u16 types = 0;
827f4381365SJohannes Berg 		bool have_he = false;
828c4cbaf79SLuca Coelho 
8298318d78aSJohannes Berg 		sband = wiphy->bands[band];
8308318d78aSJohannes Berg 		if (!sband)
8318318d78aSJohannes Berg 			continue;
8328318d78aSJohannes Berg 
8338318d78aSJohannes Berg 		sband->band = band;
8343a0c52a6SVladimir Kondratiev 		if (WARN_ON(!sband->n_channels))
8353a0c52a6SVladimir Kondratiev 			return -EINVAL;
8363a0c52a6SVladimir Kondratiev 		/*
837df78a0c0SThomas Pedersen 		 * on 60GHz or sub-1Ghz band, there are no legacy rates, so
8383a0c52a6SVladimir Kondratiev 		 * n_bitrates is 0
8393a0c52a6SVladimir Kondratiev 		 */
840df78a0c0SThomas Pedersen 		if (WARN_ON((band != NL80211_BAND_60GHZ &&
841df78a0c0SThomas Pedersen 			     band != NL80211_BAND_S1GHZ) &&
8423a0c52a6SVladimir Kondratiev 			    !sband->n_bitrates))
8438318d78aSJohannes Berg 			return -EINVAL;
844881d948cSJohannes Berg 
845461ce35dSJohannes Berg 		if (WARN_ON(band == NL80211_BAND_6GHZ &&
846461ce35dSJohannes Berg 			    (sband->ht_cap.ht_supported ||
847461ce35dSJohannes Berg 			     sband->vht_cap.vht_supported)))
848461ce35dSJohannes Berg 			return -EINVAL;
849461ce35dSJohannes Berg 
850881d948cSJohannes Berg 		/*
85140db6c77SAmitkumar Karwar 		 * Since cfg80211_disable_40mhz_24ghz is global, we can
85240db6c77SAmitkumar Karwar 		 * modify the sband's ht data even if the driver uses a
85340db6c77SAmitkumar Karwar 		 * global structure for that.
85440db6c77SAmitkumar Karwar 		 */
85540db6c77SAmitkumar Karwar 		if (cfg80211_disable_40mhz_24ghz &&
85657fbcce3SJohannes Berg 		    band == NL80211_BAND_2GHZ &&
85740db6c77SAmitkumar Karwar 		    sband->ht_cap.ht_supported) {
85840db6c77SAmitkumar Karwar 			sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
85940db6c77SAmitkumar Karwar 			sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
86040db6c77SAmitkumar Karwar 		}
86140db6c77SAmitkumar Karwar 
86240db6c77SAmitkumar Karwar 		/*
863881d948cSJohannes Berg 		 * Since we use a u32 for rate bitmaps in
864881d948cSJohannes Berg 		 * ieee80211_get_response_rate, we cannot
865881d948cSJohannes Berg 		 * have more than 32 legacy rates.
866881d948cSJohannes Berg 		 */
867881d948cSJohannes Berg 		if (WARN_ON(sband->n_bitrates > 32))
868881d948cSJohannes Berg 			return -EINVAL;
8698318d78aSJohannes Berg 
8708318d78aSJohannes Berg 		for (i = 0; i < sband->n_channels; i++) {
8718318d78aSJohannes Berg 			sband->channels[i].orig_flags =
8728318d78aSJohannes Berg 				sband->channels[i].flags;
873c4a9fafcSFelix Fietkau 			sband->channels[i].orig_mag = INT_MAX;
8748318d78aSJohannes Berg 			sband->channels[i].orig_mpwr =
8758318d78aSJohannes Berg 				sband->channels[i].max_power;
8768318d78aSJohannes Berg 			sband->channels[i].band = band;
877be689f68SJohannes Berg 
878be689f68SJohannes Berg 			if (WARN_ON(sband->channels[i].freq_offset >= 1000))
879be689f68SJohannes Berg 				return -EINVAL;
8808318d78aSJohannes Berg 		}
8818318d78aSJohannes Berg 
882c4cbaf79SLuca Coelho 		for (i = 0; i < sband->n_iftype_data; i++) {
883c4cbaf79SLuca Coelho 			const struct ieee80211_sband_iftype_data *iftd;
884ea5cba26SJohannes Berg 			bool has_ap, has_non_ap;
885ea5cba26SJohannes Berg 			u32 ap_bits = BIT(NL80211_IFTYPE_AP) |
886ea5cba26SJohannes Berg 				      BIT(NL80211_IFTYPE_P2P_GO);
887c4cbaf79SLuca Coelho 
888c4cbaf79SLuca Coelho 			iftd = &sband->iftype_data[i];
889c4cbaf79SLuca Coelho 
890c4cbaf79SLuca Coelho 			if (WARN_ON(!iftd->types_mask))
891c4cbaf79SLuca Coelho 				return -EINVAL;
892c4cbaf79SLuca Coelho 			if (WARN_ON(types & iftd->types_mask))
893c4cbaf79SLuca Coelho 				return -EINVAL;
894c4cbaf79SLuca Coelho 
895c4cbaf79SLuca Coelho 			/* at least one piece of information must be present */
896c4cbaf79SLuca Coelho 			if (WARN_ON(!iftd->he_cap.has_he))
897c4cbaf79SLuca Coelho 				return -EINVAL;
898c4cbaf79SLuca Coelho 
899c4cbaf79SLuca Coelho 			types |= iftd->types_mask;
900f4381365SJohannes Berg 
901f4381365SJohannes Berg 			if (i == 0)
902f4381365SJohannes Berg 				have_he = iftd->he_cap.has_he;
903f4381365SJohannes Berg 			else
904f4381365SJohannes Berg 				have_he = have_he &&
905f4381365SJohannes Berg 					  iftd->he_cap.has_he;
906ea5cba26SJohannes Berg 
907ea5cba26SJohannes Berg 			has_ap = iftd->types_mask & ap_bits;
908ea5cba26SJohannes Berg 			has_non_ap = iftd->types_mask & ~ap_bits;
909ea5cba26SJohannes Berg 
910ea5cba26SJohannes Berg 			/*
911ea5cba26SJohannes Berg 			 * For EHT 20 MHz STA, the capabilities format differs
912ea5cba26SJohannes Berg 			 * but to simplify, don't check 20 MHz but rather check
913ea5cba26SJohannes Berg 			 * only if AP and non-AP were mentioned at the same time,
914ea5cba26SJohannes Berg 			 * reject if so.
915ea5cba26SJohannes Berg 			 */
916ea5cba26SJohannes Berg 			if (WARN_ON(iftd->eht_cap.has_eht &&
917ea5cba26SJohannes Berg 				    has_ap && has_non_ap))
918ea5cba26SJohannes Berg 				return -EINVAL;
919c4cbaf79SLuca Coelho 		}
920c4cbaf79SLuca Coelho 
921f4381365SJohannes Berg 		if (WARN_ON(!have_he && band == NL80211_BAND_6GHZ))
922f4381365SJohannes Berg 			return -EINVAL;
923f4381365SJohannes Berg 
9248318d78aSJohannes Berg 		have_band = true;
9258318d78aSJohannes Berg 	}
9268318d78aSJohannes Berg 
9278318d78aSJohannes Berg 	if (!have_band) {
9288318d78aSJohannes Berg 		WARN_ON(1);
9298318d78aSJohannes Berg 		return -EINVAL;
9308318d78aSJohannes Berg 	}
9318318d78aSJohannes Berg 
932901bb989SJohannes Berg 	for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
933901bb989SJohannes Berg 		/*
934901bb989SJohannes Berg 		 * Validate we have a policy (can be explicitly set to
935901bb989SJohannes Berg 		 * VENDOR_CMD_RAW_DATA which is non-NULL) and also that
936901bb989SJohannes Berg 		 * we have at least one of doit/dumpit.
937901bb989SJohannes Berg 		 */
938901bb989SJohannes Berg 		if (WARN_ON(!rdev->wiphy.vendor_commands[i].policy))
939901bb989SJohannes Berg 			return -EINVAL;
940901bb989SJohannes Berg 		if (WARN_ON(!rdev->wiphy.vendor_commands[i].doit &&
941901bb989SJohannes Berg 			    !rdev->wiphy.vendor_commands[i].dumpit))
942901bb989SJohannes Berg 			return -EINVAL;
943901bb989SJohannes Berg 	}
944901bb989SJohannes Berg 
945dfb89c56SJohannes Berg #ifdef CONFIG_PM
946964dc9e2SJohannes Berg 	if (WARN_ON(rdev->wiphy.wowlan && rdev->wiphy.wowlan->n_patterns &&
947964dc9e2SJohannes Berg 		    (!rdev->wiphy.wowlan->pattern_min_len ||
948964dc9e2SJohannes Berg 		     rdev->wiphy.wowlan->pattern_min_len >
949964dc9e2SJohannes Berg 				rdev->wiphy.wowlan->pattern_max_len)))
950ff1b6e69SJohannes Berg 		return -EINVAL;
951dfb89c56SJohannes Berg #endif
952ff1b6e69SJohannes Berg 
953ecad3b0bSVeerendranath Jakkam 	if (!wiphy->max_num_akm_suites)
954ecad3b0bSVeerendranath Jakkam 		wiphy->max_num_akm_suites = NL80211_MAX_NR_AKM_SUITES;
955ecad3b0bSVeerendranath Jakkam 	else if (wiphy->max_num_akm_suites < NL80211_MAX_NR_AKM_SUITES ||
956ecad3b0bSVeerendranath Jakkam 		 wiphy->max_num_akm_suites > CFG80211_MAX_NUM_AKM_SUITES)
957ecad3b0bSVeerendranath Jakkam 		return -EINVAL;
958ecad3b0bSVeerendranath Jakkam 
9598318d78aSJohannes Berg 	/* check and set up bitrates */
9608318d78aSJohannes Berg 	ieee80211_set_bitrate_flags(wiphy);
9618318d78aSJohannes Berg 
96200c3a6edSJohannes Berg 	rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH;
96300c3a6edSJohannes Berg 
964aa5f66d5SJohannes Berg 	rtnl_lock();
9654d45145bSJohannes Berg 	wiphy_lock(&rdev->wiphy);
96679c97e97SJohannes Berg 	res = device_add(&rdev->wiphy.dev);
967c3d34d5dSJohn W. Linville 	if (res) {
9684d45145bSJohannes Berg 		wiphy_unlock(&rdev->wiphy);
969aa5f66d5SJohannes Berg 		rtnl_unlock();
970c3d34d5dSJohn W. Linville 		return res;
971c3d34d5dSJohn W. Linville 	}
9721f87f7d3SJohannes Berg 
9735f2aa25eSJohannes Berg 	list_add_rcu(&rdev->list, &cfg80211_rdev_list);
974f5ea9120SJohannes Berg 	cfg80211_rdev_list_generation++;
975704232c2SJohannes Berg 
976704232c2SJohannes Berg 	/* add to debugfs */
977d82574a8SGreg Kroah-Hartman 	rdev->wiphy.debugfsdir = debugfs_create_dir(wiphy_name(&rdev->wiphy),
978704232c2SJohannes Berg 						    ieee80211_debugfs_dir);
979704232c2SJohannes Berg 
980a796dac9STomasz Bursztyka 	cfg80211_debugfs_rdev_add(rdev);
981a796dac9STomasz Bursztyka 	nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
9824d45145bSJohannes Berg 	wiphy_unlock(&rdev->wiphy);
983a796dac9STomasz Bursztyka 
9841b7b3ac8SMiri Korenblit 	/* set up regulatory info */
9851b7b3ac8SMiri Korenblit 	wiphy_regulatory_register(wiphy);
9861b7b3ac8SMiri Korenblit 
987a2f73b6cSLuis R. Rodriguez 	if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
98873d54c9eSLuis R. Rodriguez 		struct regulatory_request request;
98973d54c9eSLuis R. Rodriguez 
99073d54c9eSLuis R. Rodriguez 		request.wiphy_idx = get_wiphy_idx(wiphy);
99173d54c9eSLuis R. Rodriguez 		request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
99273d54c9eSLuis R. Rodriguez 		request.alpha2[0] = '9';
99373d54c9eSLuis R. Rodriguez 		request.alpha2[1] = '9';
99473d54c9eSLuis R. Rodriguez 
99573d54c9eSLuis R. Rodriguez 		nl80211_send_reg_change_event(&request);
99673d54c9eSLuis R. Rodriguez 	}
99773d54c9eSLuis R. Rodriguez 
998019ae3a9SKanchanapally, Vidyullatha 	/* Check that nobody globally advertises any capabilities they do not
999019ae3a9SKanchanapally, Vidyullatha 	 * advertise on all possible interface types.
1000019ae3a9SKanchanapally, Vidyullatha 	 */
1001019ae3a9SKanchanapally, Vidyullatha 	if (wiphy->extended_capabilities_len &&
1002019ae3a9SKanchanapally, Vidyullatha 	    wiphy->num_iftype_ext_capab &&
1003019ae3a9SKanchanapally, Vidyullatha 	    wiphy->iftype_ext_capab) {
1004019ae3a9SKanchanapally, Vidyullatha 		u8 supported_on_all, j;
1005019ae3a9SKanchanapally, Vidyullatha 		const struct wiphy_iftype_ext_capab *capab;
1006019ae3a9SKanchanapally, Vidyullatha 
1007019ae3a9SKanchanapally, Vidyullatha 		capab = wiphy->iftype_ext_capab;
1008019ae3a9SKanchanapally, Vidyullatha 		for (j = 0; j < wiphy->extended_capabilities_len; j++) {
1009019ae3a9SKanchanapally, Vidyullatha 			if (capab[0].extended_capabilities_len > j)
1010019ae3a9SKanchanapally, Vidyullatha 				supported_on_all =
1011019ae3a9SKanchanapally, Vidyullatha 					capab[0].extended_capabilities[j];
1012019ae3a9SKanchanapally, Vidyullatha 			else
1013019ae3a9SKanchanapally, Vidyullatha 				supported_on_all = 0x00;
1014019ae3a9SKanchanapally, Vidyullatha 			for (i = 1; i < wiphy->num_iftype_ext_capab; i++) {
1015019ae3a9SKanchanapally, Vidyullatha 				if (j >= capab[i].extended_capabilities_len) {
1016019ae3a9SKanchanapally, Vidyullatha 					supported_on_all = 0x00;
1017019ae3a9SKanchanapally, Vidyullatha 					break;
1018019ae3a9SKanchanapally, Vidyullatha 				}
1019019ae3a9SKanchanapally, Vidyullatha 				supported_on_all &=
1020019ae3a9SKanchanapally, Vidyullatha 					capab[i].extended_capabilities[j];
1021019ae3a9SKanchanapally, Vidyullatha 			}
1022019ae3a9SKanchanapally, Vidyullatha 			if (WARN_ON(wiphy->extended_capabilities[j] &
1023019ae3a9SKanchanapally, Vidyullatha 				    ~supported_on_all))
1024019ae3a9SKanchanapally, Vidyullatha 				break;
1025019ae3a9SKanchanapally, Vidyullatha 		}
1026019ae3a9SKanchanapally, Vidyullatha 	}
1027019ae3a9SKanchanapally, Vidyullatha 
1028ecb44335SStanislaw Gruszka 	rdev->wiphy.registered = true;
1029ecb44335SStanislaw Gruszka 	rtnl_unlock();
1030aa5f66d5SJohannes Berg 
1031358ae888SEmmanuel Grumbach 	res = rfkill_register(rdev->wiphy.rfkill);
1032aa5f66d5SJohannes Berg 	if (res) {
1033358ae888SEmmanuel Grumbach 		rfkill_destroy(rdev->wiphy.rfkill);
1034358ae888SEmmanuel Grumbach 		rdev->wiphy.rfkill = NULL;
1035aa5f66d5SJohannes Berg 		wiphy_unregister(&rdev->wiphy);
1036aa5f66d5SJohannes Berg 		return res;
1037aa5f66d5SJohannes Berg 	}
1038aa5f66d5SJohannes Berg 
10392f0accc1SJohannes Berg 	return 0;
1040704232c2SJohannes Berg }
1041704232c2SJohannes Berg EXPORT_SYMBOL(wiphy_register);
1042704232c2SJohannes Berg 
wiphy_rfkill_start_polling(struct wiphy * wiphy)10431f87f7d3SJohannes Berg void wiphy_rfkill_start_polling(struct wiphy *wiphy)
10441f87f7d3SJohannes Berg {
1045f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
10461f87f7d3SJohannes Berg 
104779c97e97SJohannes Berg 	if (!rdev->ops->rfkill_poll)
10481f87f7d3SJohannes Berg 		return;
104979c97e97SJohannes Berg 	rdev->rfkill_ops.poll = cfg80211_rfkill_poll;
1050358ae888SEmmanuel Grumbach 	rfkill_resume_polling(wiphy->rfkill);
10511f87f7d3SJohannes Berg }
10521f87f7d3SJohannes Berg EXPORT_SYMBOL(wiphy_rfkill_start_polling);
10531f87f7d3SJohannes Berg 
cfg80211_process_wiphy_works(struct cfg80211_registered_device * rdev,struct wiphy_work * end)10545d9eefa2SJohannes Berg void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev,
10555d9eefa2SJohannes Berg 				  struct wiphy_work *end)
1056a3ee4dc8SJohannes Berg {
1057a3ee4dc8SJohannes Berg 	unsigned int runaway_limit = 100;
1058a3ee4dc8SJohannes Berg 	unsigned long flags;
1059a3ee4dc8SJohannes Berg 
1060a3ee4dc8SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
1061a3ee4dc8SJohannes Berg 
1062a3ee4dc8SJohannes Berg 	spin_lock_irqsave(&rdev->wiphy_work_lock, flags);
1063a3ee4dc8SJohannes Berg 	while (!list_empty(&rdev->wiphy_work_list)) {
1064a3ee4dc8SJohannes Berg 		struct wiphy_work *wk;
1065a3ee4dc8SJohannes Berg 
1066a3ee4dc8SJohannes Berg 		wk = list_first_entry(&rdev->wiphy_work_list,
1067a3ee4dc8SJohannes Berg 				      struct wiphy_work, entry);
1068a3ee4dc8SJohannes Berg 		list_del_init(&wk->entry);
1069a3ee4dc8SJohannes Berg 		spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags);
1070a3ee4dc8SJohannes Berg 
1071a3ee4dc8SJohannes Berg 		wk->func(&rdev->wiphy, wk);
1072a3ee4dc8SJohannes Berg 
1073a3ee4dc8SJohannes Berg 		spin_lock_irqsave(&rdev->wiphy_work_lock, flags);
10745d9eefa2SJohannes Berg 
10755d9eefa2SJohannes Berg 		if (wk == end)
10765d9eefa2SJohannes Berg 			break;
10775d9eefa2SJohannes Berg 
1078a3ee4dc8SJohannes Berg 		if (WARN_ON(--runaway_limit == 0))
1079a3ee4dc8SJohannes Berg 			INIT_LIST_HEAD(&rdev->wiphy_work_list);
1080a3ee4dc8SJohannes Berg 	}
1081a3ee4dc8SJohannes Berg 	spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags);
1082a3ee4dc8SJohannes Berg }
1083a3ee4dc8SJohannes Berg 
wiphy_unregister(struct wiphy * wiphy)1084704232c2SJohannes Berg void wiphy_unregister(struct wiphy *wiphy)
1085704232c2SJohannes Berg {
1086f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1087704232c2SJohannes Berg 
10885fe231e8SJohannes Berg 	wait_event(rdev->dev_wait, ({
10895fe231e8SJohannes Berg 		int __count;
1090a05829a7SJohannes Berg 		wiphy_lock(&rdev->wiphy);
10915fe231e8SJohannes Berg 		__count = rdev->opencount;
1092a05829a7SJohannes Berg 		wiphy_unlock(&rdev->wiphy);
10935fe231e8SJohannes Berg 		__count == 0; }));
10945fe231e8SJohannes Berg 
1095358ae888SEmmanuel Grumbach 	if (rdev->wiphy.rfkill)
1096358ae888SEmmanuel Grumbach 		rfkill_unregister(rdev->wiphy.rfkill);
1097256c90deSJohannes Berg 
1098ecb44335SStanislaw Gruszka 	rtnl_lock();
1099a05829a7SJohannes Berg 	wiphy_lock(&rdev->wiphy);
11003bb20556SJohannes Berg 	nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
1101ecb44335SStanislaw Gruszka 	rdev->wiphy.registered = false;
1102ecb44335SStanislaw Gruszka 
110353873f13SJohannes Berg 	WARN_ON(!list_empty(&rdev->wiphy.wdev_list));
1104ad002395SJohannes Berg 
1105ad002395SJohannes Berg 	/*
1106ad002395SJohannes Berg 	 * First remove the hardware from everywhere, this makes
1107ad002395SJohannes Berg 	 * it impossible to find from userspace.
1108ad002395SJohannes Berg 	 */
11097bcfaf2fSJohannes Berg 	debugfs_remove_recursive(rdev->wiphy.debugfsdir);
11105f2aa25eSJohannes Berg 	list_del_rcu(&rdev->list);
11115f2aa25eSJohannes Berg 	synchronize_rcu();
1112f16bfc1cSJohannes Berg 
1113f16bfc1cSJohannes Berg 	/*
1114bfead080SLuis R. Rodriguez 	 * If this device got a regulatory hint tell core its
1115bfead080SLuis R. Rodriguez 	 * free to listen now to a new shiny device regulatory hint
1116bfead080SLuis R. Rodriguez 	 */
1117bfead080SLuis R. Rodriguez 	wiphy_regulatory_deregister(wiphy);
11183f2355cbSLuis R. Rodriguez 
1119f5ea9120SJohannes Berg 	cfg80211_rdev_list_generation++;
112079c97e97SJohannes Berg 	device_del(&rdev->wiphy.dev);
1121704232c2SJohannes Berg 
1122a993df0fSJohannes Berg #ifdef CONFIG_PM
1123a993df0fSJohannes Berg 	if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
1124a993df0fSJohannes Berg 		rdev_set_wakeup(rdev, false);
1125a993df0fSJohannes Berg #endif
1126a3ee4dc8SJohannes Berg 
1127a3ee4dc8SJohannes Berg 	/* surely nothing is reachable now, clean up work */
11285d9eefa2SJohannes Berg 	cfg80211_process_wiphy_works(rdev, NULL);
1129a05829a7SJohannes Berg 	wiphy_unlock(&rdev->wiphy);
11305fe231e8SJohannes Berg 	rtnl_unlock();
11316682588aSJohannes Berg 
1132a3ee4dc8SJohannes Berg 	/* this has nothing to do now but make sure it's gone */
1133a3ee4dc8SJohannes Berg 	cancel_work_sync(&rdev->wiphy_work);
1134a3ee4dc8SJohannes Berg 
11356682588aSJohannes Berg 	cancel_work_sync(&rdev->conn_work);
11366682588aSJohannes Berg 	flush_work(&rdev->event_work);
113704f39047SSimon Wunderlich 	cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
1138a95bfb87SLorenzo Bianconi 	cancel_delayed_work_sync(&rdev->background_cac_done_wk);
113978f22b6aSJohannes Berg 	flush_work(&rdev->destroy_work);
114089766727SVasanthakumar Thiagarajan 	flush_work(&rdev->propagate_radar_detect_wk);
114189766727SVasanthakumar Thiagarajan 	flush_work(&rdev->propagate_cac_done_wk);
114279ea1e12SJohannes Berg 	flush_work(&rdev->mgmt_registrations_update_wk);
1143a95bfb87SLorenzo Bianconi 	flush_work(&rdev->background_cac_abort_wk);
11446d52563fSJohannes Berg 
11456d52563fSJohannes Berg 	cfg80211_rdev_free_wowlan(rdev);
1146be29b99aSAmitkumar Karwar 	cfg80211_rdev_free_coalesce(rdev);
1147704232c2SJohannes Berg }
1148704232c2SJohannes Berg EXPORT_SYMBOL(wiphy_unregister);
1149704232c2SJohannes Berg 
cfg80211_dev_free(struct cfg80211_registered_device * rdev)115079c97e97SJohannes Berg void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
1151704232c2SJohannes Berg {
11522a519311SJohannes Berg 	struct cfg80211_internal_bss *scan, *tmp;
115337c73b5fSBen Greear 	struct cfg80211_beacon_registration *reg, *treg;
1154*75d262adSMiri Korenblit 	unsigned long flags;
1155*75d262adSMiri Korenblit 
1156*75d262adSMiri Korenblit 	spin_lock_irqsave(&rdev->wiphy_work_lock, flags);
1157*75d262adSMiri Korenblit 	WARN_ON(!list_empty(&rdev->wiphy_work_list));
1158*75d262adSMiri Korenblit 	spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags);
1159*75d262adSMiri Korenblit 	cancel_work_sync(&rdev->wiphy_work);
1160*75d262adSMiri Korenblit 
1161358ae888SEmmanuel Grumbach 	rfkill_destroy(rdev->wiphy.rfkill);
116237c73b5fSBen Greear 	list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) {
116337c73b5fSBen Greear 		list_del(&reg->list);
116437c73b5fSBen Greear 		kfree(reg);
116537c73b5fSBen Greear 	}
116679c97e97SJohannes Berg 	list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
11675b112d3dSJohannes Berg 		cfg80211_put_bss(&rdev->wiphy, &scan->pub);
1168a05829a7SJohannes Berg 	mutex_destroy(&rdev->wiphy.mtx);
1169e53e9828SJohannes Berg 
1170e53e9828SJohannes Berg 	/*
1171e53e9828SJohannes Berg 	 * The 'regd' can only be non-NULL if we never finished
1172e53e9828SJohannes Berg 	 * initializing the wiphy and thus never went through the
1173e53e9828SJohannes Berg 	 * unregister path - e.g. in failure scenarios. Thus, it
1174e53e9828SJohannes Berg 	 * cannot have been visible to anyone if non-NULL, so we
1175e53e9828SJohannes Berg 	 * can just free it here.
1176e53e9828SJohannes Berg 	 */
1177e53e9828SJohannes Berg 	kfree(rcu_dereference_raw(rdev->wiphy.regd));
1178e53e9828SJohannes Berg 
117979c97e97SJohannes Berg 	kfree(rdev);
1180704232c2SJohannes Berg }
wiphy_rfkill_set_hw_state_reason(struct wiphy * wiphy,bool blocked,enum rfkill_hard_block_reasons reason)1181704232c2SJohannes Berg 
1182704232c2SJohannes Berg void wiphy_free(struct wiphy *wiphy)
1183704232c2SJohannes Berg {
1184704232c2SJohannes Berg 	put_device(&wiphy->dev);
1185704232c2SJohannes Berg }
1186704232c2SJohannes Berg EXPORT_SYMBOL(wiphy_free);
1187704232c2SJohannes Berg 
11886f779a66SEmmanuel Grumbach void wiphy_rfkill_set_hw_state_reason(struct wiphy *wiphy, bool blocked,
11896f779a66SEmmanuel Grumbach 				      enum rfkill_hard_block_reasons reason)
11901f87f7d3SJohannes Berg {
_cfg80211_unregister_wdev(struct wireless_dev * wdev,bool unregister_netdev)1191f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
11921f87f7d3SJohannes Berg 
1193358ae888SEmmanuel Grumbach 	if (rfkill_set_hw_state_reason(wiphy->rfkill, blocked, reason))
11943cfe91c4SJohannes Berg 		schedule_work(&rdev->rfkill_block);
11951f87f7d3SJohannes Berg }
11966f779a66SEmmanuel Grumbach EXPORT_SYMBOL(wiphy_rfkill_set_hw_state_reason);
11971f87f7d3SJohannes Berg 
11982fe8ef10SJohannes Berg static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
11992fe8ef10SJohannes Berg 				      bool unregister_netdev)
120098104fdeSJohannes Berg {
1201f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
120237c20b2eSJohannes Berg 	struct cfg80211_cqm_config *cqm_config;
12037b0a0e3cSJohannes Berg 	unsigned int link_id;
120498104fdeSJohannes Berg 
120598104fdeSJohannes Berg 	ASSERT_RTNL();
1206a05829a7SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
120798104fdeSJohannes Berg 
12087f8ed01eSDenis Kenzior 	nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
12097f8ed01eSDenis Kenzior 
12102fe8ef10SJohannes Berg 	wdev->registered = false;
12112fe8ef10SJohannes Berg 
12122fe8ef10SJohannes Berg 	if (wdev->netdev) {
12132fe8ef10SJohannes Berg 		sysfs_remove_link(&wdev->netdev->dev.kobj, "phy80211");
12142fe8ef10SJohannes Berg 		if (unregister_netdev)
12152fe8ef10SJohannes Berg 			unregister_netdevice(wdev->netdev);
12162fe8ef10SJohannes Berg 	}
12172fe8ef10SJohannes Berg 
121898104fdeSJohannes Berg 	list_del_rcu(&wdev->list);
12192fe8ef10SJohannes Berg 	synchronize_net();
122098104fdeSJohannes Berg 	rdev->devlist_generation++;
122198104fdeSJohannes Berg 
122285dd3da4SJohannes Berg 	cfg80211_mlme_purge_registrations(wdev);
122385dd3da4SJohannes Berg 
122498104fdeSJohannes Berg 	switch (wdev->iftype) {
122598104fdeSJohannes Berg 	case NL80211_IFTYPE_P2P_DEVICE:
1226f9f47529SJohannes Berg 		cfg80211_stop_p2p_device(rdev, wdev);
122798104fdeSJohannes Berg 		break;
1228cb3b7d87SAyala Beker 	case NL80211_IFTYPE_NAN:
1229cb3b7d87SAyala Beker 		cfg80211_stop_nan(rdev, wdev);
1230cb3b7d87SAyala Beker 		break;
123198104fdeSJohannes Berg 	default:
123298104fdeSJohannes Berg 		break;
123398104fdeSJohannes Berg 	}
12344a4b8169SAndrew Zaborowski 
123585dd3da4SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
1236453431a5SWaiman Long 	kfree_sensitive(wdev->wext.keys);
123756cb31e1SStefan Bühler 	wdev->wext.keys = NULL;
123885dd3da4SJohannes Berg #endif
123937c20b2eSJohannes Berg 	wiphy_work_cancel(wdev->wiphy, &wdev->cqm_rssi_work);
124037c20b2eSJohannes Berg 	/* deleted from the list, so can't be found from nl80211 any more */
124137c20b2eSJohannes Berg 	cqm_config = rcu_access_pointer(wdev->cqm_config);
124237c20b2eSJohannes Berg 	kfree_rcu(cqm_config, rcu_head);
12436c44abb2SJohannes Berg 	RCU_INIT_POINTER(wdev->cqm_config, NULL);
12442fe8ef10SJohannes Berg 
12452fe8ef10SJohannes Berg 	/*
12462fe8ef10SJohannes Berg 	 * Ensure that all events have been processed and
12472fe8ef10SJohannes Berg 	 * freed.
12482fe8ef10SJohannes Berg 	 */
12492fe8ef10SJohannes Berg 	cfg80211_process_wdev_events(wdev);
12502fe8ef10SJohannes Berg 
12517b0a0e3cSJohannes Berg 	if (wdev->iftype == NL80211_IFTYPE_STATION ||
12527b0a0e3cSJohannes Berg 	    wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) {
12537b0a0e3cSJohannes Berg 		for (link_id = 0; link_id < ARRAY_SIZE(wdev->links); link_id++) {
12547b0a0e3cSJohannes Berg 			struct cfg80211_internal_bss *curbss;
12557b0a0e3cSJohannes Berg 
12567b0a0e3cSJohannes Berg 			curbss = wdev->links[link_id].client.current_bss;
12577b0a0e3cSJohannes Berg 
12587b0a0e3cSJohannes Berg 			if (WARN_ON(curbss)) {
12597b0a0e3cSJohannes Berg 				cfg80211_unhold_bss(curbss);
12607b0a0e3cSJohannes Berg 				cfg80211_put_bss(wdev->wiphy, &curbss->pub);
12617b0a0e3cSJohannes Berg 				wdev->links[link_id].client.current_bss = NULL;
cfg80211_unregister_wdev(struct wireless_dev * wdev)12622fe8ef10SJohannes Berg 			}
126398104fdeSJohannes Berg 		}
12647b0a0e3cSJohannes Berg 	}
12657b0a0e3cSJohannes Berg 
12667b0a0e3cSJohannes Berg 	wdev->connected = false;
12677b0a0e3cSJohannes Berg }
126885dd3da4SJohannes Berg 
126985dd3da4SJohannes Berg void cfg80211_unregister_wdev(struct wireless_dev *wdev)
127085dd3da4SJohannes Berg {
12712fe8ef10SJohannes Berg 	_cfg80211_unregister_wdev(wdev, true);
cfg80211_update_iface_num(struct cfg80211_registered_device * rdev,enum nl80211_iftype iftype,int num)127285dd3da4SJohannes Berg }
127398104fdeSJohannes Berg EXPORT_SYMBOL(cfg80211_unregister_wdev);
127498104fdeSJohannes Berg 
1275f1e3d556SJohannes Berg static const struct device_type wiphy_type = {
1276053a93ddSMarcel Holtmann 	.name	= "wlan",
1277053a93ddSMarcel Holtmann };
1278053a93ddSMarcel Holtmann 
1279dbbae26aSMichal Kazior void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
1280dbbae26aSMichal Kazior 			       enum nl80211_iftype iftype, int num)
1281dbbae26aSMichal Kazior {
__cfg80211_leave(struct cfg80211_registered_device * rdev,struct wireless_dev * wdev)1282a05829a7SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
1283dbbae26aSMichal Kazior 
1284dbbae26aSMichal Kazior 	rdev->num_running_ifaces += num;
1285dbbae26aSMichal Kazior 	if (iftype == NL80211_IFTYPE_MONITOR)
1286dbbae26aSMichal Kazior 		rdev->num_running_monitor_ifaces += num;
1287dbbae26aSMichal Kazior }
1288dbbae26aSMichal Kazior 
1289f04c2203SMichal Kazior void __cfg80211_leave(struct cfg80211_registered_device *rdev,
129081256969SStanislaw Gruszka 		      struct wireless_dev *wdev)
129181256969SStanislaw Gruszka {
129281256969SStanislaw Gruszka 	struct net_device *dev = wdev->netdev;
1293ca986ad9SArend Van Spriel 	struct cfg80211_sched_scan_request *pos, *tmp;
129481256969SStanislaw Gruszka 
1295a05829a7SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
1296f04c2203SMichal Kazior 	ASSERT_WDEV_LOCK(wdev);
129724d584d7SBarak Bercovitz 
12989bb7e0f2SJohannes Berg 	cfg80211_pmsr_wdev_down(wdev);
12999bb7e0f2SJohannes Berg 
1300a95bfb87SLorenzo Bianconi 	cfg80211_stop_background_radar_detection(wdev);
1301bc2dfc02SLorenzo Bianconi 
130281256969SStanislaw Gruszka 	switch (wdev->iftype) {
130381256969SStanislaw Gruszka 	case NL80211_IFTYPE_ADHOC:
1304f04c2203SMichal Kazior 		__cfg80211_leave_ibss(rdev, dev, true);
130581256969SStanislaw Gruszka 		break;
130681256969SStanislaw Gruszka 	case NL80211_IFTYPE_P2P_CLIENT:
130781256969SStanislaw Gruszka 	case NL80211_IFTYPE_STATION:
1308ca986ad9SArend Van Spriel 		list_for_each_entry_safe(pos, tmp, &rdev->sched_scan_req_list,
1309ca986ad9SArend Van Spriel 					 list) {
1310ca986ad9SArend Van Spriel 			if (dev == pos->dev)
1311ca986ad9SArend Van Spriel 				cfg80211_stop_sched_scan_req(rdev, pos, false);
1312ca986ad9SArend Van Spriel 		}
131381256969SStanislaw Gruszka 
131481256969SStanislaw Gruszka #ifdef CONFIG_CFG80211_WEXT
131581256969SStanislaw Gruszka 		kfree(wdev->wext.ie);
131681256969SStanislaw Gruszka 		wdev->wext.ie = NULL;
131781256969SStanislaw Gruszka 		wdev->wext.ie_len = 0;
131881256969SStanislaw Gruszka 		wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
131981256969SStanislaw Gruszka #endif
132083739b03SJohannes Berg 		cfg80211_disconnect(rdev, dev,
132181256969SStanislaw Gruszka 				    WLAN_REASON_DEAUTH_LEAVING, true);
132281256969SStanislaw Gruszka 		break;
132381256969SStanislaw Gruszka 	case NL80211_IFTYPE_MESH_POINT:
1324f04c2203SMichal Kazior 		__cfg80211_leave_mesh(rdev, dev);
132581256969SStanislaw Gruszka 		break;
132681256969SStanislaw Gruszka 	case NL80211_IFTYPE_AP:
132774418edeSJohannes Berg 	case NL80211_IFTYPE_P2P_GO:
13287b0a0e3cSJohannes Berg 		__cfg80211_stop_ap(rdev, dev, -1, true);
132981256969SStanislaw Gruszka 		break;
13306e0bd6c3SRostislav Lisovy 	case NL80211_IFTYPE_OCB:
13316e0bd6c3SRostislav Lisovy 		__cfg80211_leave_ocb(rdev, dev);
13326e0bd6c3SRostislav Lisovy 		break;
1333de4fcbadSJohannes Berg 	case NL80211_IFTYPE_P2P_DEVICE:
1334cb3b7d87SAyala Beker 	case NL80211_IFTYPE_NAN:
1335de4fcbadSJohannes Berg 		/* cannot happen, has no netdev */
1336de4fcbadSJohannes Berg 		break;
1337de4fcbadSJohannes Berg 	case NL80211_IFTYPE_AP_VLAN:
1338de4fcbadSJohannes Berg 	case NL80211_IFTYPE_MONITOR:
1339de4fcbadSJohannes Berg 		/* nothing to do */
1340de4fcbadSJohannes Berg 		break;
1341de4fcbadSJohannes Berg 	case NL80211_IFTYPE_UNSPECIFIED:
cfg80211_leave(struct cfg80211_registered_device * rdev,struct wireless_dev * wdev)1342e7e0517cSJohannes Berg 	case NL80211_IFTYPE_WDS:
1343de4fcbadSJohannes Berg 	case NUM_NL80211_IFTYPES:
1344de4fcbadSJohannes Berg 		/* invalid */
134581256969SStanislaw Gruszka 		break;
134681256969SStanislaw Gruszka 	}
134781256969SStanislaw Gruszka }
134881256969SStanislaw Gruszka 
1349f04c2203SMichal Kazior void cfg80211_leave(struct cfg80211_registered_device *rdev,
cfg80211_stop_iface(struct wiphy * wiphy,struct wireless_dev * wdev,gfp_t gfp)1350f04c2203SMichal Kazior 		    struct wireless_dev *wdev)
1351f04c2203SMichal Kazior {
1352f04c2203SMichal Kazior 	wdev_lock(wdev);
1353f04c2203SMichal Kazior 	__cfg80211_leave(rdev, wdev);
1354f04c2203SMichal Kazior 	wdev_unlock(wdev);
1355f04c2203SMichal Kazior }
1356f04c2203SMichal Kazior 
1357f04c2203SMichal Kazior void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
1358f04c2203SMichal Kazior 			 gfp_t gfp)
1359f04c2203SMichal Kazior {
1360f04c2203SMichal Kazior 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1361f04c2203SMichal Kazior 	struct cfg80211_event *ev;
1362f04c2203SMichal Kazior 	unsigned long flags;
1363f04c2203SMichal Kazior 
1364f04c2203SMichal Kazior 	trace_cfg80211_stop_iface(wiphy, wdev);
1365f04c2203SMichal Kazior 
1366f04c2203SMichal Kazior 	ev = kzalloc(sizeof(*ev), gfp);
1367f04c2203SMichal Kazior 	if (!ev)
1368f04c2203SMichal Kazior 		return;
1369f04c2203SMichal Kazior 
1370f04c2203SMichal Kazior 	ev->type = EVENT_STOPPED;
1371f04c2203SMichal Kazior 
cfg80211_init_wdev(struct wireless_dev * wdev)1372f04c2203SMichal Kazior 	spin_lock_irqsave(&wdev->event_lock, flags);
1373f04c2203SMichal Kazior 	list_add_tail(&ev->list, &wdev->event_list);
1374f04c2203SMichal Kazior 	spin_unlock_irqrestore(&wdev->event_lock, flags);
1375f04c2203SMichal Kazior 	queue_work(cfg80211_wq, &rdev->event_work);
1376f04c2203SMichal Kazior }
1377f04c2203SMichal Kazior EXPORT_SYMBOL(cfg80211_stop_iface);
1378f04c2203SMichal Kazior 
13799bdaf3b9SJohannes Berg void cfg80211_init_wdev(struct wireless_dev *wdev)
1380e4d4216eSJohannes Berg {
1381e4d4216eSJohannes Berg 	mutex_init(&wdev->mtx);
1382e4d4216eSJohannes Berg 	INIT_LIST_HEAD(&wdev->event_list);
1383e4d4216eSJohannes Berg 	spin_lock_init(&wdev->event_lock);
1384e4d4216eSJohannes Berg 	INIT_LIST_HEAD(&wdev->mgmt_registrations);
13859bb7e0f2SJohannes Berg 	INIT_LIST_HEAD(&wdev->pmsr_list);
13869bb7e0f2SJohannes Berg 	spin_lock_init(&wdev->pmsr_lock);
13879bb7e0f2SJohannes Berg 	INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
1388e4d4216eSJohannes Berg 
13899bdaf3b9SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
13909bdaf3b9SJohannes Berg 	wdev->wext.default_key = -1;
13919bdaf3b9SJohannes Berg 	wdev->wext.default_mgmt_key = -1;
13929bdaf3b9SJohannes Berg 	wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
13939bdaf3b9SJohannes Berg #endif
13949bdaf3b9SJohannes Berg 
139537c20b2eSJohannes Berg 	wiphy_work_init(&wdev->cqm_rssi_work, cfg80211_cqm_rssi_notify_work);
139637c20b2eSJohannes Berg 
13979bdaf3b9SJohannes Berg 	if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT)
13989bdaf3b9SJohannes Berg 		wdev->ps = true;
13999bdaf3b9SJohannes Berg 	else
14009bdaf3b9SJohannes Berg 		wdev->ps = false;
14019bdaf3b9SJohannes Berg 	/* allow mac80211 to determine the timeout */
14029bdaf3b9SJohannes Berg 	wdev->ps_timeout = -1;
14039bdaf3b9SJohannes Berg 
14049bdaf3b9SJohannes Berg 	if ((wdev->iftype == NL80211_IFTYPE_STATION ||
cfg80211_register_wdev(struct cfg80211_registered_device * rdev,struct wireless_dev * wdev)14059bdaf3b9SJohannes Berg 	     wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
14069bdaf3b9SJohannes Berg 	     wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
14079bdaf3b9SJohannes Berg 		wdev->netdev->priv_flags |= IFF_DONT_BRIDGE;
14089bdaf3b9SJohannes Berg 
14099bdaf3b9SJohannes Berg 	INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
14109bdaf3b9SJohannes Berg }
14119bdaf3b9SJohannes Berg 
14129bdaf3b9SJohannes Berg void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
14139bdaf3b9SJohannes Berg 			    struct wireless_dev *wdev)
14149bdaf3b9SJohannes Berg {
1415a05829a7SJohannes Berg 	ASSERT_RTNL();
1416a05829a7SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
1417a05829a7SJohannes Berg 
1418e4d4216eSJohannes Berg 	/*
1419e4d4216eSJohannes Berg 	 * We get here also when the interface changes network namespaces,
1420e4d4216eSJohannes Berg 	 * as it's registered into the new one, but we don't want it to
1421e4d4216eSJohannes Berg 	 * change ID in that case. Checking if the ID is already assigned
1422e4d4216eSJohannes Berg 	 * works, because 0 isn't considered a valid ID and the memory is
1423e4d4216eSJohannes Berg 	 * 0-initialized.
1424e4d4216eSJohannes Berg 	 */
1425e4d4216eSJohannes Berg 	if (!wdev->identifier)
1426e4d4216eSJohannes Berg 		wdev->identifier = ++rdev->wdev_id;
1427e4d4216eSJohannes Berg 	list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list);
1428e4d4216eSJohannes Berg 	rdev->devlist_generation++;
14292fe8ef10SJohannes Berg 	wdev->registered = true;
1430ec8f170bSJohannes Berg 
143143076c1eSJohannes Berg 	if (wdev->netdev &&
cfg80211_register_netdevice(struct net_device * dev)143243076c1eSJohannes Berg 	    sysfs_create_link(&wdev->netdev->dev.kobj, &rdev->wiphy.dev.kobj,
143343076c1eSJohannes Berg 			      "phy80211"))
143443076c1eSJohannes Berg 		pr_err("failed to add phy80211 symlink to netdev!\n");
143543076c1eSJohannes Berg 
1436ec8f170bSJohannes Berg 	nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
1437e4d4216eSJohannes Berg }
1438e4d4216eSJohannes Berg 
14392fe8ef10SJohannes Berg int cfg80211_register_netdevice(struct net_device *dev)
14402fe8ef10SJohannes Berg {
14412fe8ef10SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
14422fe8ef10SJohannes Berg 	struct cfg80211_registered_device *rdev;
14432fe8ef10SJohannes Berg 	int ret;
14442fe8ef10SJohannes Berg 
14452fe8ef10SJohannes Berg 	ASSERT_RTNL();
14462fe8ef10SJohannes Berg 
14472fe8ef10SJohannes Berg 	if (WARN_ON(!wdev))
14482fe8ef10SJohannes Berg 		return -EINVAL;
14492fe8ef10SJohannes Berg 
14502fe8ef10SJohannes Berg 	rdev = wiphy_to_rdev(wdev->wiphy);
14512fe8ef10SJohannes Berg 
14522fe8ef10SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
14532fe8ef10SJohannes Berg 
14542fe8ef10SJohannes Berg 	/* we'll take care of this */
14552fe8ef10SJohannes Berg 	wdev->registered = true;
145640c575d1SJohannes Berg 	wdev->registering = true;
14572fe8ef10SJohannes Berg 	ret = register_netdevice(dev);
14582fe8ef10SJohannes Berg 	if (ret)
14592fe8ef10SJohannes Berg 		goto out;
14602fe8ef10SJohannes Berg 
14612fe8ef10SJohannes Berg 	cfg80211_register_wdev(rdev, wdev);
14622fe8ef10SJohannes Berg 	ret = 0;
14632fe8ef10SJohannes Berg out:
cfg80211_netdev_notifier_call(struct notifier_block * nb,unsigned long state,void * ptr)146440c575d1SJohannes Berg 	wdev->registering = false;
14652fe8ef10SJohannes Berg 	if (ret)
14662fe8ef10SJohannes Berg 		wdev->registered = false;
14672fe8ef10SJohannes Berg 	return ret;
14682fe8ef10SJohannes Berg }
14692fe8ef10SJohannes Berg EXPORT_SYMBOL(cfg80211_register_netdevice);
14702fe8ef10SJohannes Berg 
1471704232c2SJohannes Berg static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
1472351638e7SJiri Pirko 					 unsigned long state, void *ptr)
1473704232c2SJohannes Berg {
1474351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
14752a783c13SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
1476704232c2SJohannes Berg 	struct cfg80211_registered_device *rdev;
1477ca986ad9SArend Van Spriel 	struct cfg80211_sched_scan_request *pos, *tmp;
1478704232c2SJohannes Berg 
14792a783c13SJohannes Berg 	if (!wdev)
14801f87f7d3SJohannes Berg 		return NOTIFY_DONE;
1481704232c2SJohannes Berg 
1482f26cbf40SZhao, Gang 	rdev = wiphy_to_rdev(wdev->wiphy);
1483704232c2SJohannes Berg 
14842a783c13SJohannes Berg 	WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED);
148560719ffdSJohannes Berg 
1486704232c2SJohannes Berg 	switch (state) {
1487053a93ddSMarcel Holtmann 	case NETDEV_POST_INIT:
1488053a93ddSMarcel Holtmann 		SET_NETDEV_DEVTYPE(dev, &wiphy_type);
14899bdaf3b9SJohannes Berg 		wdev->netdev = dev;
14909bdaf3b9SJohannes Berg 		/* can only change netns with wiphy */
14919bdaf3b9SJohannes Berg 		dev->features |= NETIF_F_NETNS_LOCAL;
14929bdaf3b9SJohannes Berg 
14939bdaf3b9SJohannes Berg 		cfg80211_init_wdev(wdev);
1494053a93ddSMarcel Holtmann 		break;
1495704232c2SJohannes Berg 	case NETDEV_REGISTER:
1496a05829a7SJohannes Berg 		if (!wdev->registered) {
1497a05829a7SJohannes Berg 			wiphy_lock(&rdev->wiphy);
14989bdaf3b9SJohannes Berg 			cfg80211_register_wdev(rdev, wdev);
1499a05829a7SJohannes Berg 			wiphy_unlock(&rdev->wiphy);
1500a05829a7SJohannes Berg 		}
1501704232c2SJohannes Berg 		break;
15022fe8ef10SJohannes Berg 	case NETDEV_UNREGISTER:
15032fe8ef10SJohannes Berg 		/*
15042fe8ef10SJohannes Berg 		 * It is possible to get NETDEV_UNREGISTER multiple times,
15052fe8ef10SJohannes Berg 		 * so check wdev->registered.
15062fe8ef10SJohannes Berg 		 */
150740c575d1SJohannes Berg 		if (wdev->registered && !wdev->registering) {
1508a05829a7SJohannes Berg 			wiphy_lock(&rdev->wiphy);
15092fe8ef10SJohannes Berg 			_cfg80211_unregister_wdev(wdev, false);
1510a05829a7SJohannes Berg 			wiphy_unlock(&rdev->wiphy);
1511a05829a7SJohannes Berg 		}
15122fe8ef10SJohannes Berg 		break;
151304a773adSJohannes Berg 	case NETDEV_GOING_DOWN:
1514a05829a7SJohannes Berg 		wiphy_lock(&rdev->wiphy);
151581256969SStanislaw Gruszka 		cfg80211_leave(rdev, wdev);
1516cdf0a0a8SJohannes Berg 		cfg80211_remove_links(wdev);
1517a05829a7SJohannes Berg 		wiphy_unlock(&rdev->wiphy);
1518e9da6df7SJohannes Berg 		/* since we just did cfg80211_leave() nothing to do there */
1519e9da6df7SJohannes Berg 		cancel_work_sync(&wdev->disconnect_wk);
15200dcb84edSJohannes Berg 		cancel_work_sync(&wdev->pmsr_free_wk);
152101a0ac41SJohannes Berg 		break;
152201a0ac41SJohannes Berg 	case NETDEV_DOWN:
1523a05829a7SJohannes Berg 		wiphy_lock(&rdev->wiphy);
1524dbbae26aSMichal Kazior 		cfg80211_update_iface_num(rdev, wdev->iftype, -1);
1525a617302cSJohannes Berg 		if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
1526c8cb5b85STova Mussai 			if (WARN_ON(!rdev->scan_req->notified &&
1527c8cb5b85STova Mussai 				    (!rdev->int_scan_req ||
1528c8cb5b85STova Mussai 				     !rdev->int_scan_req->notified)))
15291d76250bSAvraham Stern 				rdev->scan_req->info.aborted = true;
1530f9d15d16SJohannes Berg 			___cfg80211_scan_done(rdev, false);
1531a617302cSJohannes Berg 		}
15325fe231e8SJohannes Berg 
1533ca986ad9SArend Van Spriel 		list_for_each_entry_safe(pos, tmp,
1534ca986ad9SArend Van Spriel 					 &rdev->sched_scan_req_list, list) {
1535d25d062fSzhong jiang 			if (WARN_ON(pos->dev == wdev->netdev))
1536ca986ad9SArend Van Spriel 				cfg80211_stop_sched_scan_req(rdev, pos, false);
15375fe231e8SJohannes Berg 		}
15385fe231e8SJohannes Berg 
15395fe231e8SJohannes Berg 		rdev->opencount--;
1540a05829a7SJohannes Berg 		wiphy_unlock(&rdev->wiphy);
15415fe231e8SJohannes Berg 		wake_up(&rdev->dev_wait);
1542b23aa676SSamuel Ortiz 		break;
154304a773adSJohannes Berg 	case NETDEV_UP:
1544a05829a7SJohannes Berg 		wiphy_lock(&rdev->wiphy);
15454290cb4bSJohannes Berg 		cfg80211_update_iface_num(rdev, wdev->iftype, 1);
1546667503ddSJohannes Berg 		wdev_lock(wdev);
1547f2129354SJohannes Berg 		switch (wdev->iftype) {
154829cbe68cSJohannes Berg #ifdef CONFIG_CFG80211_WEXT
1549f2129354SJohannes Berg 		case NL80211_IFTYPE_ADHOC:
1550fffd0934SJohannes Berg 			cfg80211_ibss_wext_join(rdev, wdev);
155104a773adSJohannes Berg 			break;
1552f2129354SJohannes Berg 		case NL80211_IFTYPE_STATION:
1553fffd0934SJohannes Berg 			cfg80211_mgd_wext_connect(rdev, wdev);
155404a773adSJohannes Berg 			break;
155529cbe68cSJohannes Berg #endif
1556c80d545dSJavier Cardona #ifdef CONFIG_MAC80211_MESH
155729cbe68cSJohannes Berg 		case NL80211_IFTYPE_MESH_POINT:
1558c80d545dSJavier Cardona 			{
155929cbe68cSJohannes Berg 				/* backward compat code... */
1560c80d545dSJavier Cardona 				struct mesh_setup setup;
1561c80d545dSJavier Cardona 				memcpy(&setup, &default_mesh_setup,
1562c80d545dSJavier Cardona 						sizeof(setup));
1563c80d545dSJavier Cardona 				 /* back compat only needed for mesh_id */
15647b0a0e3cSJohannes Berg 				setup.mesh_id = wdev->u.mesh.id;
15657b0a0e3cSJohannes Berg 				setup.mesh_id_len = wdev->u.mesh.id_up_len;
15667b0a0e3cSJohannes Berg 				if (wdev->u.mesh.id_up_len)
1567c80d545dSJavier Cardona 					__cfg80211_join_mesh(rdev, dev,
1568c80d545dSJavier Cardona 							&setup,
156929cbe68cSJohannes Berg 							&default_mesh_config);
157029cbe68cSJohannes Berg 				break;
1571c80d545dSJavier Cardona 			}
1572c80d545dSJavier Cardona #endif
1573f2129354SJohannes Berg 		default:
1574f2129354SJohannes Berg 			break;
1575f2129354SJohannes Berg 		}
1576667503ddSJohannes Berg 		wdev_unlock(wdev);
1577ad002395SJohannes Berg 		rdev->opencount++;
1578bf6a0579SJuuso Oikarinen 
1579bf6a0579SJuuso Oikarinen 		/*
1580bf6a0579SJuuso Oikarinen 		 * Configure power management to the driver here so that its
1581bf6a0579SJuuso Oikarinen 		 * correctly set also after interface type changes etc.
1582bf6a0579SJuuso Oikarinen 		 */
15835966f2ddSEliad Peller 		if ((wdev->iftype == NL80211_IFTYPE_STATION ||
15845966f2ddSEliad Peller 		     wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
1585d4f29978SJohannes Berg 		    rdev->ops->set_power_mgmt &&
1586d4f29978SJohannes Berg 		    rdev_set_power_mgmt(rdev, dev, wdev->ps,
1587bf6a0579SJuuso Oikarinen 					wdev->ps_timeout)) {
1588bf6a0579SJuuso Oikarinen 			/* assume this means it's off */
1589bf6a0579SJuuso Oikarinen 			wdev->ps = false;
1590bf6a0579SJuuso Oikarinen 		}
1591a05829a7SJohannes Berg 		wiphy_unlock(&rdev->wiphy);
15922a783c13SJohannes Berg 		break;
15931f87f7d3SJohannes Berg 	case NETDEV_PRE_UP:
1594e6f40511SManikanta Pubbisetty 		if (!cfg80211_iftype_allowed(wdev->wiphy, wdev->iftype,
1595e6f40511SManikanta Pubbisetty 					     wdev->use_4addr, 0))
15960b20633dSJohannes Berg 			return notifier_from_errno(-EOPNOTSUPP);
159733d915d9SManikanta Pubbisetty 
1598358ae888SEmmanuel Grumbach 		if (rfkill_blocked(rdev->wiphy.rfkill))
1599b6a55015SLuciano Coelho 			return notifier_from_errno(-ERFKILL);
16001f87f7d3SJohannes Berg 		break;
16016784c7dbSZhao, Gang 	default:
16026784c7dbSZhao, Gang 		return NOTIFY_DONE;
1603704232c2SJohannes Berg 	}
1604704232c2SJohannes Berg 
1605cb150b9dSJohannes Berg 	wireless_nlevent_flush();
1606cb150b9dSJohannes Berg 
cfg80211_pernet_exit(struct net * net)16076784c7dbSZhao, Gang 	return NOTIFY_OK;
1608704232c2SJohannes Berg }
1609704232c2SJohannes Berg 
1610704232c2SJohannes Berg static struct notifier_block cfg80211_netdev_notifier = {
1611704232c2SJohannes Berg 	.notifier_call = cfg80211_netdev_notifier_call,
1612704232c2SJohannes Berg };
1613704232c2SJohannes Berg 
1614463d0183SJohannes Berg static void __net_exit cfg80211_pernet_exit(struct net *net)
1615463d0183SJohannes Berg {
1616463d0183SJohannes Berg 	struct cfg80211_registered_device *rdev;
1617463d0183SJohannes Berg 
1618463d0183SJohannes Berg 	rtnl_lock();
1619463d0183SJohannes Berg 	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1620463d0183SJohannes Berg 		if (net_eq(wiphy_net(&rdev->wiphy), net))
1621463d0183SJohannes Berg 			WARN_ON(cfg80211_switch_netns(rdev, &init_net));
1622463d0183SJohannes Berg 	}
wiphy_work_queue(struct wiphy * wiphy,struct wiphy_work * work)1623463d0183SJohannes Berg 	rtnl_unlock();
1624463d0183SJohannes Berg }
1625463d0183SJohannes Berg 
1626463d0183SJohannes Berg static struct pernet_operations cfg80211_pernet_ops = {
1627463d0183SJohannes Berg 	.exit = cfg80211_pernet_exit,
1628463d0183SJohannes Berg };
1629463d0183SJohannes Berg 
1630a3ee4dc8SJohannes Berg void wiphy_work_queue(struct wiphy *wiphy, struct wiphy_work *work)
1631a3ee4dc8SJohannes Berg {
1632a3ee4dc8SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1633a3ee4dc8SJohannes Berg 	unsigned long flags;
1634a3ee4dc8SJohannes Berg 
1635a3ee4dc8SJohannes Berg 	spin_lock_irqsave(&rdev->wiphy_work_lock, flags);
1636a3ee4dc8SJohannes Berg 	if (list_empty(&work->entry))
wiphy_work_cancel(struct wiphy * wiphy,struct wiphy_work * work)1637a3ee4dc8SJohannes Berg 		list_add_tail(&work->entry, &rdev->wiphy_work_list);
1638a3ee4dc8SJohannes Berg 	spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags);
1639a3ee4dc8SJohannes Berg 
164091d20ab9SJohannes Berg 	queue_work(system_unbound_wq, &rdev->wiphy_work);
1641a3ee4dc8SJohannes Berg }
1642a3ee4dc8SJohannes Berg EXPORT_SYMBOL_GPL(wiphy_work_queue);
1643a3ee4dc8SJohannes Berg 
1644a3ee4dc8SJohannes Berg void wiphy_work_cancel(struct wiphy *wiphy, struct wiphy_work *work)
1645a3ee4dc8SJohannes Berg {
1646a3ee4dc8SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1647a3ee4dc8SJohannes Berg 	unsigned long flags;
1648a3ee4dc8SJohannes Berg 
1649a3ee4dc8SJohannes Berg 	lockdep_assert_held(&wiphy->mtx);
1650a3ee4dc8SJohannes Berg 
wiphy_work_flush(struct wiphy * wiphy,struct wiphy_work * work)1651a3ee4dc8SJohannes Berg 	spin_lock_irqsave(&rdev->wiphy_work_lock, flags);
1652a3ee4dc8SJohannes Berg 	if (!list_empty(&work->entry))
1653a3ee4dc8SJohannes Berg 		list_del_init(&work->entry);
1654a3ee4dc8SJohannes Berg 	spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags);
1655a3ee4dc8SJohannes Berg }
1656a3ee4dc8SJohannes Berg EXPORT_SYMBOL_GPL(wiphy_work_cancel);
1657a3ee4dc8SJohannes Berg 
16585d9eefa2SJohannes Berg void wiphy_work_flush(struct wiphy *wiphy, struct wiphy_work *work)
16595d9eefa2SJohannes Berg {
16605d9eefa2SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
16615d9eefa2SJohannes Berg 	unsigned long flags;
16625d9eefa2SJohannes Berg 	bool run;
16635d9eefa2SJohannes Berg 
16645d9eefa2SJohannes Berg 	spin_lock_irqsave(&rdev->wiphy_work_lock, flags);
16655d9eefa2SJohannes Berg 	run = !work || !list_empty(&work->entry);
wiphy_delayed_work_timer(struct timer_list * t)16665d9eefa2SJohannes Berg 	spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags);
16675d9eefa2SJohannes Berg 
16685d9eefa2SJohannes Berg 	if (run)
16695d9eefa2SJohannes Berg 		cfg80211_process_wiphy_works(rdev, work);
16705d9eefa2SJohannes Berg }
16715d9eefa2SJohannes Berg EXPORT_SYMBOL_GPL(wiphy_work_flush);
16725d9eefa2SJohannes Berg 
1673a3ee4dc8SJohannes Berg void wiphy_delayed_work_timer(struct timer_list *t)
wiphy_delayed_work_queue(struct wiphy * wiphy,struct wiphy_delayed_work * dwork,unsigned long delay)1674a3ee4dc8SJohannes Berg {
1675a3ee4dc8SJohannes Berg 	struct wiphy_delayed_work *dwork = from_timer(dwork, t, timer);
1676a3ee4dc8SJohannes Berg 
1677a3ee4dc8SJohannes Berg 	wiphy_work_queue(dwork->wiphy, &dwork->work);
1678a3ee4dc8SJohannes Berg }
1679a3ee4dc8SJohannes Berg EXPORT_SYMBOL(wiphy_delayed_work_timer);
1680a3ee4dc8SJohannes Berg 
1681a3ee4dc8SJohannes Berg void wiphy_delayed_work_queue(struct wiphy *wiphy,
1682a3ee4dc8SJohannes Berg 			      struct wiphy_delayed_work *dwork,
1683a3ee4dc8SJohannes Berg 			      unsigned long delay)
1684a3ee4dc8SJohannes Berg {
1685a3ee4dc8SJohannes Berg 	if (!delay) {
16866585c74bSJohannes Berg 		del_timer(&dwork->timer);
1687a3ee4dc8SJohannes Berg 		wiphy_work_queue(wiphy, &dwork->work);
1688a3ee4dc8SJohannes Berg 		return;
wiphy_delayed_work_cancel(struct wiphy * wiphy,struct wiphy_delayed_work * dwork)1689a3ee4dc8SJohannes Berg 	}
1690a3ee4dc8SJohannes Berg 
1691a3ee4dc8SJohannes Berg 	dwork->wiphy = wiphy;
1692a3ee4dc8SJohannes Berg 	mod_timer(&dwork->timer, jiffies + delay);
1693a3ee4dc8SJohannes Berg }
1694a3ee4dc8SJohannes Berg EXPORT_SYMBOL_GPL(wiphy_delayed_work_queue);
1695a3ee4dc8SJohannes Berg 
1696a3ee4dc8SJohannes Berg void wiphy_delayed_work_cancel(struct wiphy *wiphy,
1697a3ee4dc8SJohannes Berg 			       struct wiphy_delayed_work *dwork)
1698a3ee4dc8SJohannes Berg {
wiphy_delayed_work_flush(struct wiphy * wiphy,struct wiphy_delayed_work * dwork)1699a3ee4dc8SJohannes Berg 	lockdep_assert_held(&wiphy->mtx);
1700a3ee4dc8SJohannes Berg 
1701a3ee4dc8SJohannes Berg 	del_timer_sync(&dwork->timer);
1702a3ee4dc8SJohannes Berg 	wiphy_work_cancel(wiphy, &dwork->work);
1703a3ee4dc8SJohannes Berg }
1704a3ee4dc8SJohannes Berg EXPORT_SYMBOL_GPL(wiphy_delayed_work_cancel);
1705a3ee4dc8SJohannes Berg 
17065d9eefa2SJohannes Berg void wiphy_delayed_work_flush(struct wiphy *wiphy,
17075d9eefa2SJohannes Berg 			      struct wiphy_delayed_work *dwork)
17085d9eefa2SJohannes Berg {
cfg80211_init(void)17095d9eefa2SJohannes Berg 	lockdep_assert_held(&wiphy->mtx);
17105d9eefa2SJohannes Berg 
17115d9eefa2SJohannes Berg 	del_timer_sync(&dwork->timer);
17125d9eefa2SJohannes Berg 	wiphy_work_flush(wiphy, &dwork->work);
17135d9eefa2SJohannes Berg }
17145d9eefa2SJohannes Berg EXPORT_SYMBOL_GPL(wiphy_delayed_work_flush);
17155d9eefa2SJohannes Berg 
1716463d0183SJohannes Berg static int __init cfg80211_init(void)
1717704232c2SJohannes Berg {
1718b2e1b302SLuis R. Rodriguez 	int err;
1719b2e1b302SLuis R. Rodriguez 
1720463d0183SJohannes Berg 	err = register_pernet_device(&cfg80211_pernet_ops);
1721463d0183SJohannes Berg 	if (err)
1722463d0183SJohannes Berg 		goto out_fail_pernet;
1723463d0183SJohannes Berg 
1724b2e1b302SLuis R. Rodriguez 	err = wiphy_sysfs_init();
1725704232c2SJohannes Berg 	if (err)
1726704232c2SJohannes Berg 		goto out_fail_sysfs;
1727704232c2SJohannes Berg 
1728704232c2SJohannes Berg 	err = register_netdevice_notifier(&cfg80211_netdev_notifier);
1729704232c2SJohannes Berg 	if (err)
1730704232c2SJohannes Berg 		goto out_fail_notifier;
1731704232c2SJohannes Berg 
173255682965SJohannes Berg 	err = nl80211_init();
173355682965SJohannes Berg 	if (err)
173455682965SJohannes Berg 		goto out_fail_nl80211;
173555682965SJohannes Berg 
1736704232c2SJohannes Berg 	ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
1737704232c2SJohannes Berg 
1738b2e1b302SLuis R. Rodriguez 	err = regulatory_init();
1739b2e1b302SLuis R. Rodriguez 	if (err)
1740b2e1b302SLuis R. Rodriguez 		goto out_fail_reg;
1741b2e1b302SLuis R. Rodriguez 
1742e4819013SBhaktipriya Shridhar 	cfg80211_wq = alloc_ordered_workqueue("cfg80211", WQ_MEM_RECLAIM);
1743f00f188fSWei Yongjun 	if (!cfg80211_wq) {
1744f00f188fSWei Yongjun 		err = -ENOMEM;
1745e60d7443SAlban Browaeys 		goto out_fail_wq;
1746f00f188fSWei Yongjun 	}
1747e60d7443SAlban Browaeys 
1748704232c2SJohannes Berg 	return 0;
1749704232c2SJohannes Berg 
1750e60d7443SAlban Browaeys out_fail_wq:
1751e60d7443SAlban Browaeys 	regulatory_exit();
1752b2e1b302SLuis R. Rodriguez out_fail_reg:
1753b2e1b302SLuis R. Rodriguez 	debugfs_remove(ieee80211_debugfs_dir);
175481daf735SJunjie Mao 	nl80211_exit();
175555682965SJohannes Berg out_fail_nl80211:
175655682965SJohannes Berg 	unregister_netdevice_notifier(&cfg80211_netdev_notifier);
1757704232c2SJohannes Berg out_fail_notifier:
1758704232c2SJohannes Berg 	wiphy_sysfs_exit();
cfg80211_exit(void)1759704232c2SJohannes Berg out_fail_sysfs:
1760463d0183SJohannes Berg 	unregister_pernet_device(&cfg80211_pernet_ops);
1761463d0183SJohannes Berg out_fail_pernet:
1762704232c2SJohannes Berg 	return err;
1763704232c2SJohannes Berg }
1764007f6c5eSJohannes Berg fs_initcall(cfg80211_init);
1765704232c2SJohannes Berg 
1766f884e387SUwe Kleine-König static void __exit cfg80211_exit(void)
1767704232c2SJohannes Berg {
1768704232c2SJohannes Berg 	debugfs_remove(ieee80211_debugfs_dir);
176955682965SJohannes Berg 	nl80211_exit();
1770704232c2SJohannes Berg 	unregister_netdevice_notifier(&cfg80211_netdev_notifier);
1771704232c2SJohannes Berg 	wiphy_sysfs_exit();
1772b2e1b302SLuis R. Rodriguez 	regulatory_exit();
1773463d0183SJohannes Berg 	unregister_pernet_device(&cfg80211_pernet_ops);
1774e60d7443SAlban Browaeys 	destroy_workqueue(cfg80211_wq);
1775704232c2SJohannes Berg }
1776704232c2SJohannes Berg module_exit(cfg80211_exit);
1777