162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This is the linux wireless configuration interface. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> 662306a36Sopenharmony_ci * Copyright 2013-2014 Intel Mobile Communications GmbH 762306a36Sopenharmony_ci * Copyright 2015-2017 Intel Deutschland GmbH 862306a36Sopenharmony_ci * Copyright (C) 2018-2022 Intel Corporation 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/if.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/list.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/nl80211.h> 1962306a36Sopenharmony_ci#include <linux/debugfs.h> 2062306a36Sopenharmony_ci#include <linux/notifier.h> 2162306a36Sopenharmony_ci#include <linux/device.h> 2262306a36Sopenharmony_ci#include <linux/etherdevice.h> 2362306a36Sopenharmony_ci#include <linux/rtnetlink.h> 2462306a36Sopenharmony_ci#include <linux/sched.h> 2562306a36Sopenharmony_ci#include <net/genetlink.h> 2662306a36Sopenharmony_ci#include <net/cfg80211.h> 2762306a36Sopenharmony_ci#include "nl80211.h" 2862306a36Sopenharmony_ci#include "core.h" 2962306a36Sopenharmony_ci#include "sysfs.h" 3062306a36Sopenharmony_ci#include "debugfs.h" 3162306a36Sopenharmony_ci#include "wext-compat.h" 3262306a36Sopenharmony_ci#include "rdev-ops.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* name for sysfs, %d is appended */ 3562306a36Sopenharmony_ci#define PHY_NAME "phy" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciMODULE_AUTHOR("Johannes Berg"); 3862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3962306a36Sopenharmony_ciMODULE_DESCRIPTION("wireless configuration support"); 4062306a36Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* RCU-protected (and RTNL for writers) */ 4362306a36Sopenharmony_ciLIST_HEAD(cfg80211_rdev_list); 4462306a36Sopenharmony_ciint cfg80211_rdev_list_generation; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* for debugfs */ 4762306a36Sopenharmony_cistatic struct dentry *ieee80211_debugfs_dir; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* for the cleanup, scan and event works */ 5062306a36Sopenharmony_cistruct workqueue_struct *cfg80211_wq; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic bool cfg80211_disable_40mhz_24ghz; 5362306a36Sopenharmony_cimodule_param(cfg80211_disable_40mhz_24ghz, bool, 0644); 5462306a36Sopenharmony_ciMODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, 5562306a36Sopenharmony_ci "Disable 40MHz support in the 2.4GHz band"); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct cfg80211_registered_device *result = NULL, *rdev; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci ASSERT_RTNL(); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci list_for_each_entry(rdev, &cfg80211_rdev_list, list) { 6462306a36Sopenharmony_ci if (rdev->wiphy_idx == wiphy_idx) { 6562306a36Sopenharmony_ci result = rdev; 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return result; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciint get_wiphy_idx(struct wiphy *wiphy) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return rdev->wiphy_idx; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ASSERT_RTNL(); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); 8762306a36Sopenharmony_ci if (!rdev) 8862306a36Sopenharmony_ci return NULL; 8962306a36Sopenharmony_ci return &rdev->wiphy; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev, 9362306a36Sopenharmony_ci const char *newname) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct cfg80211_registered_device *rdev2; 9662306a36Sopenharmony_ci int wiphy_idx, taken = -1, digits; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci ASSERT_RTNL(); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (strlen(newname) > NL80211_WIPHY_NAME_MAXLEN) 10162306a36Sopenharmony_ci return -EINVAL; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* prohibit calling the thing phy%d when %d is not its number */ 10462306a36Sopenharmony_ci sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); 10562306a36Sopenharmony_ci if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) { 10662306a36Sopenharmony_ci /* count number of places needed to print wiphy_idx */ 10762306a36Sopenharmony_ci digits = 1; 10862306a36Sopenharmony_ci while (wiphy_idx /= 10) 10962306a36Sopenharmony_ci digits++; 11062306a36Sopenharmony_ci /* 11162306a36Sopenharmony_ci * deny the name if it is phy<idx> where <idx> is printed 11262306a36Sopenharmony_ci * without leading zeroes. taken == strlen(newname) here 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci if (taken == strlen(PHY_NAME) + digits) 11562306a36Sopenharmony_ci return -EINVAL; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Ensure another device does not already have this name. */ 11962306a36Sopenharmony_ci list_for_each_entry(rdev2, &cfg80211_rdev_list, list) 12062306a36Sopenharmony_ci if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0) 12162306a36Sopenharmony_ci return -EINVAL; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciint cfg80211_dev_rename(struct cfg80211_registered_device *rdev, 12762306a36Sopenharmony_ci char *newname) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int result; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci ASSERT_RTNL(); 13262306a36Sopenharmony_ci lockdep_assert_wiphy(&rdev->wiphy); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* Ignore nop renames */ 13562306a36Sopenharmony_ci if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0) 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci result = cfg80211_dev_check_name(rdev, newname); 13962306a36Sopenharmony_ci if (result < 0) 14062306a36Sopenharmony_ci return result; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci result = device_rename(&rdev->wiphy.dev, newname); 14362306a36Sopenharmony_ci if (result) 14462306a36Sopenharmony_ci return result; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(rdev->wiphy.debugfsdir)) 14762306a36Sopenharmony_ci debugfs_rename(rdev->wiphy.debugfsdir->d_parent, 14862306a36Sopenharmony_ci rdev->wiphy.debugfsdir, 14962306a36Sopenharmony_ci rdev->wiphy.debugfsdir->d_parent, newname); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciint cfg80211_switch_netns(struct cfg80211_registered_device *rdev, 15762306a36Sopenharmony_ci struct net *net) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct wireless_dev *wdev; 16062306a36Sopenharmony_ci int err = 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK)) 16362306a36Sopenharmony_ci return -EOPNOTSUPP; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 16662306a36Sopenharmony_ci if (!wdev->netdev) 16762306a36Sopenharmony_ci continue; 16862306a36Sopenharmony_ci wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; 16962306a36Sopenharmony_ci err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); 17062306a36Sopenharmony_ci if (err) 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci wdev->netdev->features |= NETIF_F_NETNS_LOCAL; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (err) { 17662306a36Sopenharmony_ci /* failed -- clean up to old netns */ 17762306a36Sopenharmony_ci net = wiphy_net(&rdev->wiphy); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci list_for_each_entry_continue_reverse(wdev, 18062306a36Sopenharmony_ci &rdev->wiphy.wdev_list, 18162306a36Sopenharmony_ci list) { 18262306a36Sopenharmony_ci if (!wdev->netdev) 18362306a36Sopenharmony_ci continue; 18462306a36Sopenharmony_ci wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; 18562306a36Sopenharmony_ci err = dev_change_net_namespace(wdev->netdev, net, 18662306a36Sopenharmony_ci "wlan%d"); 18762306a36Sopenharmony_ci WARN_ON(err); 18862306a36Sopenharmony_ci wdev->netdev->features |= NETIF_F_NETNS_LOCAL; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return err; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 19562306a36Sopenharmony_ci if (!wdev->netdev) 19662306a36Sopenharmony_ci continue; 19762306a36Sopenharmony_ci nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 20162306a36Sopenharmony_ci nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci wiphy_net_set(&rdev->wiphy, net); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev)); 20662306a36Sopenharmony_ci WARN_ON(err); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); 20962306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 21262306a36Sopenharmony_ci if (!wdev->netdev) 21362306a36Sopenharmony_ci continue; 21462306a36Sopenharmony_ci nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = data; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 22562306a36Sopenharmony_ci rdev_rfkill_poll(rdev); 22662306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_civoid cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, 23062306a36Sopenharmony_ci struct wireless_dev *wdev) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)) 23562306a36Sopenharmony_ci return; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!wdev_running(wdev)) 23862306a36Sopenharmony_ci return; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci rdev_stop_p2p_device(rdev, wdev); 24162306a36Sopenharmony_ci wdev->is_running = false; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci rdev->opencount--; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (rdev->scan_req && rdev->scan_req->wdev == wdev) { 24662306a36Sopenharmony_ci if (WARN_ON(!rdev->scan_req->notified && 24762306a36Sopenharmony_ci (!rdev->int_scan_req || 24862306a36Sopenharmony_ci !rdev->int_scan_req->notified))) 24962306a36Sopenharmony_ci rdev->scan_req->info.aborted = true; 25062306a36Sopenharmony_ci ___cfg80211_scan_done(rdev, false); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_civoid cfg80211_stop_nan(struct cfg80211_registered_device *rdev, 25562306a36Sopenharmony_ci struct wireless_dev *wdev) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN)) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!wdev_running(wdev)) 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci rdev_stop_nan(rdev, wdev); 26662306a36Sopenharmony_ci wdev->is_running = false; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci rdev->opencount--; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_civoid cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 27462306a36Sopenharmony_ci struct wireless_dev *wdev; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ASSERT_RTNL(); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 27962306a36Sopenharmony_ci if (wdev->netdev) { 28062306a36Sopenharmony_ci dev_close(wdev->netdev); 28162306a36Sopenharmony_ci continue; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* otherwise, check iftype */ 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci wiphy_lock(wiphy); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci switch (wdev->iftype) { 28962306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 29062306a36Sopenharmony_ci cfg80211_stop_p2p_device(rdev, wdev); 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci case NL80211_IFTYPE_NAN: 29362306a36Sopenharmony_ci cfg80211_stop_nan(rdev, wdev); 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci default: 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci wiphy_unlock(wiphy); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int cfg80211_rfkill_set_block(void *data, bool blocked) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = data; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (!blocked) 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci rtnl_lock(); 31262306a36Sopenharmony_ci cfg80211_shutdown_all_interfaces(&rdev->wiphy); 31362306a36Sopenharmony_ci rtnl_unlock(); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void cfg80211_rfkill_block_work(struct work_struct *work) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 32362306a36Sopenharmony_ci rfkill_block); 32462306a36Sopenharmony_ci cfg80211_rfkill_set_block(rdev, true); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic void cfg80211_event_work(struct work_struct *work) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 33262306a36Sopenharmony_ci event_work); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 33562306a36Sopenharmony_ci cfg80211_process_rdev_events(rdev); 33662306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_civoid cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct wireless_dev *wdev, *tmp; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ASSERT_RTNL(); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) { 34662306a36Sopenharmony_ci if (wdev->nl_owner_dead) { 34762306a36Sopenharmony_ci if (wdev->netdev) 34862306a36Sopenharmony_ci dev_close(wdev->netdev); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 35162306a36Sopenharmony_ci cfg80211_leave(rdev, wdev); 35262306a36Sopenharmony_ci cfg80211_remove_virtual_intf(rdev, wdev); 35362306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void cfg80211_destroy_iface_wk(struct work_struct *work) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 36362306a36Sopenharmony_ci destroy_work); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci rtnl_lock(); 36662306a36Sopenharmony_ci cfg80211_destroy_ifaces(rdev); 36762306a36Sopenharmony_ci rtnl_unlock(); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void cfg80211_sched_scan_stop_wk(struct wiphy *wiphy, 37162306a36Sopenharmony_ci struct wiphy_work *work) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 37462306a36Sopenharmony_ci struct cfg80211_sched_scan_request *req, *tmp; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 37762306a36Sopenharmony_ci sched_scan_stop_wk); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) { 38062306a36Sopenharmony_ci if (req->nl_owner_dead) 38162306a36Sopenharmony_ci cfg80211_stop_sched_scan_req(rdev, req, false); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic void cfg80211_propagate_radar_detect_wk(struct work_struct *work) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 39062306a36Sopenharmony_ci propagate_radar_detect_wk); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci rtnl_lock(); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->radar_chandef, 39562306a36Sopenharmony_ci NL80211_DFS_UNAVAILABLE, 39662306a36Sopenharmony_ci NL80211_RADAR_DETECTED); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci rtnl_unlock(); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void cfg80211_propagate_cac_done_wk(struct work_struct *work) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 40662306a36Sopenharmony_ci propagate_cac_done_wk); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci rtnl_lock(); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->cac_done_chandef, 41162306a36Sopenharmony_ci NL80211_DFS_AVAILABLE, 41262306a36Sopenharmony_ci NL80211_RADAR_CAC_FINISHED); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci rtnl_unlock(); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void cfg80211_wiphy_work(struct work_struct *work) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 42062306a36Sopenharmony_ci struct wiphy_work *wk; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, wiphy_work); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 42562306a36Sopenharmony_ci if (rdev->suspended) 42662306a36Sopenharmony_ci goto out; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci spin_lock_irq(&rdev->wiphy_work_lock); 42962306a36Sopenharmony_ci wk = list_first_entry_or_null(&rdev->wiphy_work_list, 43062306a36Sopenharmony_ci struct wiphy_work, entry); 43162306a36Sopenharmony_ci if (wk) { 43262306a36Sopenharmony_ci list_del_init(&wk->entry); 43362306a36Sopenharmony_ci if (!list_empty(&rdev->wiphy_work_list)) 43462306a36Sopenharmony_ci schedule_work(work); 43562306a36Sopenharmony_ci spin_unlock_irq(&rdev->wiphy_work_lock); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci wk->func(&rdev->wiphy, wk); 43862306a36Sopenharmony_ci } else { 43962306a36Sopenharmony_ci spin_unlock_irq(&rdev->wiphy_work_lock); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ciout: 44262306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* exported functions */ 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistruct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, 44862306a36Sopenharmony_ci const char *requested_name) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci static atomic_t wiphy_counter = ATOMIC_INIT(0); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 45362306a36Sopenharmony_ci int alloc_size; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); 45662306a36Sopenharmony_ci WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); 45762306a36Sopenharmony_ci WARN_ON(ops->connect && !ops->disconnect); 45862306a36Sopenharmony_ci WARN_ON(ops->join_ibss && !ops->leave_ibss); 45962306a36Sopenharmony_ci WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf); 46062306a36Sopenharmony_ci WARN_ON(ops->add_station && !ops->del_station); 46162306a36Sopenharmony_ci WARN_ON(ops->add_mpath && !ops->del_mpath); 46262306a36Sopenharmony_ci WARN_ON(ops->join_mesh && !ops->leave_mesh); 46362306a36Sopenharmony_ci WARN_ON(ops->start_p2p_device && !ops->stop_p2p_device); 46462306a36Sopenharmony_ci WARN_ON(ops->start_ap && !ops->stop_ap); 46562306a36Sopenharmony_ci WARN_ON(ops->join_ocb && !ops->leave_ocb); 46662306a36Sopenharmony_ci WARN_ON(ops->suspend && !ops->resume); 46762306a36Sopenharmony_ci WARN_ON(ops->sched_scan_start && !ops->sched_scan_stop); 46862306a36Sopenharmony_ci WARN_ON(ops->remain_on_channel && !ops->cancel_remain_on_channel); 46962306a36Sopenharmony_ci WARN_ON(ops->tdls_channel_switch && !ops->tdls_cancel_channel_switch); 47062306a36Sopenharmony_ci WARN_ON(ops->add_tx_ts && !ops->del_tx_ts); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci alloc_size = sizeof(*rdev) + sizeof_priv; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci rdev = kzalloc(alloc_size, GFP_KERNEL); 47562306a36Sopenharmony_ci if (!rdev) 47662306a36Sopenharmony_ci return NULL; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci rdev->ops = ops; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci rdev->wiphy_idx = atomic_inc_return(&wiphy_counter); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (unlikely(rdev->wiphy_idx < 0)) { 48362306a36Sopenharmony_ci /* ugh, wrapped! */ 48462306a36Sopenharmony_ci atomic_dec(&wiphy_counter); 48562306a36Sopenharmony_ci kfree(rdev); 48662306a36Sopenharmony_ci return NULL; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* atomic_inc_return makes it start at 1, make it start at 0 */ 49062306a36Sopenharmony_ci rdev->wiphy_idx--; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* give it a proper name */ 49362306a36Sopenharmony_ci if (requested_name && requested_name[0]) { 49462306a36Sopenharmony_ci int rv; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci rtnl_lock(); 49762306a36Sopenharmony_ci rv = cfg80211_dev_check_name(rdev, requested_name); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (rv < 0) { 50062306a36Sopenharmony_ci rtnl_unlock(); 50162306a36Sopenharmony_ci goto use_default_name; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name); 50562306a36Sopenharmony_ci rtnl_unlock(); 50662306a36Sopenharmony_ci if (rv) 50762306a36Sopenharmony_ci goto use_default_name; 50862306a36Sopenharmony_ci } else { 50962306a36Sopenharmony_ci int rv; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ciuse_default_name: 51262306a36Sopenharmony_ci /* NOTE: This is *probably* safe w/out holding rtnl because of 51362306a36Sopenharmony_ci * the restrictions on phy names. Probably this call could 51462306a36Sopenharmony_ci * fail if some other part of the kernel (re)named a device 51562306a36Sopenharmony_ci * phyX. But, might should add some locking and check return 51662306a36Sopenharmony_ci * value, and use a different name if this one exists? 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci rv = dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); 51962306a36Sopenharmony_ci if (rv < 0) { 52062306a36Sopenharmony_ci kfree(rdev); 52162306a36Sopenharmony_ci return NULL; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci mutex_init(&rdev->wiphy.mtx); 52662306a36Sopenharmony_ci INIT_LIST_HEAD(&rdev->wiphy.wdev_list); 52762306a36Sopenharmony_ci INIT_LIST_HEAD(&rdev->beacon_registrations); 52862306a36Sopenharmony_ci spin_lock_init(&rdev->beacon_registrations_lock); 52962306a36Sopenharmony_ci spin_lock_init(&rdev->bss_lock); 53062306a36Sopenharmony_ci INIT_LIST_HEAD(&rdev->bss_list); 53162306a36Sopenharmony_ci INIT_LIST_HEAD(&rdev->sched_scan_req_list); 53262306a36Sopenharmony_ci wiphy_work_init(&rdev->scan_done_wk, __cfg80211_scan_done); 53362306a36Sopenharmony_ci INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk, 53462306a36Sopenharmony_ci cfg80211_dfs_channels_update_work); 53562306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 53662306a36Sopenharmony_ci rdev->wiphy.wext = &cfg80211_wext_handler; 53762306a36Sopenharmony_ci#endif 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci device_initialize(&rdev->wiphy.dev); 54062306a36Sopenharmony_ci rdev->wiphy.dev.class = &ieee80211_class; 54162306a36Sopenharmony_ci rdev->wiphy.dev.platform_data = rdev; 54262306a36Sopenharmony_ci device_enable_async_suspend(&rdev->wiphy.dev); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); 54562306a36Sopenharmony_ci wiphy_work_init(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk); 54662306a36Sopenharmony_ci INIT_WORK(&rdev->sched_scan_res_wk, cfg80211_sched_scan_results_wk); 54762306a36Sopenharmony_ci INIT_WORK(&rdev->propagate_radar_detect_wk, 54862306a36Sopenharmony_ci cfg80211_propagate_radar_detect_wk); 54962306a36Sopenharmony_ci INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk); 55062306a36Sopenharmony_ci INIT_WORK(&rdev->mgmt_registrations_update_wk, 55162306a36Sopenharmony_ci cfg80211_mgmt_registrations_update_wk); 55262306a36Sopenharmony_ci spin_lock_init(&rdev->mgmt_registrations_lock); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_DEFAULT_PS 55562306a36Sopenharmony_ci rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; 55662306a36Sopenharmony_ci#endif 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci wiphy_net_set(&rdev->wiphy, &init_net); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; 56162306a36Sopenharmony_ci rdev->wiphy.rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), 56262306a36Sopenharmony_ci &rdev->wiphy.dev, RFKILL_TYPE_WLAN, 56362306a36Sopenharmony_ci &rdev->rfkill_ops, rdev); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (!rdev->wiphy.rfkill) { 56662306a36Sopenharmony_ci wiphy_free(&rdev->wiphy); 56762306a36Sopenharmony_ci return NULL; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci INIT_WORK(&rdev->wiphy_work, cfg80211_wiphy_work); 57162306a36Sopenharmony_ci INIT_LIST_HEAD(&rdev->wiphy_work_list); 57262306a36Sopenharmony_ci spin_lock_init(&rdev->wiphy_work_lock); 57362306a36Sopenharmony_ci INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work); 57462306a36Sopenharmony_ci INIT_WORK(&rdev->conn_work, cfg80211_conn_work); 57562306a36Sopenharmony_ci INIT_WORK(&rdev->event_work, cfg80211_event_work); 57662306a36Sopenharmony_ci INIT_WORK(&rdev->background_cac_abort_wk, 57762306a36Sopenharmony_ci cfg80211_background_cac_abort_wk); 57862306a36Sopenharmony_ci INIT_DELAYED_WORK(&rdev->background_cac_done_wk, 57962306a36Sopenharmony_ci cfg80211_background_cac_done_wk); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci init_waitqueue_head(&rdev->dev_wait); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* 58462306a36Sopenharmony_ci * Initialize wiphy parameters to IEEE 802.11 MIB default values. 58562306a36Sopenharmony_ci * Fragmentation and RTS threshold are disabled by default with the 58662306a36Sopenharmony_ci * special -1 value. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci rdev->wiphy.retry_short = 7; 58962306a36Sopenharmony_ci rdev->wiphy.retry_long = 4; 59062306a36Sopenharmony_ci rdev->wiphy.frag_threshold = (u32) -1; 59162306a36Sopenharmony_ci rdev->wiphy.rts_threshold = (u32) -1; 59262306a36Sopenharmony_ci rdev->wiphy.coverage_class = 0; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci rdev->wiphy.max_num_csa_counters = 1; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci rdev->wiphy.max_sched_scan_plans = 1; 59762306a36Sopenharmony_ci rdev->wiphy.max_sched_scan_plan_interval = U32_MAX; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return &rdev->wiphy; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ciEXPORT_SYMBOL(wiphy_new_nm); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int wiphy_verify_combinations(struct wiphy *wiphy) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci const struct ieee80211_iface_combination *c; 60662306a36Sopenharmony_ci int i, j; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci for (i = 0; i < wiphy->n_iface_combinations; i++) { 60962306a36Sopenharmony_ci u32 cnt = 0; 61062306a36Sopenharmony_ci u16 all_iftypes = 0; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci c = &wiphy->iface_combinations[i]; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* 61562306a36Sopenharmony_ci * Combinations with just one interface aren't real, 61662306a36Sopenharmony_ci * however we make an exception for DFS. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ci if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths)) 61962306a36Sopenharmony_ci return -EINVAL; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* Need at least one channel */ 62262306a36Sopenharmony_ci if (WARN_ON(!c->num_different_channels)) 62362306a36Sopenharmony_ci return -EINVAL; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* DFS only works on one channel. */ 62662306a36Sopenharmony_ci if (WARN_ON(c->radar_detect_widths && 62762306a36Sopenharmony_ci (c->num_different_channels > 1))) 62862306a36Sopenharmony_ci return -EINVAL; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (WARN_ON(!c->n_limits)) 63162306a36Sopenharmony_ci return -EINVAL; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci for (j = 0; j < c->n_limits; j++) { 63462306a36Sopenharmony_ci u16 types = c->limits[j].types; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* interface types shouldn't overlap */ 63762306a36Sopenharmony_ci if (WARN_ON(types & all_iftypes)) 63862306a36Sopenharmony_ci return -EINVAL; 63962306a36Sopenharmony_ci all_iftypes |= types; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (WARN_ON(!c->limits[j].max)) 64262306a36Sopenharmony_ci return -EINVAL; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* Shouldn't list software iftypes in combinations! */ 64562306a36Sopenharmony_ci if (WARN_ON(wiphy->software_iftypes & types)) 64662306a36Sopenharmony_ci return -EINVAL; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* Only a single P2P_DEVICE can be allowed */ 64962306a36Sopenharmony_ci if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) && 65062306a36Sopenharmony_ci c->limits[j].max > 1)) 65162306a36Sopenharmony_ci return -EINVAL; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Only a single NAN can be allowed */ 65462306a36Sopenharmony_ci if (WARN_ON(types & BIT(NL80211_IFTYPE_NAN) && 65562306a36Sopenharmony_ci c->limits[j].max > 1)) 65662306a36Sopenharmony_ci return -EINVAL; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* 65962306a36Sopenharmony_ci * This isn't well-defined right now. If you have an 66062306a36Sopenharmony_ci * IBSS interface, then its beacon interval may change 66162306a36Sopenharmony_ci * by joining other networks, and nothing prevents it 66262306a36Sopenharmony_ci * from doing that. 66362306a36Sopenharmony_ci * So technically we probably shouldn't even allow AP 66462306a36Sopenharmony_ci * and IBSS in the same interface, but it seems that 66562306a36Sopenharmony_ci * some drivers support that, possibly only with fixed 66662306a36Sopenharmony_ci * beacon intervals for IBSS. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) && 66962306a36Sopenharmony_ci c->beacon_int_min_gcd)) { 67062306a36Sopenharmony_ci return -EINVAL; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci cnt += c->limits[j].max; 67462306a36Sopenharmony_ci /* 67562306a36Sopenharmony_ci * Don't advertise an unsupported type 67662306a36Sopenharmony_ci * in a combination. 67762306a36Sopenharmony_ci */ 67862306a36Sopenharmony_ci if (WARN_ON((wiphy->interface_modes & types) != types)) 67962306a36Sopenharmony_ci return -EINVAL; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (WARN_ON(all_iftypes & BIT(NL80211_IFTYPE_WDS))) 68362306a36Sopenharmony_ci return -EINVAL; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* You can't even choose that many! */ 68662306a36Sopenharmony_ci if (WARN_ON(cnt < c->max_interfaces)) 68762306a36Sopenharmony_ci return -EINVAL; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ciint wiphy_register(struct wiphy *wiphy) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 69662306a36Sopenharmony_ci int res; 69762306a36Sopenharmony_ci enum nl80211_band band; 69862306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 69962306a36Sopenharmony_ci bool have_band = false; 70062306a36Sopenharmony_ci int i; 70162306a36Sopenharmony_ci u16 ifmodes = wiphy->interface_modes; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci#ifdef CONFIG_PM 70462306a36Sopenharmony_ci if (WARN_ON(wiphy->wowlan && 70562306a36Sopenharmony_ci (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && 70662306a36Sopenharmony_ci !(wiphy->wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) 70762306a36Sopenharmony_ci return -EINVAL; 70862306a36Sopenharmony_ci if (WARN_ON(wiphy->wowlan && 70962306a36Sopenharmony_ci !wiphy->wowlan->flags && !wiphy->wowlan->n_patterns && 71062306a36Sopenharmony_ci !wiphy->wowlan->tcp)) 71162306a36Sopenharmony_ci return -EINVAL; 71262306a36Sopenharmony_ci#endif 71362306a36Sopenharmony_ci if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) && 71462306a36Sopenharmony_ci (!rdev->ops->tdls_channel_switch || 71562306a36Sopenharmony_ci !rdev->ops->tdls_cancel_channel_switch))) 71662306a36Sopenharmony_ci return -EINVAL; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN)) && 71962306a36Sopenharmony_ci (!rdev->ops->start_nan || !rdev->ops->stop_nan || 72062306a36Sopenharmony_ci !rdev->ops->add_nan_func || !rdev->ops->del_nan_func || 72162306a36Sopenharmony_ci !(wiphy->nan_supported_bands & BIT(NL80211_BAND_2GHZ))))) 72262306a36Sopenharmony_ci return -EINVAL; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))) 72562306a36Sopenharmony_ci return -EINVAL; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (WARN_ON(wiphy->pmsr_capa && !wiphy->pmsr_capa->ftm.supported)) 72862306a36Sopenharmony_ci return -EINVAL; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (wiphy->pmsr_capa && wiphy->pmsr_capa->ftm.supported) { 73162306a36Sopenharmony_ci if (WARN_ON(!wiphy->pmsr_capa->ftm.asap && 73262306a36Sopenharmony_ci !wiphy->pmsr_capa->ftm.non_asap)) 73362306a36Sopenharmony_ci return -EINVAL; 73462306a36Sopenharmony_ci if (WARN_ON(!wiphy->pmsr_capa->ftm.preambles || 73562306a36Sopenharmony_ci !wiphy->pmsr_capa->ftm.bandwidths)) 73662306a36Sopenharmony_ci return -EINVAL; 73762306a36Sopenharmony_ci if (WARN_ON(wiphy->pmsr_capa->ftm.preambles & 73862306a36Sopenharmony_ci ~(BIT(NL80211_PREAMBLE_LEGACY) | 73962306a36Sopenharmony_ci BIT(NL80211_PREAMBLE_HT) | 74062306a36Sopenharmony_ci BIT(NL80211_PREAMBLE_VHT) | 74162306a36Sopenharmony_ci BIT(NL80211_PREAMBLE_HE) | 74262306a36Sopenharmony_ci BIT(NL80211_PREAMBLE_DMG)))) 74362306a36Sopenharmony_ci return -EINVAL; 74462306a36Sopenharmony_ci if (WARN_ON((wiphy->pmsr_capa->ftm.trigger_based || 74562306a36Sopenharmony_ci wiphy->pmsr_capa->ftm.non_trigger_based) && 74662306a36Sopenharmony_ci !(wiphy->pmsr_capa->ftm.preambles & 74762306a36Sopenharmony_ci BIT(NL80211_PREAMBLE_HE)))) 74862306a36Sopenharmony_ci return -EINVAL; 74962306a36Sopenharmony_ci if (WARN_ON(wiphy->pmsr_capa->ftm.bandwidths & 75062306a36Sopenharmony_ci ~(BIT(NL80211_CHAN_WIDTH_20_NOHT) | 75162306a36Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_20) | 75262306a36Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_40) | 75362306a36Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_80) | 75462306a36Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_80P80) | 75562306a36Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_160) | 75662306a36Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_5) | 75762306a36Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_10)))) 75862306a36Sopenharmony_ci return -EINVAL; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) && 76262306a36Sopenharmony_ci (wiphy->regulatory_flags & 76362306a36Sopenharmony_ci (REGULATORY_CUSTOM_REG | 76462306a36Sopenharmony_ci REGULATORY_STRICT_REG | 76562306a36Sopenharmony_ci REGULATORY_COUNTRY_IE_FOLLOW_POWER | 76662306a36Sopenharmony_ci REGULATORY_COUNTRY_IE_IGNORE)))) 76762306a36Sopenharmony_ci return -EINVAL; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (WARN_ON(wiphy->coalesce && 77062306a36Sopenharmony_ci (!wiphy->coalesce->n_rules || 77162306a36Sopenharmony_ci !wiphy->coalesce->n_patterns) && 77262306a36Sopenharmony_ci (!wiphy->coalesce->pattern_min_len || 77362306a36Sopenharmony_ci wiphy->coalesce->pattern_min_len > 77462306a36Sopenharmony_ci wiphy->coalesce->pattern_max_len))) 77562306a36Sopenharmony_ci return -EINVAL; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (WARN_ON(wiphy->ap_sme_capa && 77862306a36Sopenharmony_ci !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) 77962306a36Sopenharmony_ci return -EINVAL; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) 78262306a36Sopenharmony_ci return -EINVAL; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (WARN_ON(wiphy->addresses && 78562306a36Sopenharmony_ci !is_zero_ether_addr(wiphy->perm_addr) && 78662306a36Sopenharmony_ci memcmp(wiphy->perm_addr, wiphy->addresses[0].addr, 78762306a36Sopenharmony_ci ETH_ALEN))) 78862306a36Sopenharmony_ci return -EINVAL; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (WARN_ON(wiphy->max_acl_mac_addrs && 79162306a36Sopenharmony_ci (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) || 79262306a36Sopenharmony_ci !rdev->ops->set_mac_acl))) 79362306a36Sopenharmony_ci return -EINVAL; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* assure only valid behaviours are flagged by driver 79662306a36Sopenharmony_ci * hence subtract 2 as bit 0 is invalid. 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci if (WARN_ON(wiphy->bss_select_support && 79962306a36Sopenharmony_ci (wiphy->bss_select_support & ~(BIT(__NL80211_BSS_SELECT_ATTR_AFTER_LAST) - 2)))) 80062306a36Sopenharmony_ci return -EINVAL; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (WARN_ON(wiphy_ext_feature_isset(&rdev->wiphy, 80362306a36Sopenharmony_ci NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X) && 80462306a36Sopenharmony_ci (!rdev->ops->set_pmk || !rdev->ops->del_pmk))) 80562306a36Sopenharmony_ci return -EINVAL; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (WARN_ON(!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) && 80862306a36Sopenharmony_ci rdev->ops->update_connect_params)) 80962306a36Sopenharmony_ci return -EINVAL; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (wiphy->addresses) 81262306a36Sopenharmony_ci memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* sanity check ifmodes */ 81562306a36Sopenharmony_ci WARN_ON(!ifmodes); 81662306a36Sopenharmony_ci ifmodes &= ((1 << NUM_NL80211_IFTYPES) - 1) & ~1; 81762306a36Sopenharmony_ci if (WARN_ON(ifmodes != wiphy->interface_modes)) 81862306a36Sopenharmony_ci wiphy->interface_modes = ifmodes; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci res = wiphy_verify_combinations(wiphy); 82162306a36Sopenharmony_ci if (res) 82262306a36Sopenharmony_ci return res; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* sanity check supported bands/channels */ 82562306a36Sopenharmony_ci for (band = 0; band < NUM_NL80211_BANDS; band++) { 82662306a36Sopenharmony_ci u16 types = 0; 82762306a36Sopenharmony_ci bool have_he = false; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci sband = wiphy->bands[band]; 83062306a36Sopenharmony_ci if (!sband) 83162306a36Sopenharmony_ci continue; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci sband->band = band; 83462306a36Sopenharmony_ci if (WARN_ON(!sband->n_channels)) 83562306a36Sopenharmony_ci return -EINVAL; 83662306a36Sopenharmony_ci /* 83762306a36Sopenharmony_ci * on 60GHz or sub-1Ghz band, there are no legacy rates, so 83862306a36Sopenharmony_ci * n_bitrates is 0 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci if (WARN_ON((band != NL80211_BAND_60GHZ && 84162306a36Sopenharmony_ci band != NL80211_BAND_S1GHZ) && 84262306a36Sopenharmony_ci !sband->n_bitrates)) 84362306a36Sopenharmony_ci return -EINVAL; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (WARN_ON(band == NL80211_BAND_6GHZ && 84662306a36Sopenharmony_ci (sband->ht_cap.ht_supported || 84762306a36Sopenharmony_ci sband->vht_cap.vht_supported))) 84862306a36Sopenharmony_ci return -EINVAL; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* 85162306a36Sopenharmony_ci * Since cfg80211_disable_40mhz_24ghz is global, we can 85262306a36Sopenharmony_ci * modify the sband's ht data even if the driver uses a 85362306a36Sopenharmony_ci * global structure for that. 85462306a36Sopenharmony_ci */ 85562306a36Sopenharmony_ci if (cfg80211_disable_40mhz_24ghz && 85662306a36Sopenharmony_ci band == NL80211_BAND_2GHZ && 85762306a36Sopenharmony_ci sband->ht_cap.ht_supported) { 85862306a36Sopenharmony_ci sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; 85962306a36Sopenharmony_ci sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* 86362306a36Sopenharmony_ci * Since we use a u32 for rate bitmaps in 86462306a36Sopenharmony_ci * ieee80211_get_response_rate, we cannot 86562306a36Sopenharmony_ci * have more than 32 legacy rates. 86662306a36Sopenharmony_ci */ 86762306a36Sopenharmony_ci if (WARN_ON(sband->n_bitrates > 32)) 86862306a36Sopenharmony_ci return -EINVAL; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 87162306a36Sopenharmony_ci sband->channels[i].orig_flags = 87262306a36Sopenharmony_ci sband->channels[i].flags; 87362306a36Sopenharmony_ci sband->channels[i].orig_mag = INT_MAX; 87462306a36Sopenharmony_ci sband->channels[i].orig_mpwr = 87562306a36Sopenharmony_ci sband->channels[i].max_power; 87662306a36Sopenharmony_ci sband->channels[i].band = band; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (WARN_ON(sband->channels[i].freq_offset >= 1000)) 87962306a36Sopenharmony_ci return -EINVAL; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci for (i = 0; i < sband->n_iftype_data; i++) { 88362306a36Sopenharmony_ci const struct ieee80211_sband_iftype_data *iftd; 88462306a36Sopenharmony_ci bool has_ap, has_non_ap; 88562306a36Sopenharmony_ci u32 ap_bits = BIT(NL80211_IFTYPE_AP) | 88662306a36Sopenharmony_ci BIT(NL80211_IFTYPE_P2P_GO); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci iftd = &sband->iftype_data[i]; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci if (WARN_ON(!iftd->types_mask)) 89162306a36Sopenharmony_ci return -EINVAL; 89262306a36Sopenharmony_ci if (WARN_ON(types & iftd->types_mask)) 89362306a36Sopenharmony_ci return -EINVAL; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* at least one piece of information must be present */ 89662306a36Sopenharmony_ci if (WARN_ON(!iftd->he_cap.has_he)) 89762306a36Sopenharmony_ci return -EINVAL; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci types |= iftd->types_mask; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (i == 0) 90262306a36Sopenharmony_ci have_he = iftd->he_cap.has_he; 90362306a36Sopenharmony_ci else 90462306a36Sopenharmony_ci have_he = have_he && 90562306a36Sopenharmony_ci iftd->he_cap.has_he; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci has_ap = iftd->types_mask & ap_bits; 90862306a36Sopenharmony_ci has_non_ap = iftd->types_mask & ~ap_bits; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* 91162306a36Sopenharmony_ci * For EHT 20 MHz STA, the capabilities format differs 91262306a36Sopenharmony_ci * but to simplify, don't check 20 MHz but rather check 91362306a36Sopenharmony_ci * only if AP and non-AP were mentioned at the same time, 91462306a36Sopenharmony_ci * reject if so. 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_ci if (WARN_ON(iftd->eht_cap.has_eht && 91762306a36Sopenharmony_ci has_ap && has_non_ap)) 91862306a36Sopenharmony_ci return -EINVAL; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (WARN_ON(!have_he && band == NL80211_BAND_6GHZ)) 92262306a36Sopenharmony_ci return -EINVAL; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci have_band = true; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (!have_band) { 92862306a36Sopenharmony_ci WARN_ON(1); 92962306a36Sopenharmony_ci return -EINVAL; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { 93362306a36Sopenharmony_ci /* 93462306a36Sopenharmony_ci * Validate we have a policy (can be explicitly set to 93562306a36Sopenharmony_ci * VENDOR_CMD_RAW_DATA which is non-NULL) and also that 93662306a36Sopenharmony_ci * we have at least one of doit/dumpit. 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci if (WARN_ON(!rdev->wiphy.vendor_commands[i].policy)) 93962306a36Sopenharmony_ci return -EINVAL; 94062306a36Sopenharmony_ci if (WARN_ON(!rdev->wiphy.vendor_commands[i].doit && 94162306a36Sopenharmony_ci !rdev->wiphy.vendor_commands[i].dumpit)) 94262306a36Sopenharmony_ci return -EINVAL; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci#ifdef CONFIG_PM 94662306a36Sopenharmony_ci if (WARN_ON(rdev->wiphy.wowlan && rdev->wiphy.wowlan->n_patterns && 94762306a36Sopenharmony_ci (!rdev->wiphy.wowlan->pattern_min_len || 94862306a36Sopenharmony_ci rdev->wiphy.wowlan->pattern_min_len > 94962306a36Sopenharmony_ci rdev->wiphy.wowlan->pattern_max_len))) 95062306a36Sopenharmony_ci return -EINVAL; 95162306a36Sopenharmony_ci#endif 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci if (!wiphy->max_num_akm_suites) 95462306a36Sopenharmony_ci wiphy->max_num_akm_suites = NL80211_MAX_NR_AKM_SUITES; 95562306a36Sopenharmony_ci else if (wiphy->max_num_akm_suites < NL80211_MAX_NR_AKM_SUITES || 95662306a36Sopenharmony_ci wiphy->max_num_akm_suites > CFG80211_MAX_NUM_AKM_SUITES) 95762306a36Sopenharmony_ci return -EINVAL; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci /* check and set up bitrates */ 96062306a36Sopenharmony_ci ieee80211_set_bitrate_flags(wiphy); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci rtnl_lock(); 96562306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 96662306a36Sopenharmony_ci res = device_add(&rdev->wiphy.dev); 96762306a36Sopenharmony_ci if (res) { 96862306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 96962306a36Sopenharmony_ci rtnl_unlock(); 97062306a36Sopenharmony_ci return res; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci list_add_rcu(&rdev->list, &cfg80211_rdev_list); 97462306a36Sopenharmony_ci cfg80211_rdev_list_generation++; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* add to debugfs */ 97762306a36Sopenharmony_ci rdev->wiphy.debugfsdir = debugfs_create_dir(wiphy_name(&rdev->wiphy), 97862306a36Sopenharmony_ci ieee80211_debugfs_dir); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci cfg80211_debugfs_rdev_add(rdev); 98162306a36Sopenharmony_ci nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); 98262306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci /* set up regulatory info */ 98562306a36Sopenharmony_ci wiphy_regulatory_register(wiphy); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { 98862306a36Sopenharmony_ci struct regulatory_request request; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci request.wiphy_idx = get_wiphy_idx(wiphy); 99162306a36Sopenharmony_ci request.initiator = NL80211_REGDOM_SET_BY_DRIVER; 99262306a36Sopenharmony_ci request.alpha2[0] = '9'; 99362306a36Sopenharmony_ci request.alpha2[1] = '9'; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci nl80211_send_reg_change_event(&request); 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* Check that nobody globally advertises any capabilities they do not 99962306a36Sopenharmony_ci * advertise on all possible interface types. 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ci if (wiphy->extended_capabilities_len && 100262306a36Sopenharmony_ci wiphy->num_iftype_ext_capab && 100362306a36Sopenharmony_ci wiphy->iftype_ext_capab) { 100462306a36Sopenharmony_ci u8 supported_on_all, j; 100562306a36Sopenharmony_ci const struct wiphy_iftype_ext_capab *capab; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci capab = wiphy->iftype_ext_capab; 100862306a36Sopenharmony_ci for (j = 0; j < wiphy->extended_capabilities_len; j++) { 100962306a36Sopenharmony_ci if (capab[0].extended_capabilities_len > j) 101062306a36Sopenharmony_ci supported_on_all = 101162306a36Sopenharmony_ci capab[0].extended_capabilities[j]; 101262306a36Sopenharmony_ci else 101362306a36Sopenharmony_ci supported_on_all = 0x00; 101462306a36Sopenharmony_ci for (i = 1; i < wiphy->num_iftype_ext_capab; i++) { 101562306a36Sopenharmony_ci if (j >= capab[i].extended_capabilities_len) { 101662306a36Sopenharmony_ci supported_on_all = 0x00; 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci supported_on_all &= 102062306a36Sopenharmony_ci capab[i].extended_capabilities[j]; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci if (WARN_ON(wiphy->extended_capabilities[j] & 102362306a36Sopenharmony_ci ~supported_on_all)) 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci rdev->wiphy.registered = true; 102962306a36Sopenharmony_ci rtnl_unlock(); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci res = rfkill_register(rdev->wiphy.rfkill); 103262306a36Sopenharmony_ci if (res) { 103362306a36Sopenharmony_ci rfkill_destroy(rdev->wiphy.rfkill); 103462306a36Sopenharmony_ci rdev->wiphy.rfkill = NULL; 103562306a36Sopenharmony_ci wiphy_unregister(&rdev->wiphy); 103662306a36Sopenharmony_ci return res; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci return 0; 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ciEXPORT_SYMBOL(wiphy_register); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_civoid wiphy_rfkill_start_polling(struct wiphy *wiphy) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (!rdev->ops->rfkill_poll) 104862306a36Sopenharmony_ci return; 104962306a36Sopenharmony_ci rdev->rfkill_ops.poll = cfg80211_rfkill_poll; 105062306a36Sopenharmony_ci rfkill_resume_polling(wiphy->rfkill); 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ciEXPORT_SYMBOL(wiphy_rfkill_start_polling); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_civoid cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev, 105562306a36Sopenharmony_ci struct wiphy_work *end) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci unsigned int runaway_limit = 100; 105862306a36Sopenharmony_ci unsigned long flags; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci spin_lock_irqsave(&rdev->wiphy_work_lock, flags); 106362306a36Sopenharmony_ci while (!list_empty(&rdev->wiphy_work_list)) { 106462306a36Sopenharmony_ci struct wiphy_work *wk; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci wk = list_first_entry(&rdev->wiphy_work_list, 106762306a36Sopenharmony_ci struct wiphy_work, entry); 106862306a36Sopenharmony_ci list_del_init(&wk->entry); 106962306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci wk->func(&rdev->wiphy, wk); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci spin_lock_irqsave(&rdev->wiphy_work_lock, flags); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (wk == end) 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (WARN_ON(--runaway_limit == 0)) 107962306a36Sopenharmony_ci INIT_LIST_HEAD(&rdev->wiphy_work_list); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags); 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_civoid wiphy_unregister(struct wiphy *wiphy) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci wait_event(rdev->dev_wait, ({ 108962306a36Sopenharmony_ci int __count; 109062306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 109162306a36Sopenharmony_ci __count = rdev->opencount; 109262306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 109362306a36Sopenharmony_ci __count == 0; })); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci if (rdev->wiphy.rfkill) 109662306a36Sopenharmony_ci rfkill_unregister(rdev->wiphy.rfkill); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci rtnl_lock(); 109962306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 110062306a36Sopenharmony_ci nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); 110162306a36Sopenharmony_ci rdev->wiphy.registered = false; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci WARN_ON(!list_empty(&rdev->wiphy.wdev_list)); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci /* 110662306a36Sopenharmony_ci * First remove the hardware from everywhere, this makes 110762306a36Sopenharmony_ci * it impossible to find from userspace. 110862306a36Sopenharmony_ci */ 110962306a36Sopenharmony_ci debugfs_remove_recursive(rdev->wiphy.debugfsdir); 111062306a36Sopenharmony_ci list_del_rcu(&rdev->list); 111162306a36Sopenharmony_ci synchronize_rcu(); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* 111462306a36Sopenharmony_ci * If this device got a regulatory hint tell core its 111562306a36Sopenharmony_ci * free to listen now to a new shiny device regulatory hint 111662306a36Sopenharmony_ci */ 111762306a36Sopenharmony_ci wiphy_regulatory_deregister(wiphy); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci cfg80211_rdev_list_generation++; 112062306a36Sopenharmony_ci device_del(&rdev->wiphy.dev); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci#ifdef CONFIG_PM 112362306a36Sopenharmony_ci if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) 112462306a36Sopenharmony_ci rdev_set_wakeup(rdev, false); 112562306a36Sopenharmony_ci#endif 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci /* surely nothing is reachable now, clean up work */ 112862306a36Sopenharmony_ci cfg80211_process_wiphy_works(rdev, NULL); 112962306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 113062306a36Sopenharmony_ci rtnl_unlock(); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* this has nothing to do now but make sure it's gone */ 113362306a36Sopenharmony_ci cancel_work_sync(&rdev->wiphy_work); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci cancel_work_sync(&rdev->conn_work); 113662306a36Sopenharmony_ci flush_work(&rdev->event_work); 113762306a36Sopenharmony_ci cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); 113862306a36Sopenharmony_ci cancel_delayed_work_sync(&rdev->background_cac_done_wk); 113962306a36Sopenharmony_ci flush_work(&rdev->destroy_work); 114062306a36Sopenharmony_ci flush_work(&rdev->propagate_radar_detect_wk); 114162306a36Sopenharmony_ci flush_work(&rdev->propagate_cac_done_wk); 114262306a36Sopenharmony_ci flush_work(&rdev->mgmt_registrations_update_wk); 114362306a36Sopenharmony_ci flush_work(&rdev->background_cac_abort_wk); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci cfg80211_rdev_free_wowlan(rdev); 114662306a36Sopenharmony_ci cfg80211_rdev_free_coalesce(rdev); 114762306a36Sopenharmony_ci} 114862306a36Sopenharmony_ciEXPORT_SYMBOL(wiphy_unregister); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_civoid cfg80211_dev_free(struct cfg80211_registered_device *rdev) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci struct cfg80211_internal_bss *scan, *tmp; 115362306a36Sopenharmony_ci struct cfg80211_beacon_registration *reg, *treg; 115462306a36Sopenharmony_ci rfkill_destroy(rdev->wiphy.rfkill); 115562306a36Sopenharmony_ci list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) { 115662306a36Sopenharmony_ci list_del(®->list); 115762306a36Sopenharmony_ci kfree(reg); 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) 116062306a36Sopenharmony_ci cfg80211_put_bss(&rdev->wiphy, &scan->pub); 116162306a36Sopenharmony_ci mutex_destroy(&rdev->wiphy.mtx); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* 116462306a36Sopenharmony_ci * The 'regd' can only be non-NULL if we never finished 116562306a36Sopenharmony_ci * initializing the wiphy and thus never went through the 116662306a36Sopenharmony_ci * unregister path - e.g. in failure scenarios. Thus, it 116762306a36Sopenharmony_ci * cannot have been visible to anyone if non-NULL, so we 116862306a36Sopenharmony_ci * can just free it here. 116962306a36Sopenharmony_ci */ 117062306a36Sopenharmony_ci kfree(rcu_dereference_raw(rdev->wiphy.regd)); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci kfree(rdev); 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_civoid wiphy_free(struct wiphy *wiphy) 117662306a36Sopenharmony_ci{ 117762306a36Sopenharmony_ci put_device(&wiphy->dev); 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ciEXPORT_SYMBOL(wiphy_free); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_civoid wiphy_rfkill_set_hw_state_reason(struct wiphy *wiphy, bool blocked, 118262306a36Sopenharmony_ci enum rfkill_hard_block_reasons reason) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (rfkill_set_hw_state_reason(wiphy->rfkill, blocked, reason)) 118762306a36Sopenharmony_ci schedule_work(&rdev->rfkill_block); 118862306a36Sopenharmony_ci} 118962306a36Sopenharmony_ciEXPORT_SYMBOL(wiphy_rfkill_set_hw_state_reason); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_cistatic void _cfg80211_unregister_wdev(struct wireless_dev *wdev, 119262306a36Sopenharmony_ci bool unregister_netdev) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 119562306a36Sopenharmony_ci struct cfg80211_cqm_config *cqm_config; 119662306a36Sopenharmony_ci unsigned int link_id; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci ASSERT_RTNL(); 119962306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci wdev->registered = false; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci if (wdev->netdev) { 120662306a36Sopenharmony_ci sysfs_remove_link(&wdev->netdev->dev.kobj, "phy80211"); 120762306a36Sopenharmony_ci if (unregister_netdev) 120862306a36Sopenharmony_ci unregister_netdevice(wdev->netdev); 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci list_del_rcu(&wdev->list); 121262306a36Sopenharmony_ci synchronize_net(); 121362306a36Sopenharmony_ci rdev->devlist_generation++; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci cfg80211_mlme_purge_registrations(wdev); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci switch (wdev->iftype) { 121862306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 121962306a36Sopenharmony_ci cfg80211_stop_p2p_device(rdev, wdev); 122062306a36Sopenharmony_ci break; 122162306a36Sopenharmony_ci case NL80211_IFTYPE_NAN: 122262306a36Sopenharmony_ci cfg80211_stop_nan(rdev, wdev); 122362306a36Sopenharmony_ci break; 122462306a36Sopenharmony_ci default: 122562306a36Sopenharmony_ci break; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 122962306a36Sopenharmony_ci kfree_sensitive(wdev->wext.keys); 123062306a36Sopenharmony_ci wdev->wext.keys = NULL; 123162306a36Sopenharmony_ci#endif 123262306a36Sopenharmony_ci wiphy_work_cancel(wdev->wiphy, &wdev->cqm_rssi_work); 123362306a36Sopenharmony_ci /* deleted from the list, so can't be found from nl80211 any more */ 123462306a36Sopenharmony_ci cqm_config = rcu_access_pointer(wdev->cqm_config); 123562306a36Sopenharmony_ci kfree_rcu(cqm_config, rcu_head); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* 123862306a36Sopenharmony_ci * Ensure that all events have been processed and 123962306a36Sopenharmony_ci * freed. 124062306a36Sopenharmony_ci */ 124162306a36Sopenharmony_ci cfg80211_process_wdev_events(wdev); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (wdev->iftype == NL80211_IFTYPE_STATION || 124462306a36Sopenharmony_ci wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) { 124562306a36Sopenharmony_ci for (link_id = 0; link_id < ARRAY_SIZE(wdev->links); link_id++) { 124662306a36Sopenharmony_ci struct cfg80211_internal_bss *curbss; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci curbss = wdev->links[link_id].client.current_bss; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (WARN_ON(curbss)) { 125162306a36Sopenharmony_ci cfg80211_unhold_bss(curbss); 125262306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, &curbss->pub); 125362306a36Sopenharmony_ci wdev->links[link_id].client.current_bss = NULL; 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci wdev->connected = false; 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_civoid cfg80211_unregister_wdev(struct wireless_dev *wdev) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci _cfg80211_unregister_wdev(wdev, true); 126462306a36Sopenharmony_ci} 126562306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_unregister_wdev); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cistatic const struct device_type wiphy_type = { 126862306a36Sopenharmony_ci .name = "wlan", 126962306a36Sopenharmony_ci}; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_civoid cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, 127262306a36Sopenharmony_ci enum nl80211_iftype iftype, int num) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci rdev->num_running_ifaces += num; 127762306a36Sopenharmony_ci if (iftype == NL80211_IFTYPE_MONITOR) 127862306a36Sopenharmony_ci rdev->num_running_monitor_ifaces += num; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_civoid __cfg80211_leave(struct cfg80211_registered_device *rdev, 128262306a36Sopenharmony_ci struct wireless_dev *wdev) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci struct net_device *dev = wdev->netdev; 128562306a36Sopenharmony_ci struct cfg80211_sched_scan_request *pos, *tmp; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 128862306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci cfg80211_pmsr_wdev_down(wdev); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci cfg80211_stop_background_radar_detection(wdev); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci switch (wdev->iftype) { 129562306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 129662306a36Sopenharmony_ci __cfg80211_leave_ibss(rdev, dev, true); 129762306a36Sopenharmony_ci break; 129862306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 129962306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 130062306a36Sopenharmony_ci list_for_each_entry_safe(pos, tmp, &rdev->sched_scan_req_list, 130162306a36Sopenharmony_ci list) { 130262306a36Sopenharmony_ci if (dev == pos->dev) 130362306a36Sopenharmony_ci cfg80211_stop_sched_scan_req(rdev, pos, false); 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 130762306a36Sopenharmony_ci kfree(wdev->wext.ie); 130862306a36Sopenharmony_ci wdev->wext.ie = NULL; 130962306a36Sopenharmony_ci wdev->wext.ie_len = 0; 131062306a36Sopenharmony_ci wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; 131162306a36Sopenharmony_ci#endif 131262306a36Sopenharmony_ci cfg80211_disconnect(rdev, dev, 131362306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, true); 131462306a36Sopenharmony_ci break; 131562306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 131662306a36Sopenharmony_ci __cfg80211_leave_mesh(rdev, dev); 131762306a36Sopenharmony_ci break; 131862306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 131962306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 132062306a36Sopenharmony_ci __cfg80211_stop_ap(rdev, dev, -1, true); 132162306a36Sopenharmony_ci break; 132262306a36Sopenharmony_ci case NL80211_IFTYPE_OCB: 132362306a36Sopenharmony_ci __cfg80211_leave_ocb(rdev, dev); 132462306a36Sopenharmony_ci break; 132562306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 132662306a36Sopenharmony_ci case NL80211_IFTYPE_NAN: 132762306a36Sopenharmony_ci /* cannot happen, has no netdev */ 132862306a36Sopenharmony_ci break; 132962306a36Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 133062306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 133162306a36Sopenharmony_ci /* nothing to do */ 133262306a36Sopenharmony_ci break; 133362306a36Sopenharmony_ci case NL80211_IFTYPE_UNSPECIFIED: 133462306a36Sopenharmony_ci case NL80211_IFTYPE_WDS: 133562306a36Sopenharmony_ci case NUM_NL80211_IFTYPES: 133662306a36Sopenharmony_ci /* invalid */ 133762306a36Sopenharmony_ci break; 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_civoid cfg80211_leave(struct cfg80211_registered_device *rdev, 134262306a36Sopenharmony_ci struct wireless_dev *wdev) 134362306a36Sopenharmony_ci{ 134462306a36Sopenharmony_ci wdev_lock(wdev); 134562306a36Sopenharmony_ci __cfg80211_leave(rdev, wdev); 134662306a36Sopenharmony_ci wdev_unlock(wdev); 134762306a36Sopenharmony_ci} 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_civoid cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, 135062306a36Sopenharmony_ci gfp_t gfp) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 135362306a36Sopenharmony_ci struct cfg80211_event *ev; 135462306a36Sopenharmony_ci unsigned long flags; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci trace_cfg80211_stop_iface(wiphy, wdev); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev), gfp); 135962306a36Sopenharmony_ci if (!ev) 136062306a36Sopenharmony_ci return; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci ev->type = EVENT_STOPPED; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci spin_lock_irqsave(&wdev->event_lock, flags); 136562306a36Sopenharmony_ci list_add_tail(&ev->list, &wdev->event_list); 136662306a36Sopenharmony_ci spin_unlock_irqrestore(&wdev->event_lock, flags); 136762306a36Sopenharmony_ci queue_work(cfg80211_wq, &rdev->event_work); 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_stop_iface); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_civoid cfg80211_init_wdev(struct wireless_dev *wdev) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci mutex_init(&wdev->mtx); 137462306a36Sopenharmony_ci INIT_LIST_HEAD(&wdev->event_list); 137562306a36Sopenharmony_ci spin_lock_init(&wdev->event_lock); 137662306a36Sopenharmony_ci INIT_LIST_HEAD(&wdev->mgmt_registrations); 137762306a36Sopenharmony_ci INIT_LIST_HEAD(&wdev->pmsr_list); 137862306a36Sopenharmony_ci spin_lock_init(&wdev->pmsr_lock); 137962306a36Sopenharmony_ci INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 138262306a36Sopenharmony_ci wdev->wext.default_key = -1; 138362306a36Sopenharmony_ci wdev->wext.default_mgmt_key = -1; 138462306a36Sopenharmony_ci wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; 138562306a36Sopenharmony_ci#endif 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci wiphy_work_init(&wdev->cqm_rssi_work, cfg80211_cqm_rssi_notify_work); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) 139062306a36Sopenharmony_ci wdev->ps = true; 139162306a36Sopenharmony_ci else 139262306a36Sopenharmony_ci wdev->ps = false; 139362306a36Sopenharmony_ci /* allow mac80211 to determine the timeout */ 139462306a36Sopenharmony_ci wdev->ps_timeout = -1; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci if ((wdev->iftype == NL80211_IFTYPE_STATION || 139762306a36Sopenharmony_ci wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || 139862306a36Sopenharmony_ci wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) 139962306a36Sopenharmony_ci wdev->netdev->priv_flags |= IFF_DONT_BRIDGE; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk); 140262306a36Sopenharmony_ci} 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_civoid cfg80211_register_wdev(struct cfg80211_registered_device *rdev, 140562306a36Sopenharmony_ci struct wireless_dev *wdev) 140662306a36Sopenharmony_ci{ 140762306a36Sopenharmony_ci ASSERT_RTNL(); 140862306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci /* 141162306a36Sopenharmony_ci * We get here also when the interface changes network namespaces, 141262306a36Sopenharmony_ci * as it's registered into the new one, but we don't want it to 141362306a36Sopenharmony_ci * change ID in that case. Checking if the ID is already assigned 141462306a36Sopenharmony_ci * works, because 0 isn't considered a valid ID and the memory is 141562306a36Sopenharmony_ci * 0-initialized. 141662306a36Sopenharmony_ci */ 141762306a36Sopenharmony_ci if (!wdev->identifier) 141862306a36Sopenharmony_ci wdev->identifier = ++rdev->wdev_id; 141962306a36Sopenharmony_ci list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list); 142062306a36Sopenharmony_ci rdev->devlist_generation++; 142162306a36Sopenharmony_ci wdev->registered = true; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if (wdev->netdev && 142462306a36Sopenharmony_ci sysfs_create_link(&wdev->netdev->dev.kobj, &rdev->wiphy.dev.kobj, 142562306a36Sopenharmony_ci "phy80211")) 142662306a36Sopenharmony_ci pr_err("failed to add phy80211 symlink to netdev!\n"); 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE); 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ciint cfg80211_register_netdevice(struct net_device *dev) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 143462306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 143562306a36Sopenharmony_ci int ret; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci ASSERT_RTNL(); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci if (WARN_ON(!wdev)) 144062306a36Sopenharmony_ci return -EINVAL; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci rdev = wiphy_to_rdev(wdev->wiphy); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci /* we'll take care of this */ 144762306a36Sopenharmony_ci wdev->registered = true; 144862306a36Sopenharmony_ci wdev->registering = true; 144962306a36Sopenharmony_ci ret = register_netdevice(dev); 145062306a36Sopenharmony_ci if (ret) 145162306a36Sopenharmony_ci goto out; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci cfg80211_register_wdev(rdev, wdev); 145462306a36Sopenharmony_ci ret = 0; 145562306a36Sopenharmony_ciout: 145662306a36Sopenharmony_ci wdev->registering = false; 145762306a36Sopenharmony_ci if (ret) 145862306a36Sopenharmony_ci wdev->registered = false; 145962306a36Sopenharmony_ci return ret; 146062306a36Sopenharmony_ci} 146162306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_register_netdevice); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_cistatic int cfg80211_netdev_notifier_call(struct notifier_block *nb, 146462306a36Sopenharmony_ci unsigned long state, void *ptr) 146562306a36Sopenharmony_ci{ 146662306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 146762306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 146862306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 146962306a36Sopenharmony_ci struct cfg80211_sched_scan_request *pos, *tmp; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci if (!wdev) 147262306a36Sopenharmony_ci return NOTIFY_DONE; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci rdev = wiphy_to_rdev(wdev->wiphy); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci switch (state) { 147962306a36Sopenharmony_ci case NETDEV_POST_INIT: 148062306a36Sopenharmony_ci SET_NETDEV_DEVTYPE(dev, &wiphy_type); 148162306a36Sopenharmony_ci wdev->netdev = dev; 148262306a36Sopenharmony_ci /* can only change netns with wiphy */ 148362306a36Sopenharmony_ci dev->features |= NETIF_F_NETNS_LOCAL; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci cfg80211_init_wdev(wdev); 148662306a36Sopenharmony_ci break; 148762306a36Sopenharmony_ci case NETDEV_REGISTER: 148862306a36Sopenharmony_ci if (!wdev->registered) { 148962306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 149062306a36Sopenharmony_ci cfg80211_register_wdev(rdev, wdev); 149162306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci break; 149462306a36Sopenharmony_ci case NETDEV_UNREGISTER: 149562306a36Sopenharmony_ci /* 149662306a36Sopenharmony_ci * It is possible to get NETDEV_UNREGISTER multiple times, 149762306a36Sopenharmony_ci * so check wdev->registered. 149862306a36Sopenharmony_ci */ 149962306a36Sopenharmony_ci if (wdev->registered && !wdev->registering) { 150062306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 150162306a36Sopenharmony_ci _cfg80211_unregister_wdev(wdev, false); 150262306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci case NETDEV_GOING_DOWN: 150662306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 150762306a36Sopenharmony_ci cfg80211_leave(rdev, wdev); 150862306a36Sopenharmony_ci cfg80211_remove_links(wdev); 150962306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 151062306a36Sopenharmony_ci /* since we just did cfg80211_leave() nothing to do there */ 151162306a36Sopenharmony_ci cancel_work_sync(&wdev->disconnect_wk); 151262306a36Sopenharmony_ci cancel_work_sync(&wdev->pmsr_free_wk); 151362306a36Sopenharmony_ci break; 151462306a36Sopenharmony_ci case NETDEV_DOWN: 151562306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 151662306a36Sopenharmony_ci cfg80211_update_iface_num(rdev, wdev->iftype, -1); 151762306a36Sopenharmony_ci if (rdev->scan_req && rdev->scan_req->wdev == wdev) { 151862306a36Sopenharmony_ci if (WARN_ON(!rdev->scan_req->notified && 151962306a36Sopenharmony_ci (!rdev->int_scan_req || 152062306a36Sopenharmony_ci !rdev->int_scan_req->notified))) 152162306a36Sopenharmony_ci rdev->scan_req->info.aborted = true; 152262306a36Sopenharmony_ci ___cfg80211_scan_done(rdev, false); 152362306a36Sopenharmony_ci } 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci list_for_each_entry_safe(pos, tmp, 152662306a36Sopenharmony_ci &rdev->sched_scan_req_list, list) { 152762306a36Sopenharmony_ci if (WARN_ON(pos->dev == wdev->netdev)) 152862306a36Sopenharmony_ci cfg80211_stop_sched_scan_req(rdev, pos, false); 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci rdev->opencount--; 153262306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 153362306a36Sopenharmony_ci wake_up(&rdev->dev_wait); 153462306a36Sopenharmony_ci break; 153562306a36Sopenharmony_ci case NETDEV_UP: 153662306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 153762306a36Sopenharmony_ci cfg80211_update_iface_num(rdev, wdev->iftype, 1); 153862306a36Sopenharmony_ci wdev_lock(wdev); 153962306a36Sopenharmony_ci switch (wdev->iftype) { 154062306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 154162306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 154262306a36Sopenharmony_ci cfg80211_ibss_wext_join(rdev, wdev); 154362306a36Sopenharmony_ci break; 154462306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 154562306a36Sopenharmony_ci cfg80211_mgd_wext_connect(rdev, wdev); 154662306a36Sopenharmony_ci break; 154762306a36Sopenharmony_ci#endif 154862306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH 154962306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 155062306a36Sopenharmony_ci { 155162306a36Sopenharmony_ci /* backward compat code... */ 155262306a36Sopenharmony_ci struct mesh_setup setup; 155362306a36Sopenharmony_ci memcpy(&setup, &default_mesh_setup, 155462306a36Sopenharmony_ci sizeof(setup)); 155562306a36Sopenharmony_ci /* back compat only needed for mesh_id */ 155662306a36Sopenharmony_ci setup.mesh_id = wdev->u.mesh.id; 155762306a36Sopenharmony_ci setup.mesh_id_len = wdev->u.mesh.id_up_len; 155862306a36Sopenharmony_ci if (wdev->u.mesh.id_up_len) 155962306a36Sopenharmony_ci __cfg80211_join_mesh(rdev, dev, 156062306a36Sopenharmony_ci &setup, 156162306a36Sopenharmony_ci &default_mesh_config); 156262306a36Sopenharmony_ci break; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci#endif 156562306a36Sopenharmony_ci default: 156662306a36Sopenharmony_ci break; 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci wdev_unlock(wdev); 156962306a36Sopenharmony_ci rdev->opencount++; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci /* 157262306a36Sopenharmony_ci * Configure power management to the driver here so that its 157362306a36Sopenharmony_ci * correctly set also after interface type changes etc. 157462306a36Sopenharmony_ci */ 157562306a36Sopenharmony_ci if ((wdev->iftype == NL80211_IFTYPE_STATION || 157662306a36Sopenharmony_ci wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && 157762306a36Sopenharmony_ci rdev->ops->set_power_mgmt && 157862306a36Sopenharmony_ci rdev_set_power_mgmt(rdev, dev, wdev->ps, 157962306a36Sopenharmony_ci wdev->ps_timeout)) { 158062306a36Sopenharmony_ci /* assume this means it's off */ 158162306a36Sopenharmony_ci wdev->ps = false; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 158462306a36Sopenharmony_ci break; 158562306a36Sopenharmony_ci case NETDEV_PRE_UP: 158662306a36Sopenharmony_ci if (!cfg80211_iftype_allowed(wdev->wiphy, wdev->iftype, 158762306a36Sopenharmony_ci wdev->use_4addr, 0)) 158862306a36Sopenharmony_ci return notifier_from_errno(-EOPNOTSUPP); 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci if (rfkill_blocked(rdev->wiphy.rfkill)) 159162306a36Sopenharmony_ci return notifier_from_errno(-ERFKILL); 159262306a36Sopenharmony_ci break; 159362306a36Sopenharmony_ci default: 159462306a36Sopenharmony_ci return NOTIFY_DONE; 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci wireless_nlevent_flush(); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci return NOTIFY_OK; 160062306a36Sopenharmony_ci} 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_cistatic struct notifier_block cfg80211_netdev_notifier = { 160362306a36Sopenharmony_ci .notifier_call = cfg80211_netdev_notifier_call, 160462306a36Sopenharmony_ci}; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_cistatic void __net_exit cfg80211_pernet_exit(struct net *net) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci rtnl_lock(); 161162306a36Sopenharmony_ci list_for_each_entry(rdev, &cfg80211_rdev_list, list) { 161262306a36Sopenharmony_ci if (net_eq(wiphy_net(&rdev->wiphy), net)) 161362306a36Sopenharmony_ci WARN_ON(cfg80211_switch_netns(rdev, &init_net)); 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci rtnl_unlock(); 161662306a36Sopenharmony_ci} 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_cistatic struct pernet_operations cfg80211_pernet_ops = { 161962306a36Sopenharmony_ci .exit = cfg80211_pernet_exit, 162062306a36Sopenharmony_ci}; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_civoid wiphy_work_queue(struct wiphy *wiphy, struct wiphy_work *work) 162362306a36Sopenharmony_ci{ 162462306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 162562306a36Sopenharmony_ci unsigned long flags; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci spin_lock_irqsave(&rdev->wiphy_work_lock, flags); 162862306a36Sopenharmony_ci if (list_empty(&work->entry)) 162962306a36Sopenharmony_ci list_add_tail(&work->entry, &rdev->wiphy_work_list); 163062306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags); 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci queue_work(system_unbound_wq, &rdev->wiphy_work); 163362306a36Sopenharmony_ci} 163462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wiphy_work_queue); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_civoid wiphy_work_cancel(struct wiphy *wiphy, struct wiphy_work *work) 163762306a36Sopenharmony_ci{ 163862306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 163962306a36Sopenharmony_ci unsigned long flags; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci lockdep_assert_held(&wiphy->mtx); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci spin_lock_irqsave(&rdev->wiphy_work_lock, flags); 164462306a36Sopenharmony_ci if (!list_empty(&work->entry)) 164562306a36Sopenharmony_ci list_del_init(&work->entry); 164662306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags); 164762306a36Sopenharmony_ci} 164862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wiphy_work_cancel); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_civoid wiphy_work_flush(struct wiphy *wiphy, struct wiphy_work *work) 165162306a36Sopenharmony_ci{ 165262306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 165362306a36Sopenharmony_ci unsigned long flags; 165462306a36Sopenharmony_ci bool run; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci spin_lock_irqsave(&rdev->wiphy_work_lock, flags); 165762306a36Sopenharmony_ci run = !work || !list_empty(&work->entry); 165862306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci if (run) 166162306a36Sopenharmony_ci cfg80211_process_wiphy_works(rdev, work); 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wiphy_work_flush); 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_civoid wiphy_delayed_work_timer(struct timer_list *t) 166662306a36Sopenharmony_ci{ 166762306a36Sopenharmony_ci struct wiphy_delayed_work *dwork = from_timer(dwork, t, timer); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci wiphy_work_queue(dwork->wiphy, &dwork->work); 167062306a36Sopenharmony_ci} 167162306a36Sopenharmony_ciEXPORT_SYMBOL(wiphy_delayed_work_timer); 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_civoid wiphy_delayed_work_queue(struct wiphy *wiphy, 167462306a36Sopenharmony_ci struct wiphy_delayed_work *dwork, 167562306a36Sopenharmony_ci unsigned long delay) 167662306a36Sopenharmony_ci{ 167762306a36Sopenharmony_ci if (!delay) { 167862306a36Sopenharmony_ci del_timer(&dwork->timer); 167962306a36Sopenharmony_ci wiphy_work_queue(wiphy, &dwork->work); 168062306a36Sopenharmony_ci return; 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci dwork->wiphy = wiphy; 168462306a36Sopenharmony_ci mod_timer(&dwork->timer, jiffies + delay); 168562306a36Sopenharmony_ci} 168662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wiphy_delayed_work_queue); 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_civoid wiphy_delayed_work_cancel(struct wiphy *wiphy, 168962306a36Sopenharmony_ci struct wiphy_delayed_work *dwork) 169062306a36Sopenharmony_ci{ 169162306a36Sopenharmony_ci lockdep_assert_held(&wiphy->mtx); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci del_timer_sync(&dwork->timer); 169462306a36Sopenharmony_ci wiphy_work_cancel(wiphy, &dwork->work); 169562306a36Sopenharmony_ci} 169662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wiphy_delayed_work_cancel); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_civoid wiphy_delayed_work_flush(struct wiphy *wiphy, 169962306a36Sopenharmony_ci struct wiphy_delayed_work *dwork) 170062306a36Sopenharmony_ci{ 170162306a36Sopenharmony_ci lockdep_assert_held(&wiphy->mtx); 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci del_timer_sync(&dwork->timer); 170462306a36Sopenharmony_ci wiphy_work_flush(wiphy, &dwork->work); 170562306a36Sopenharmony_ci} 170662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wiphy_delayed_work_flush); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic int __init cfg80211_init(void) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci int err; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci err = register_pernet_device(&cfg80211_pernet_ops); 171362306a36Sopenharmony_ci if (err) 171462306a36Sopenharmony_ci goto out_fail_pernet; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci err = wiphy_sysfs_init(); 171762306a36Sopenharmony_ci if (err) 171862306a36Sopenharmony_ci goto out_fail_sysfs; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci err = register_netdevice_notifier(&cfg80211_netdev_notifier); 172162306a36Sopenharmony_ci if (err) 172262306a36Sopenharmony_ci goto out_fail_notifier; 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci err = nl80211_init(); 172562306a36Sopenharmony_ci if (err) 172662306a36Sopenharmony_ci goto out_fail_nl80211; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci err = regulatory_init(); 173162306a36Sopenharmony_ci if (err) 173262306a36Sopenharmony_ci goto out_fail_reg; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci cfg80211_wq = alloc_ordered_workqueue("cfg80211", WQ_MEM_RECLAIM); 173562306a36Sopenharmony_ci if (!cfg80211_wq) { 173662306a36Sopenharmony_ci err = -ENOMEM; 173762306a36Sopenharmony_ci goto out_fail_wq; 173862306a36Sopenharmony_ci } 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci return 0; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ciout_fail_wq: 174362306a36Sopenharmony_ci regulatory_exit(); 174462306a36Sopenharmony_ciout_fail_reg: 174562306a36Sopenharmony_ci debugfs_remove(ieee80211_debugfs_dir); 174662306a36Sopenharmony_ci nl80211_exit(); 174762306a36Sopenharmony_ciout_fail_nl80211: 174862306a36Sopenharmony_ci unregister_netdevice_notifier(&cfg80211_netdev_notifier); 174962306a36Sopenharmony_ciout_fail_notifier: 175062306a36Sopenharmony_ci wiphy_sysfs_exit(); 175162306a36Sopenharmony_ciout_fail_sysfs: 175262306a36Sopenharmony_ci unregister_pernet_device(&cfg80211_pernet_ops); 175362306a36Sopenharmony_ciout_fail_pernet: 175462306a36Sopenharmony_ci return err; 175562306a36Sopenharmony_ci} 175662306a36Sopenharmony_cifs_initcall(cfg80211_init); 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_cistatic void __exit cfg80211_exit(void) 175962306a36Sopenharmony_ci{ 176062306a36Sopenharmony_ci debugfs_remove(ieee80211_debugfs_dir); 176162306a36Sopenharmony_ci nl80211_exit(); 176262306a36Sopenharmony_ci unregister_netdevice_notifier(&cfg80211_netdev_notifier); 176362306a36Sopenharmony_ci wiphy_sysfs_exit(); 176462306a36Sopenharmony_ci regulatory_exit(); 176562306a36Sopenharmony_ci unregister_pernet_device(&cfg80211_pernet_ops); 176662306a36Sopenharmony_ci destroy_workqueue(cfg80211_wq); 176762306a36Sopenharmony_ci} 176862306a36Sopenharmony_cimodule_exit(cfg80211_exit); 1769