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(®->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