18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This is the linux wireless configuration interface. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> 68c2ecf20Sopenharmony_ci * Copyright 2013-2014 Intel Mobile Communications GmbH 78c2ecf20Sopenharmony_ci * Copyright 2015-2017 Intel Deutschland GmbH 88c2ecf20Sopenharmony_ci * Copyright (C) 2018-2021 Intel Corporation 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/if.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/list.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/nl80211.h> 198c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 208c2ecf20Sopenharmony_ci#include <linux/notifier.h> 218c2ecf20Sopenharmony_ci#include <linux/device.h> 228c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 238c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 248c2ecf20Sopenharmony_ci#include <linux/sched.h> 258c2ecf20Sopenharmony_ci#include <net/genetlink.h> 268c2ecf20Sopenharmony_ci#include <net/cfg80211.h> 278c2ecf20Sopenharmony_ci#include "nl80211.h" 288c2ecf20Sopenharmony_ci#include "core.h" 298c2ecf20Sopenharmony_ci#include "sysfs.h" 308c2ecf20Sopenharmony_ci#include "debugfs.h" 318c2ecf20Sopenharmony_ci#include "wext-compat.h" 328c2ecf20Sopenharmony_ci#include "rdev-ops.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* name for sysfs, %d is appended */ 358c2ecf20Sopenharmony_ci#define PHY_NAME "phy" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johannes Berg"); 388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("wireless configuration support"); 408c2ecf20Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* RCU-protected (and RTNL for writers) */ 438c2ecf20Sopenharmony_ciLIST_HEAD(cfg80211_rdev_list); 448c2ecf20Sopenharmony_ciint cfg80211_rdev_list_generation; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* for debugfs */ 478c2ecf20Sopenharmony_cistatic struct dentry *ieee80211_debugfs_dir; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* for the cleanup, scan and event works */ 508c2ecf20Sopenharmony_cistruct workqueue_struct *cfg80211_wq; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic bool cfg80211_disable_40mhz_24ghz; 538c2ecf20Sopenharmony_cimodule_param(cfg80211_disable_40mhz_24ghz, bool, 0644); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, 558c2ecf20Sopenharmony_ci "Disable 40MHz support in the 2.4GHz band"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct cfg80211_registered_device *result = NULL, *rdev; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci ASSERT_RTNL(); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci list_for_each_entry(rdev, &cfg80211_rdev_list, list) { 648c2ecf20Sopenharmony_ci if (rdev->wiphy_idx == wiphy_idx) { 658c2ecf20Sopenharmony_ci result = rdev; 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return result; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ciint get_wiphy_idx(struct wiphy *wiphy) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return rdev->wiphy_idx; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistruct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ASSERT_RTNL(); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); 878c2ecf20Sopenharmony_ci if (!rdev) 888c2ecf20Sopenharmony_ci return NULL; 898c2ecf20Sopenharmony_ci return &rdev->wiphy; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev, 938c2ecf20Sopenharmony_ci const char *newname) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev2; 968c2ecf20Sopenharmony_ci int wiphy_idx, taken = -1, digits; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci ASSERT_RTNL(); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (strlen(newname) > NL80211_WIPHY_NAME_MAXLEN) 1018c2ecf20Sopenharmony_ci return -EINVAL; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* prohibit calling the thing phy%d when %d is not its number */ 1048c2ecf20Sopenharmony_ci sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); 1058c2ecf20Sopenharmony_ci if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) { 1068c2ecf20Sopenharmony_ci /* count number of places needed to print wiphy_idx */ 1078c2ecf20Sopenharmony_ci digits = 1; 1088c2ecf20Sopenharmony_ci while (wiphy_idx /= 10) 1098c2ecf20Sopenharmony_ci digits++; 1108c2ecf20Sopenharmony_ci /* 1118c2ecf20Sopenharmony_ci * deny the name if it is phy<idx> where <idx> is printed 1128c2ecf20Sopenharmony_ci * without leading zeroes. taken == strlen(newname) here 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci if (taken == strlen(PHY_NAME) + digits) 1158c2ecf20Sopenharmony_ci return -EINVAL; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* Ensure another device does not already have this name. */ 1198c2ecf20Sopenharmony_ci list_for_each_entry(rdev2, &cfg80211_rdev_list, list) 1208c2ecf20Sopenharmony_ci if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0) 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciint cfg80211_dev_rename(struct cfg80211_registered_device *rdev, 1278c2ecf20Sopenharmony_ci char *newname) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci int result; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Ignore nop renames */ 1348c2ecf20Sopenharmony_ci if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0) 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci result = cfg80211_dev_check_name(rdev, newname); 1388c2ecf20Sopenharmony_ci if (result < 0) 1398c2ecf20Sopenharmony_ci return result; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci result = device_rename(&rdev->wiphy.dev, newname); 1428c2ecf20Sopenharmony_ci if (result) 1438c2ecf20Sopenharmony_ci return result; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(rdev->wiphy.debugfsdir)) 1468c2ecf20Sopenharmony_ci debugfs_rename(rdev->wiphy.debugfsdir->d_parent, 1478c2ecf20Sopenharmony_ci rdev->wiphy.debugfsdir, 1488c2ecf20Sopenharmony_ci rdev->wiphy.debugfsdir->d_parent, newname); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciint cfg80211_switch_netns(struct cfg80211_registered_device *rdev, 1568c2ecf20Sopenharmony_ci struct net *net) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct wireless_dev *wdev; 1598c2ecf20Sopenharmony_ci int err = 0; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK)) 1628c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 1658c2ecf20Sopenharmony_ci if (!wdev->netdev) 1668c2ecf20Sopenharmony_ci continue; 1678c2ecf20Sopenharmony_ci wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; 1688c2ecf20Sopenharmony_ci err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); 1698c2ecf20Sopenharmony_ci if (err) 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci wdev->netdev->features |= NETIF_F_NETNS_LOCAL; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (err) { 1758c2ecf20Sopenharmony_ci /* failed -- clean up to old netns */ 1768c2ecf20Sopenharmony_ci net = wiphy_net(&rdev->wiphy); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(wdev, 1798c2ecf20Sopenharmony_ci &rdev->wiphy.wdev_list, 1808c2ecf20Sopenharmony_ci list) { 1818c2ecf20Sopenharmony_ci if (!wdev->netdev) 1828c2ecf20Sopenharmony_ci continue; 1838c2ecf20Sopenharmony_ci wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; 1848c2ecf20Sopenharmony_ci err = dev_change_net_namespace(wdev->netdev, net, 1858c2ecf20Sopenharmony_ci "wlan%d"); 1868c2ecf20Sopenharmony_ci WARN_ON(err); 1878c2ecf20Sopenharmony_ci wdev->netdev->features |= NETIF_F_NETNS_LOCAL; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return err; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 1948c2ecf20Sopenharmony_ci if (!wdev->netdev) 1958c2ecf20Sopenharmony_ci continue; 1968c2ecf20Sopenharmony_ci nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci wiphy_net_set(&rdev->wiphy, net); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev)); 2038c2ecf20Sopenharmony_ci WARN_ON(err); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); 2068c2ecf20Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 2078c2ecf20Sopenharmony_ci if (!wdev->netdev) 2088c2ecf20Sopenharmony_ci continue; 2098c2ecf20Sopenharmony_ci nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = data; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci rdev_rfkill_poll(rdev); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_civoid cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, 2238c2ecf20Sopenharmony_ci struct wireless_dev *wdev) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci ASSERT_RTNL(); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)) 2288c2ecf20Sopenharmony_ci return; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (!wdev_running(wdev)) 2318c2ecf20Sopenharmony_ci return; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci rdev_stop_p2p_device(rdev, wdev); 2348c2ecf20Sopenharmony_ci wdev->is_running = false; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci rdev->opencount--; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (rdev->scan_req && rdev->scan_req->wdev == wdev) { 2398c2ecf20Sopenharmony_ci if (WARN_ON(!rdev->scan_req->notified && 2408c2ecf20Sopenharmony_ci (!rdev->int_scan_req || 2418c2ecf20Sopenharmony_ci !rdev->int_scan_req->notified))) 2428c2ecf20Sopenharmony_ci rdev->scan_req->info.aborted = true; 2438c2ecf20Sopenharmony_ci ___cfg80211_scan_done(rdev, false); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_civoid cfg80211_stop_nan(struct cfg80211_registered_device *rdev, 2488c2ecf20Sopenharmony_ci struct wireless_dev *wdev) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci ASSERT_RTNL(); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN)) 2538c2ecf20Sopenharmony_ci return; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (!wdev_running(wdev)) 2568c2ecf20Sopenharmony_ci return; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci rdev_stop_nan(rdev, wdev); 2598c2ecf20Sopenharmony_ci wdev->is_running = false; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci rdev->opencount--; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_civoid cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 2678c2ecf20Sopenharmony_ci struct wireless_dev *wdev; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ASSERT_RTNL(); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 2728c2ecf20Sopenharmony_ci if (wdev->netdev) { 2738c2ecf20Sopenharmony_ci dev_close(wdev->netdev); 2748c2ecf20Sopenharmony_ci continue; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci /* otherwise, check iftype */ 2778c2ecf20Sopenharmony_ci switch (wdev->iftype) { 2788c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 2798c2ecf20Sopenharmony_ci cfg80211_stop_p2p_device(rdev, wdev); 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci case NL80211_IFTYPE_NAN: 2828c2ecf20Sopenharmony_ci cfg80211_stop_nan(rdev, wdev); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci default: 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int cfg80211_rfkill_set_block(void *data, bool blocked) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = data; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (!blocked) 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci rtnl_lock(); 2998c2ecf20Sopenharmony_ci cfg80211_shutdown_all_interfaces(&rdev->wiphy); 3008c2ecf20Sopenharmony_ci rtnl_unlock(); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void cfg80211_rfkill_block_work(struct work_struct *work) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 3108c2ecf20Sopenharmony_ci rfkill_block); 3118c2ecf20Sopenharmony_ci cfg80211_rfkill_set_block(rdev, true); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void cfg80211_event_work(struct work_struct *work) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 3198c2ecf20Sopenharmony_ci event_work); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci rtnl_lock(); 3228c2ecf20Sopenharmony_ci cfg80211_process_rdev_events(rdev); 3238c2ecf20Sopenharmony_ci rtnl_unlock(); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_civoid cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct wireless_dev *wdev, *tmp; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ASSERT_RTNL(); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) { 3338c2ecf20Sopenharmony_ci if (wdev->nl_owner_dead) 3348c2ecf20Sopenharmony_ci rdev_del_virtual_intf(rdev, wdev); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void cfg80211_destroy_iface_wk(struct work_struct *work) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 3438c2ecf20Sopenharmony_ci destroy_work); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci rtnl_lock(); 3468c2ecf20Sopenharmony_ci cfg80211_destroy_ifaces(rdev); 3478c2ecf20Sopenharmony_ci rtnl_unlock(); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic void cfg80211_sched_scan_stop_wk(struct work_struct *work) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev; 3538c2ecf20Sopenharmony_ci struct cfg80211_sched_scan_request *req, *tmp; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 3568c2ecf20Sopenharmony_ci sched_scan_stop_wk); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci rtnl_lock(); 3598c2ecf20Sopenharmony_ci list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) { 3608c2ecf20Sopenharmony_ci if (req->nl_owner_dead) 3618c2ecf20Sopenharmony_ci cfg80211_stop_sched_scan_req(rdev, req, false); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci rtnl_unlock(); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic void cfg80211_propagate_radar_detect_wk(struct work_struct *work) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 3718c2ecf20Sopenharmony_ci propagate_radar_detect_wk); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci rtnl_lock(); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->radar_chandef, 3768c2ecf20Sopenharmony_ci NL80211_DFS_UNAVAILABLE, 3778c2ecf20Sopenharmony_ci NL80211_RADAR_DETECTED); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci rtnl_unlock(); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void cfg80211_propagate_cac_done_wk(struct work_struct *work) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci rdev = container_of(work, struct cfg80211_registered_device, 3878c2ecf20Sopenharmony_ci propagate_cac_done_wk); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci rtnl_lock(); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->cac_done_chandef, 3928c2ecf20Sopenharmony_ci NL80211_DFS_AVAILABLE, 3938c2ecf20Sopenharmony_ci NL80211_RADAR_CAC_FINISHED); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci rtnl_unlock(); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* exported functions */ 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistruct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, 4018c2ecf20Sopenharmony_ci const char *requested_name) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci static atomic_t wiphy_counter = ATOMIC_INIT(0); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev; 4068c2ecf20Sopenharmony_ci int alloc_size; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); 4098c2ecf20Sopenharmony_ci WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); 4108c2ecf20Sopenharmony_ci WARN_ON(ops->connect && !ops->disconnect); 4118c2ecf20Sopenharmony_ci WARN_ON(ops->join_ibss && !ops->leave_ibss); 4128c2ecf20Sopenharmony_ci WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf); 4138c2ecf20Sopenharmony_ci WARN_ON(ops->add_station && !ops->del_station); 4148c2ecf20Sopenharmony_ci WARN_ON(ops->add_mpath && !ops->del_mpath); 4158c2ecf20Sopenharmony_ci WARN_ON(ops->join_mesh && !ops->leave_mesh); 4168c2ecf20Sopenharmony_ci WARN_ON(ops->start_p2p_device && !ops->stop_p2p_device); 4178c2ecf20Sopenharmony_ci WARN_ON(ops->start_ap && !ops->stop_ap); 4188c2ecf20Sopenharmony_ci WARN_ON(ops->join_ocb && !ops->leave_ocb); 4198c2ecf20Sopenharmony_ci WARN_ON(ops->suspend && !ops->resume); 4208c2ecf20Sopenharmony_ci WARN_ON(ops->sched_scan_start && !ops->sched_scan_stop); 4218c2ecf20Sopenharmony_ci WARN_ON(ops->remain_on_channel && !ops->cancel_remain_on_channel); 4228c2ecf20Sopenharmony_ci WARN_ON(ops->tdls_channel_switch && !ops->tdls_cancel_channel_switch); 4238c2ecf20Sopenharmony_ci WARN_ON(ops->add_tx_ts && !ops->del_tx_ts); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci alloc_size = sizeof(*rdev) + sizeof_priv; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci rdev = kzalloc(alloc_size, GFP_KERNEL); 4288c2ecf20Sopenharmony_ci if (!rdev) 4298c2ecf20Sopenharmony_ci return NULL; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci rdev->ops = ops; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci rdev->wiphy_idx = atomic_inc_return(&wiphy_counter); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (unlikely(rdev->wiphy_idx < 0)) { 4368c2ecf20Sopenharmony_ci /* ugh, wrapped! */ 4378c2ecf20Sopenharmony_ci atomic_dec(&wiphy_counter); 4388c2ecf20Sopenharmony_ci kfree(rdev); 4398c2ecf20Sopenharmony_ci return NULL; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* atomic_inc_return makes it start at 1, make it start at 0 */ 4438c2ecf20Sopenharmony_ci rdev->wiphy_idx--; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* give it a proper name */ 4468c2ecf20Sopenharmony_ci if (requested_name && requested_name[0]) { 4478c2ecf20Sopenharmony_ci int rv; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci rtnl_lock(); 4508c2ecf20Sopenharmony_ci rv = cfg80211_dev_check_name(rdev, requested_name); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (rv < 0) { 4538c2ecf20Sopenharmony_ci rtnl_unlock(); 4548c2ecf20Sopenharmony_ci goto use_default_name; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name); 4588c2ecf20Sopenharmony_ci rtnl_unlock(); 4598c2ecf20Sopenharmony_ci if (rv) 4608c2ecf20Sopenharmony_ci goto use_default_name; 4618c2ecf20Sopenharmony_ci } else { 4628c2ecf20Sopenharmony_ci int rv; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ciuse_default_name: 4658c2ecf20Sopenharmony_ci /* NOTE: This is *probably* safe w/out holding rtnl because of 4668c2ecf20Sopenharmony_ci * the restrictions on phy names. Probably this call could 4678c2ecf20Sopenharmony_ci * fail if some other part of the kernel (re)named a device 4688c2ecf20Sopenharmony_ci * phyX. But, might should add some locking and check return 4698c2ecf20Sopenharmony_ci * value, and use a different name if this one exists? 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_ci rv = dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); 4728c2ecf20Sopenharmony_ci if (rv < 0) { 4738c2ecf20Sopenharmony_ci kfree(rdev); 4748c2ecf20Sopenharmony_ci return NULL; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rdev->wiphy.wdev_list); 4798c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rdev->beacon_registrations); 4808c2ecf20Sopenharmony_ci spin_lock_init(&rdev->beacon_registrations_lock); 4818c2ecf20Sopenharmony_ci spin_lock_init(&rdev->bss_lock); 4828c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rdev->bss_list); 4838c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rdev->sched_scan_req_list); 4848c2ecf20Sopenharmony_ci INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); 4858c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk, 4868c2ecf20Sopenharmony_ci cfg80211_dfs_channels_update_work); 4878c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 4888c2ecf20Sopenharmony_ci rdev->wiphy.wext = &cfg80211_wext_handler; 4898c2ecf20Sopenharmony_ci#endif 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci device_initialize(&rdev->wiphy.dev); 4928c2ecf20Sopenharmony_ci rdev->wiphy.dev.class = &ieee80211_class; 4938c2ecf20Sopenharmony_ci rdev->wiphy.dev.platform_data = rdev; 4948c2ecf20Sopenharmony_ci device_enable_async_suspend(&rdev->wiphy.dev); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); 4978c2ecf20Sopenharmony_ci INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk); 4988c2ecf20Sopenharmony_ci INIT_WORK(&rdev->sched_scan_res_wk, cfg80211_sched_scan_results_wk); 4998c2ecf20Sopenharmony_ci INIT_WORK(&rdev->propagate_radar_detect_wk, 5008c2ecf20Sopenharmony_ci cfg80211_propagate_radar_detect_wk); 5018c2ecf20Sopenharmony_ci INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk); 5028c2ecf20Sopenharmony_ci INIT_WORK(&rdev->mgmt_registrations_update_wk, 5038c2ecf20Sopenharmony_ci cfg80211_mgmt_registrations_update_wk); 5048c2ecf20Sopenharmony_ci spin_lock_init(&rdev->mgmt_registrations_lock); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_DEFAULT_PS 5078c2ecf20Sopenharmony_ci rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; 5088c2ecf20Sopenharmony_ci#endif 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci wiphy_net_set(&rdev->wiphy, &init_net); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; 5138c2ecf20Sopenharmony_ci rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), 5148c2ecf20Sopenharmony_ci &rdev->wiphy.dev, RFKILL_TYPE_WLAN, 5158c2ecf20Sopenharmony_ci &rdev->rfkill_ops, rdev); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (!rdev->rfkill) { 5188c2ecf20Sopenharmony_ci wiphy_free(&rdev->wiphy); 5198c2ecf20Sopenharmony_ci return NULL; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work); 5238c2ecf20Sopenharmony_ci INIT_WORK(&rdev->conn_work, cfg80211_conn_work); 5248c2ecf20Sopenharmony_ci INIT_WORK(&rdev->event_work, cfg80211_event_work); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci init_waitqueue_head(&rdev->dev_wait); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* 5298c2ecf20Sopenharmony_ci * Initialize wiphy parameters to IEEE 802.11 MIB default values. 5308c2ecf20Sopenharmony_ci * Fragmentation and RTS threshold are disabled by default with the 5318c2ecf20Sopenharmony_ci * special -1 value. 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_ci rdev->wiphy.retry_short = 7; 5348c2ecf20Sopenharmony_ci rdev->wiphy.retry_long = 4; 5358c2ecf20Sopenharmony_ci rdev->wiphy.frag_threshold = (u32) -1; 5368c2ecf20Sopenharmony_ci rdev->wiphy.rts_threshold = (u32) -1; 5378c2ecf20Sopenharmony_ci rdev->wiphy.coverage_class = 0; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci rdev->wiphy.max_num_csa_counters = 1; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci rdev->wiphy.max_sched_scan_plans = 1; 5428c2ecf20Sopenharmony_ci rdev->wiphy.max_sched_scan_plan_interval = U32_MAX; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci return &rdev->wiphy; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wiphy_new_nm); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic int wiphy_verify_combinations(struct wiphy *wiphy) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci const struct ieee80211_iface_combination *c; 5518c2ecf20Sopenharmony_ci int i, j; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci for (i = 0; i < wiphy->n_iface_combinations; i++) { 5548c2ecf20Sopenharmony_ci u32 cnt = 0; 5558c2ecf20Sopenharmony_ci u16 all_iftypes = 0; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci c = &wiphy->iface_combinations[i]; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* 5608c2ecf20Sopenharmony_ci * Combinations with just one interface aren't real, 5618c2ecf20Sopenharmony_ci * however we make an exception for DFS. 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_ci if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths)) 5648c2ecf20Sopenharmony_ci return -EINVAL; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Need at least one channel */ 5678c2ecf20Sopenharmony_ci if (WARN_ON(!c->num_different_channels)) 5688c2ecf20Sopenharmony_ci return -EINVAL; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* 5718c2ecf20Sopenharmony_ci * Put a sane limit on maximum number of different 5728c2ecf20Sopenharmony_ci * channels to simplify channel accounting code. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci if (WARN_ON(c->num_different_channels > 5758c2ecf20Sopenharmony_ci CFG80211_MAX_NUM_DIFFERENT_CHANNELS)) 5768c2ecf20Sopenharmony_ci return -EINVAL; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* DFS only works on one channel. */ 5798c2ecf20Sopenharmony_ci if (WARN_ON(c->radar_detect_widths && 5808c2ecf20Sopenharmony_ci (c->num_different_channels > 1))) 5818c2ecf20Sopenharmony_ci return -EINVAL; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (WARN_ON(!c->n_limits)) 5848c2ecf20Sopenharmony_ci return -EINVAL; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci for (j = 0; j < c->n_limits; j++) { 5878c2ecf20Sopenharmony_ci u16 types = c->limits[j].types; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* interface types shouldn't overlap */ 5908c2ecf20Sopenharmony_ci if (WARN_ON(types & all_iftypes)) 5918c2ecf20Sopenharmony_ci return -EINVAL; 5928c2ecf20Sopenharmony_ci all_iftypes |= types; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (WARN_ON(!c->limits[j].max)) 5958c2ecf20Sopenharmony_ci return -EINVAL; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* Shouldn't list software iftypes in combinations! */ 5988c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->software_iftypes & types)) 5998c2ecf20Sopenharmony_ci return -EINVAL; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* Only a single P2P_DEVICE can be allowed */ 6028c2ecf20Sopenharmony_ci if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) && 6038c2ecf20Sopenharmony_ci c->limits[j].max > 1)) 6048c2ecf20Sopenharmony_ci return -EINVAL; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* Only a single NAN can be allowed */ 6078c2ecf20Sopenharmony_ci if (WARN_ON(types & BIT(NL80211_IFTYPE_NAN) && 6088c2ecf20Sopenharmony_ci c->limits[j].max > 1)) 6098c2ecf20Sopenharmony_ci return -EINVAL; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* 6128c2ecf20Sopenharmony_ci * This isn't well-defined right now. If you have an 6138c2ecf20Sopenharmony_ci * IBSS interface, then its beacon interval may change 6148c2ecf20Sopenharmony_ci * by joining other networks, and nothing prevents it 6158c2ecf20Sopenharmony_ci * from doing that. 6168c2ecf20Sopenharmony_ci * So technically we probably shouldn't even allow AP 6178c2ecf20Sopenharmony_ci * and IBSS in the same interface, but it seems that 6188c2ecf20Sopenharmony_ci * some drivers support that, possibly only with fixed 6198c2ecf20Sopenharmony_ci * beacon intervals for IBSS. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ci if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) && 6228c2ecf20Sopenharmony_ci c->beacon_int_min_gcd)) { 6238c2ecf20Sopenharmony_ci return -EINVAL; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci cnt += c->limits[j].max; 6278c2ecf20Sopenharmony_ci /* 6288c2ecf20Sopenharmony_ci * Don't advertise an unsupported type 6298c2ecf20Sopenharmony_ci * in a combination. 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_ci if (WARN_ON((wiphy->interface_modes & types) != types)) 6328c2ecf20Sopenharmony_ci return -EINVAL; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci#ifndef CONFIG_WIRELESS_WDS 6368c2ecf20Sopenharmony_ci if (WARN_ON(all_iftypes & BIT(NL80211_IFTYPE_WDS))) 6378c2ecf20Sopenharmony_ci return -EINVAL; 6388c2ecf20Sopenharmony_ci#endif 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* You can't even choose that many! */ 6418c2ecf20Sopenharmony_ci if (WARN_ON(cnt < c->max_interfaces)) 6428c2ecf20Sopenharmony_ci return -EINVAL; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ciint wiphy_register(struct wiphy *wiphy) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 6518c2ecf20Sopenharmony_ci int res; 6528c2ecf20Sopenharmony_ci enum nl80211_band band; 6538c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 6548c2ecf20Sopenharmony_ci bool have_band = false; 6558c2ecf20Sopenharmony_ci int i; 6568c2ecf20Sopenharmony_ci u16 ifmodes = wiphy->interface_modes; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 6598c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->wowlan && 6608c2ecf20Sopenharmony_ci (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && 6618c2ecf20Sopenharmony_ci !(wiphy->wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) 6628c2ecf20Sopenharmony_ci return -EINVAL; 6638c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->wowlan && 6648c2ecf20Sopenharmony_ci !wiphy->wowlan->flags && !wiphy->wowlan->n_patterns && 6658c2ecf20Sopenharmony_ci !wiphy->wowlan->tcp)) 6668c2ecf20Sopenharmony_ci return -EINVAL; 6678c2ecf20Sopenharmony_ci#endif 6688c2ecf20Sopenharmony_ci if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) && 6698c2ecf20Sopenharmony_ci (!rdev->ops->tdls_channel_switch || 6708c2ecf20Sopenharmony_ci !rdev->ops->tdls_cancel_channel_switch))) 6718c2ecf20Sopenharmony_ci return -EINVAL; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN)) && 6748c2ecf20Sopenharmony_ci (!rdev->ops->start_nan || !rdev->ops->stop_nan || 6758c2ecf20Sopenharmony_ci !rdev->ops->add_nan_func || !rdev->ops->del_nan_func || 6768c2ecf20Sopenharmony_ci !(wiphy->nan_supported_bands & BIT(NL80211_BAND_2GHZ))))) 6778c2ecf20Sopenharmony_ci return -EINVAL; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci#ifndef CONFIG_WIRELESS_WDS 6808c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))) 6818c2ecf20Sopenharmony_ci return -EINVAL; 6828c2ecf20Sopenharmony_ci#endif 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->pmsr_capa && !wiphy->pmsr_capa->ftm.supported)) 6858c2ecf20Sopenharmony_ci return -EINVAL; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (wiphy->pmsr_capa && wiphy->pmsr_capa->ftm.supported) { 6888c2ecf20Sopenharmony_ci if (WARN_ON(!wiphy->pmsr_capa->ftm.asap && 6898c2ecf20Sopenharmony_ci !wiphy->pmsr_capa->ftm.non_asap)) 6908c2ecf20Sopenharmony_ci return -EINVAL; 6918c2ecf20Sopenharmony_ci if (WARN_ON(!wiphy->pmsr_capa->ftm.preambles || 6928c2ecf20Sopenharmony_ci !wiphy->pmsr_capa->ftm.bandwidths)) 6938c2ecf20Sopenharmony_ci return -EINVAL; 6948c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->pmsr_capa->ftm.preambles & 6958c2ecf20Sopenharmony_ci ~(BIT(NL80211_PREAMBLE_LEGACY) | 6968c2ecf20Sopenharmony_ci BIT(NL80211_PREAMBLE_HT) | 6978c2ecf20Sopenharmony_ci BIT(NL80211_PREAMBLE_VHT) | 6988c2ecf20Sopenharmony_ci BIT(NL80211_PREAMBLE_HE) | 6998c2ecf20Sopenharmony_ci BIT(NL80211_PREAMBLE_DMG)))) 7008c2ecf20Sopenharmony_ci return -EINVAL; 7018c2ecf20Sopenharmony_ci if (WARN_ON((wiphy->pmsr_capa->ftm.trigger_based || 7028c2ecf20Sopenharmony_ci wiphy->pmsr_capa->ftm.non_trigger_based) && 7038c2ecf20Sopenharmony_ci !(wiphy->pmsr_capa->ftm.preambles & 7048c2ecf20Sopenharmony_ci BIT(NL80211_PREAMBLE_HE)))) 7058c2ecf20Sopenharmony_ci return -EINVAL; 7068c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->pmsr_capa->ftm.bandwidths & 7078c2ecf20Sopenharmony_ci ~(BIT(NL80211_CHAN_WIDTH_20_NOHT) | 7088c2ecf20Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_20) | 7098c2ecf20Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_40) | 7108c2ecf20Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_80) | 7118c2ecf20Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_80P80) | 7128c2ecf20Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_160) | 7138c2ecf20Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_5) | 7148c2ecf20Sopenharmony_ci BIT(NL80211_CHAN_WIDTH_10)))) 7158c2ecf20Sopenharmony_ci return -EINVAL; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * if a wiphy has unsupported modes for regulatory channel enforcement, 7208c2ecf20Sopenharmony_ci * opt-out of enforcement checking 7218c2ecf20Sopenharmony_ci */ 7228c2ecf20Sopenharmony_ci if (wiphy->interface_modes & ~(BIT(NL80211_IFTYPE_STATION) | 7238c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_P2P_CLIENT) | 7248c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_AP) | 7258c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_P2P_GO) | 7268c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_ADHOC) | 7278c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_P2P_DEVICE) | 7288c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_NAN) | 7298c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_AP_VLAN) | 7308c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_MONITOR))) 7318c2ecf20Sopenharmony_ci wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) && 7348c2ecf20Sopenharmony_ci (wiphy->regulatory_flags & 7358c2ecf20Sopenharmony_ci (REGULATORY_CUSTOM_REG | 7368c2ecf20Sopenharmony_ci REGULATORY_STRICT_REG | 7378c2ecf20Sopenharmony_ci REGULATORY_COUNTRY_IE_FOLLOW_POWER | 7388c2ecf20Sopenharmony_ci REGULATORY_COUNTRY_IE_IGNORE)))) 7398c2ecf20Sopenharmony_ci return -EINVAL; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->coalesce && 7428c2ecf20Sopenharmony_ci (!wiphy->coalesce->n_rules || 7438c2ecf20Sopenharmony_ci !wiphy->coalesce->n_patterns) && 7448c2ecf20Sopenharmony_ci (!wiphy->coalesce->pattern_min_len || 7458c2ecf20Sopenharmony_ci wiphy->coalesce->pattern_min_len > 7468c2ecf20Sopenharmony_ci wiphy->coalesce->pattern_max_len))) 7478c2ecf20Sopenharmony_ci return -EINVAL; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->ap_sme_capa && 7508c2ecf20Sopenharmony_ci !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) 7518c2ecf20Sopenharmony_ci return -EINVAL; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) 7548c2ecf20Sopenharmony_ci return -EINVAL; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->addresses && 7578c2ecf20Sopenharmony_ci !is_zero_ether_addr(wiphy->perm_addr) && 7588c2ecf20Sopenharmony_ci memcmp(wiphy->perm_addr, wiphy->addresses[0].addr, 7598c2ecf20Sopenharmony_ci ETH_ALEN))) 7608c2ecf20Sopenharmony_ci return -EINVAL; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->max_acl_mac_addrs && 7638c2ecf20Sopenharmony_ci (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) || 7648c2ecf20Sopenharmony_ci !rdev->ops->set_mac_acl))) 7658c2ecf20Sopenharmony_ci return -EINVAL; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* assure only valid behaviours are flagged by driver 7688c2ecf20Sopenharmony_ci * hence subtract 2 as bit 0 is invalid. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->bss_select_support && 7718c2ecf20Sopenharmony_ci (wiphy->bss_select_support & ~(BIT(__NL80211_BSS_SELECT_ATTR_AFTER_LAST) - 2)))) 7728c2ecf20Sopenharmony_ci return -EINVAL; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci if (WARN_ON(wiphy_ext_feature_isset(&rdev->wiphy, 7758c2ecf20Sopenharmony_ci NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X) && 7768c2ecf20Sopenharmony_ci (!rdev->ops->set_pmk || !rdev->ops->del_pmk))) 7778c2ecf20Sopenharmony_ci return -EINVAL; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (WARN_ON(!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) && 7808c2ecf20Sopenharmony_ci rdev->ops->update_connect_params)) 7818c2ecf20Sopenharmony_ci return -EINVAL; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (wiphy->addresses) 7848c2ecf20Sopenharmony_ci memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci /* sanity check ifmodes */ 7878c2ecf20Sopenharmony_ci WARN_ON(!ifmodes); 7888c2ecf20Sopenharmony_ci ifmodes &= ((1 << NUM_NL80211_IFTYPES) - 1) & ~1; 7898c2ecf20Sopenharmony_ci if (WARN_ON(ifmodes != wiphy->interface_modes)) 7908c2ecf20Sopenharmony_ci wiphy->interface_modes = ifmodes; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci res = wiphy_verify_combinations(wiphy); 7938c2ecf20Sopenharmony_ci if (res) 7948c2ecf20Sopenharmony_ci return res; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* sanity check supported bands/channels */ 7978c2ecf20Sopenharmony_ci for (band = 0; band < NUM_NL80211_BANDS; band++) { 7988c2ecf20Sopenharmony_ci u16 types = 0; 7998c2ecf20Sopenharmony_ci bool have_he = false; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci sband = wiphy->bands[band]; 8028c2ecf20Sopenharmony_ci if (!sband) 8038c2ecf20Sopenharmony_ci continue; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci sband->band = band; 8068c2ecf20Sopenharmony_ci if (WARN_ON(!sband->n_channels)) 8078c2ecf20Sopenharmony_ci return -EINVAL; 8088c2ecf20Sopenharmony_ci /* 8098c2ecf20Sopenharmony_ci * on 60GHz or sub-1Ghz band, there are no legacy rates, so 8108c2ecf20Sopenharmony_ci * n_bitrates is 0 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_ci if (WARN_ON((band != NL80211_BAND_60GHZ && 8138c2ecf20Sopenharmony_ci band != NL80211_BAND_S1GHZ) && 8148c2ecf20Sopenharmony_ci !sband->n_bitrates)) 8158c2ecf20Sopenharmony_ci return -EINVAL; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (WARN_ON(band == NL80211_BAND_6GHZ && 8188c2ecf20Sopenharmony_ci (sband->ht_cap.ht_supported || 8198c2ecf20Sopenharmony_ci sband->vht_cap.vht_supported))) 8208c2ecf20Sopenharmony_ci return -EINVAL; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* 8238c2ecf20Sopenharmony_ci * Since cfg80211_disable_40mhz_24ghz is global, we can 8248c2ecf20Sopenharmony_ci * modify the sband's ht data even if the driver uses a 8258c2ecf20Sopenharmony_ci * global structure for that. 8268c2ecf20Sopenharmony_ci */ 8278c2ecf20Sopenharmony_ci if (cfg80211_disable_40mhz_24ghz && 8288c2ecf20Sopenharmony_ci band == NL80211_BAND_2GHZ && 8298c2ecf20Sopenharmony_ci sband->ht_cap.ht_supported) { 8308c2ecf20Sopenharmony_ci sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; 8318c2ecf20Sopenharmony_ci sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci /* 8358c2ecf20Sopenharmony_ci * Since we use a u32 for rate bitmaps in 8368c2ecf20Sopenharmony_ci * ieee80211_get_response_rate, we cannot 8378c2ecf20Sopenharmony_ci * have more than 32 legacy rates. 8388c2ecf20Sopenharmony_ci */ 8398c2ecf20Sopenharmony_ci if (WARN_ON(sband->n_bitrates > 32)) 8408c2ecf20Sopenharmony_ci return -EINVAL; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 8438c2ecf20Sopenharmony_ci sband->channels[i].orig_flags = 8448c2ecf20Sopenharmony_ci sband->channels[i].flags; 8458c2ecf20Sopenharmony_ci sband->channels[i].orig_mag = INT_MAX; 8468c2ecf20Sopenharmony_ci sband->channels[i].orig_mpwr = 8478c2ecf20Sopenharmony_ci sband->channels[i].max_power; 8488c2ecf20Sopenharmony_ci sband->channels[i].band = band; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (WARN_ON(sband->channels[i].freq_offset >= 1000)) 8518c2ecf20Sopenharmony_ci return -EINVAL; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci for (i = 0; i < sband->n_iftype_data; i++) { 8558c2ecf20Sopenharmony_ci const struct ieee80211_sband_iftype_data *iftd; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci iftd = &sband->iftype_data[i]; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (WARN_ON(!iftd->types_mask)) 8608c2ecf20Sopenharmony_ci return -EINVAL; 8618c2ecf20Sopenharmony_ci if (WARN_ON(types & iftd->types_mask)) 8628c2ecf20Sopenharmony_ci return -EINVAL; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci /* at least one piece of information must be present */ 8658c2ecf20Sopenharmony_ci if (WARN_ON(!iftd->he_cap.has_he)) 8668c2ecf20Sopenharmony_ci return -EINVAL; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci types |= iftd->types_mask; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (i == 0) 8718c2ecf20Sopenharmony_ci have_he = iftd->he_cap.has_he; 8728c2ecf20Sopenharmony_ci else 8738c2ecf20Sopenharmony_ci have_he = have_he && 8748c2ecf20Sopenharmony_ci iftd->he_cap.has_he; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (WARN_ON(!have_he && band == NL80211_BAND_6GHZ)) 8788c2ecf20Sopenharmony_ci return -EINVAL; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci have_band = true; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci if (!have_band) { 8848c2ecf20Sopenharmony_ci WARN_ON(1); 8858c2ecf20Sopenharmony_ci return -EINVAL; 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { 8898c2ecf20Sopenharmony_ci /* 8908c2ecf20Sopenharmony_ci * Validate we have a policy (can be explicitly set to 8918c2ecf20Sopenharmony_ci * VENDOR_CMD_RAW_DATA which is non-NULL) and also that 8928c2ecf20Sopenharmony_ci * we have at least one of doit/dumpit. 8938c2ecf20Sopenharmony_ci */ 8948c2ecf20Sopenharmony_ci if (WARN_ON(!rdev->wiphy.vendor_commands[i].policy)) 8958c2ecf20Sopenharmony_ci return -EINVAL; 8968c2ecf20Sopenharmony_ci if (WARN_ON(!rdev->wiphy.vendor_commands[i].doit && 8978c2ecf20Sopenharmony_ci !rdev->wiphy.vendor_commands[i].dumpit)) 8988c2ecf20Sopenharmony_ci return -EINVAL; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 9028c2ecf20Sopenharmony_ci if (WARN_ON(rdev->wiphy.wowlan && rdev->wiphy.wowlan->n_patterns && 9038c2ecf20Sopenharmony_ci (!rdev->wiphy.wowlan->pattern_min_len || 9048c2ecf20Sopenharmony_ci rdev->wiphy.wowlan->pattern_min_len > 9058c2ecf20Sopenharmony_ci rdev->wiphy.wowlan->pattern_max_len))) 9068c2ecf20Sopenharmony_ci return -EINVAL; 9078c2ecf20Sopenharmony_ci#endif 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /* check and set up bitrates */ 9108c2ecf20Sopenharmony_ci ieee80211_set_bitrate_flags(wiphy); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci rtnl_lock(); 9158c2ecf20Sopenharmony_ci res = device_add(&rdev->wiphy.dev); 9168c2ecf20Sopenharmony_ci if (res) { 9178c2ecf20Sopenharmony_ci rtnl_unlock(); 9188c2ecf20Sopenharmony_ci return res; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci list_add_rcu(&rdev->list, &cfg80211_rdev_list); 9228c2ecf20Sopenharmony_ci cfg80211_rdev_list_generation++; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* add to debugfs */ 9258c2ecf20Sopenharmony_ci rdev->wiphy.debugfsdir = debugfs_create_dir(wiphy_name(&rdev->wiphy), 9268c2ecf20Sopenharmony_ci ieee80211_debugfs_dir); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci cfg80211_debugfs_rdev_add(rdev); 9298c2ecf20Sopenharmony_ci nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* set up regulatory info */ 9328c2ecf20Sopenharmony_ci wiphy_regulatory_register(wiphy); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { 9358c2ecf20Sopenharmony_ci struct regulatory_request request; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci request.wiphy_idx = get_wiphy_idx(wiphy); 9388c2ecf20Sopenharmony_ci request.initiator = NL80211_REGDOM_SET_BY_DRIVER; 9398c2ecf20Sopenharmony_ci request.alpha2[0] = '9'; 9408c2ecf20Sopenharmony_ci request.alpha2[1] = '9'; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci nl80211_send_reg_change_event(&request); 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* Check that nobody globally advertises any capabilities they do not 9468c2ecf20Sopenharmony_ci * advertise on all possible interface types. 9478c2ecf20Sopenharmony_ci */ 9488c2ecf20Sopenharmony_ci if (wiphy->extended_capabilities_len && 9498c2ecf20Sopenharmony_ci wiphy->num_iftype_ext_capab && 9508c2ecf20Sopenharmony_ci wiphy->iftype_ext_capab) { 9518c2ecf20Sopenharmony_ci u8 supported_on_all, j; 9528c2ecf20Sopenharmony_ci const struct wiphy_iftype_ext_capab *capab; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci capab = wiphy->iftype_ext_capab; 9558c2ecf20Sopenharmony_ci for (j = 0; j < wiphy->extended_capabilities_len; j++) { 9568c2ecf20Sopenharmony_ci if (capab[0].extended_capabilities_len > j) 9578c2ecf20Sopenharmony_ci supported_on_all = 9588c2ecf20Sopenharmony_ci capab[0].extended_capabilities[j]; 9598c2ecf20Sopenharmony_ci else 9608c2ecf20Sopenharmony_ci supported_on_all = 0x00; 9618c2ecf20Sopenharmony_ci for (i = 1; i < wiphy->num_iftype_ext_capab; i++) { 9628c2ecf20Sopenharmony_ci if (j >= capab[i].extended_capabilities_len) { 9638c2ecf20Sopenharmony_ci supported_on_all = 0x00; 9648c2ecf20Sopenharmony_ci break; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci supported_on_all &= 9678c2ecf20Sopenharmony_ci capab[i].extended_capabilities[j]; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci if (WARN_ON(wiphy->extended_capabilities[j] & 9708c2ecf20Sopenharmony_ci ~supported_on_all)) 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci rdev->wiphy.registered = true; 9768c2ecf20Sopenharmony_ci rtnl_unlock(); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci res = rfkill_register(rdev->rfkill); 9798c2ecf20Sopenharmony_ci if (res) { 9808c2ecf20Sopenharmony_ci rfkill_destroy(rdev->rfkill); 9818c2ecf20Sopenharmony_ci rdev->rfkill = NULL; 9828c2ecf20Sopenharmony_ci wiphy_unregister(&rdev->wiphy); 9838c2ecf20Sopenharmony_ci return res; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci return 0; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wiphy_register); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_civoid wiphy_rfkill_start_polling(struct wiphy *wiphy) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (!rdev->ops->rfkill_poll) 9958c2ecf20Sopenharmony_ci return; 9968c2ecf20Sopenharmony_ci rdev->rfkill_ops.poll = cfg80211_rfkill_poll; 9978c2ecf20Sopenharmony_ci rfkill_resume_polling(rdev->rfkill); 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wiphy_rfkill_start_polling); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_civoid wiphy_rfkill_stop_polling(struct wiphy *wiphy) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci rfkill_pause_polling(rdev->rfkill); 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wiphy_rfkill_stop_polling); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_civoid wiphy_unregister(struct wiphy *wiphy) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci wait_event(rdev->dev_wait, ({ 10148c2ecf20Sopenharmony_ci int __count; 10158c2ecf20Sopenharmony_ci rtnl_lock(); 10168c2ecf20Sopenharmony_ci __count = rdev->opencount; 10178c2ecf20Sopenharmony_ci rtnl_unlock(); 10188c2ecf20Sopenharmony_ci __count == 0; })); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (rdev->rfkill) 10218c2ecf20Sopenharmony_ci rfkill_unregister(rdev->rfkill); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci rtnl_lock(); 10248c2ecf20Sopenharmony_ci nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); 10258c2ecf20Sopenharmony_ci rdev->wiphy.registered = false; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&rdev->wiphy.wdev_list)); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* 10308c2ecf20Sopenharmony_ci * First remove the hardware from everywhere, this makes 10318c2ecf20Sopenharmony_ci * it impossible to find from userspace. 10328c2ecf20Sopenharmony_ci */ 10338c2ecf20Sopenharmony_ci debugfs_remove_recursive(rdev->wiphy.debugfsdir); 10348c2ecf20Sopenharmony_ci list_del_rcu(&rdev->list); 10358c2ecf20Sopenharmony_ci synchronize_rcu(); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* 10388c2ecf20Sopenharmony_ci * If this device got a regulatory hint tell core its 10398c2ecf20Sopenharmony_ci * free to listen now to a new shiny device regulatory hint 10408c2ecf20Sopenharmony_ci */ 10418c2ecf20Sopenharmony_ci wiphy_regulatory_deregister(wiphy); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci cfg80211_rdev_list_generation++; 10448c2ecf20Sopenharmony_ci device_del(&rdev->wiphy.dev); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci rtnl_unlock(); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci flush_work(&rdev->scan_done_wk); 10498c2ecf20Sopenharmony_ci cancel_work_sync(&rdev->conn_work); 10508c2ecf20Sopenharmony_ci flush_work(&rdev->event_work); 10518c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); 10528c2ecf20Sopenharmony_ci flush_work(&rdev->destroy_work); 10538c2ecf20Sopenharmony_ci flush_work(&rdev->sched_scan_stop_wk); 10548c2ecf20Sopenharmony_ci flush_work(&rdev->propagate_radar_detect_wk); 10558c2ecf20Sopenharmony_ci flush_work(&rdev->propagate_cac_done_wk); 10568c2ecf20Sopenharmony_ci flush_work(&rdev->mgmt_registrations_update_wk); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 10598c2ecf20Sopenharmony_ci if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) 10608c2ecf20Sopenharmony_ci rdev_set_wakeup(rdev, false); 10618c2ecf20Sopenharmony_ci#endif 10628c2ecf20Sopenharmony_ci cfg80211_rdev_free_wowlan(rdev); 10638c2ecf20Sopenharmony_ci cfg80211_rdev_free_coalesce(rdev); 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wiphy_unregister); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_civoid cfg80211_dev_free(struct cfg80211_registered_device *rdev) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci struct cfg80211_internal_bss *scan, *tmp; 10708c2ecf20Sopenharmony_ci struct cfg80211_beacon_registration *reg, *treg; 10718c2ecf20Sopenharmony_ci rfkill_destroy(rdev->rfkill); 10728c2ecf20Sopenharmony_ci list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) { 10738c2ecf20Sopenharmony_ci list_del(®->list); 10748c2ecf20Sopenharmony_ci kfree(reg); 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) 10778c2ecf20Sopenharmony_ci cfg80211_put_bss(&rdev->wiphy, &scan->pub); 10788c2ecf20Sopenharmony_ci kfree(rdev); 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_civoid wiphy_free(struct wiphy *wiphy) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci put_device(&wiphy->dev); 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wiphy_free); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_civoid wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (rfkill_set_hw_state(rdev->rfkill, blocked)) 10928c2ecf20Sopenharmony_ci schedule_work(&rdev->rfkill_block); 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wiphy_rfkill_set_hw_state); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_civoid cfg80211_cqm_config_free(struct wireless_dev *wdev) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci kfree(wdev->cqm_config); 10998c2ecf20Sopenharmony_ci wdev->cqm_config = NULL; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic void __cfg80211_unregister_wdev(struct wireless_dev *wdev, bool sync) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci ASSERT_RTNL(); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci flush_work(&wdev->pmsr_free_wk); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci list_del_rcu(&wdev->list); 11138c2ecf20Sopenharmony_ci if (sync) 11148c2ecf20Sopenharmony_ci synchronize_rcu(); 11158c2ecf20Sopenharmony_ci rdev->devlist_generation++; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci cfg80211_mlme_purge_registrations(wdev); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci switch (wdev->iftype) { 11208c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 11218c2ecf20Sopenharmony_ci cfg80211_stop_p2p_device(rdev, wdev); 11228c2ecf20Sopenharmony_ci break; 11238c2ecf20Sopenharmony_ci case NL80211_IFTYPE_NAN: 11248c2ecf20Sopenharmony_ci cfg80211_stop_nan(rdev, wdev); 11258c2ecf20Sopenharmony_ci break; 11268c2ecf20Sopenharmony_ci default: 11278c2ecf20Sopenharmony_ci break; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 11318c2ecf20Sopenharmony_ci kfree_sensitive(wdev->wext.keys); 11328c2ecf20Sopenharmony_ci wdev->wext.keys = NULL; 11338c2ecf20Sopenharmony_ci#endif 11348c2ecf20Sopenharmony_ci /* only initialized if we have a netdev */ 11358c2ecf20Sopenharmony_ci if (wdev->netdev) 11368c2ecf20Sopenharmony_ci flush_work(&wdev->disconnect_wk); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci cfg80211_cqm_config_free(wdev); 11398c2ecf20Sopenharmony_ci} 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_civoid cfg80211_unregister_wdev(struct wireless_dev *wdev) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci if (WARN_ON(wdev->netdev)) 11448c2ecf20Sopenharmony_ci return; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci __cfg80211_unregister_wdev(wdev, true); 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfg80211_unregister_wdev); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_cistatic const struct device_type wiphy_type = { 11518c2ecf20Sopenharmony_ci .name = "wlan", 11528c2ecf20Sopenharmony_ci}; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_civoid cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, 11558c2ecf20Sopenharmony_ci enum nl80211_iftype iftype, int num) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci ASSERT_RTNL(); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci rdev->num_running_ifaces += num; 11608c2ecf20Sopenharmony_ci if (iftype == NL80211_IFTYPE_MONITOR) 11618c2ecf20Sopenharmony_ci rdev->num_running_monitor_ifaces += num; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_civoid __cfg80211_leave(struct cfg80211_registered_device *rdev, 11658c2ecf20Sopenharmony_ci struct wireless_dev *wdev) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct net_device *dev = wdev->netdev; 11688c2ecf20Sopenharmony_ci struct cfg80211_sched_scan_request *pos, *tmp; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci ASSERT_RTNL(); 11718c2ecf20Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci cfg80211_pmsr_wdev_down(wdev); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci switch (wdev->iftype) { 11768c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 11778c2ecf20Sopenharmony_ci __cfg80211_leave_ibss(rdev, dev, true); 11788c2ecf20Sopenharmony_ci break; 11798c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 11808c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 11818c2ecf20Sopenharmony_ci list_for_each_entry_safe(pos, tmp, &rdev->sched_scan_req_list, 11828c2ecf20Sopenharmony_ci list) { 11838c2ecf20Sopenharmony_ci if (dev == pos->dev) 11848c2ecf20Sopenharmony_ci cfg80211_stop_sched_scan_req(rdev, pos, false); 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 11888c2ecf20Sopenharmony_ci kfree(wdev->wext.ie); 11898c2ecf20Sopenharmony_ci wdev->wext.ie = NULL; 11908c2ecf20Sopenharmony_ci wdev->wext.ie_len = 0; 11918c2ecf20Sopenharmony_ci wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; 11928c2ecf20Sopenharmony_ci#endif 11938c2ecf20Sopenharmony_ci cfg80211_disconnect(rdev, dev, 11948c2ecf20Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, true); 11958c2ecf20Sopenharmony_ci break; 11968c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 11978c2ecf20Sopenharmony_ci __cfg80211_leave_mesh(rdev, dev); 11988c2ecf20Sopenharmony_ci break; 11998c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 12008c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 12018c2ecf20Sopenharmony_ci __cfg80211_stop_ap(rdev, dev, true); 12028c2ecf20Sopenharmony_ci break; 12038c2ecf20Sopenharmony_ci case NL80211_IFTYPE_OCB: 12048c2ecf20Sopenharmony_ci __cfg80211_leave_ocb(rdev, dev); 12058c2ecf20Sopenharmony_ci break; 12068c2ecf20Sopenharmony_ci case NL80211_IFTYPE_WDS: 12078c2ecf20Sopenharmony_ci /* must be handled by mac80211/driver, has no APIs */ 12088c2ecf20Sopenharmony_ci break; 12098c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 12108c2ecf20Sopenharmony_ci case NL80211_IFTYPE_NAN: 12118c2ecf20Sopenharmony_ci /* cannot happen, has no netdev */ 12128c2ecf20Sopenharmony_ci break; 12138c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 12148c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 12158c2ecf20Sopenharmony_ci /* nothing to do */ 12168c2ecf20Sopenharmony_ci break; 12178c2ecf20Sopenharmony_ci case NL80211_IFTYPE_UNSPECIFIED: 12188c2ecf20Sopenharmony_ci case NUM_NL80211_IFTYPES: 12198c2ecf20Sopenharmony_ci /* invalid */ 12208c2ecf20Sopenharmony_ci break; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci} 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_civoid cfg80211_leave(struct cfg80211_registered_device *rdev, 12258c2ecf20Sopenharmony_ci struct wireless_dev *wdev) 12268c2ecf20Sopenharmony_ci{ 12278c2ecf20Sopenharmony_ci wdev_lock(wdev); 12288c2ecf20Sopenharmony_ci __cfg80211_leave(rdev, wdev); 12298c2ecf20Sopenharmony_ci wdev_unlock(wdev); 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_civoid cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, 12338c2ecf20Sopenharmony_ci gfp_t gfp) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 12368c2ecf20Sopenharmony_ci struct cfg80211_event *ev; 12378c2ecf20Sopenharmony_ci unsigned long flags; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci trace_cfg80211_stop_iface(wiphy, wdev); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci ev = kzalloc(sizeof(*ev), gfp); 12428c2ecf20Sopenharmony_ci if (!ev) 12438c2ecf20Sopenharmony_ci return; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci ev->type = EVENT_STOPPED; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci spin_lock_irqsave(&wdev->event_lock, flags); 12488c2ecf20Sopenharmony_ci list_add_tail(&ev->list, &wdev->event_list); 12498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wdev->event_lock, flags); 12508c2ecf20Sopenharmony_ci queue_work(cfg80211_wq, &rdev->event_work); 12518c2ecf20Sopenharmony_ci} 12528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfg80211_stop_iface); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_civoid cfg80211_init_wdev(struct wireless_dev *wdev) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci mutex_init(&wdev->mtx); 12578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&wdev->event_list); 12588c2ecf20Sopenharmony_ci spin_lock_init(&wdev->event_lock); 12598c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&wdev->mgmt_registrations); 12608c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&wdev->pmsr_list); 12618c2ecf20Sopenharmony_ci spin_lock_init(&wdev->pmsr_lock); 12628c2ecf20Sopenharmony_ci INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 12658c2ecf20Sopenharmony_ci wdev->wext.default_key = -1; 12668c2ecf20Sopenharmony_ci wdev->wext.default_mgmt_key = -1; 12678c2ecf20Sopenharmony_ci wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; 12688c2ecf20Sopenharmony_ci#endif 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) 12718c2ecf20Sopenharmony_ci wdev->ps = true; 12728c2ecf20Sopenharmony_ci else 12738c2ecf20Sopenharmony_ci wdev->ps = false; 12748c2ecf20Sopenharmony_ci /* allow mac80211 to determine the timeout */ 12758c2ecf20Sopenharmony_ci wdev->ps_timeout = -1; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if ((wdev->iftype == NL80211_IFTYPE_STATION || 12788c2ecf20Sopenharmony_ci wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || 12798c2ecf20Sopenharmony_ci wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) 12808c2ecf20Sopenharmony_ci wdev->netdev->priv_flags |= IFF_DONT_BRIDGE; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk); 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_civoid cfg80211_register_wdev(struct cfg80211_registered_device *rdev, 12868c2ecf20Sopenharmony_ci struct wireless_dev *wdev) 12878c2ecf20Sopenharmony_ci{ 12888c2ecf20Sopenharmony_ci /* 12898c2ecf20Sopenharmony_ci * We get here also when the interface changes network namespaces, 12908c2ecf20Sopenharmony_ci * as it's registered into the new one, but we don't want it to 12918c2ecf20Sopenharmony_ci * change ID in that case. Checking if the ID is already assigned 12928c2ecf20Sopenharmony_ci * works, because 0 isn't considered a valid ID and the memory is 12938c2ecf20Sopenharmony_ci * 0-initialized. 12948c2ecf20Sopenharmony_ci */ 12958c2ecf20Sopenharmony_ci if (!wdev->identifier) 12968c2ecf20Sopenharmony_ci wdev->identifier = ++rdev->wdev_id; 12978c2ecf20Sopenharmony_ci list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list); 12988c2ecf20Sopenharmony_ci rdev->devlist_generation++; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE); 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistatic int cfg80211_netdev_notifier_call(struct notifier_block *nb, 13048c2ecf20Sopenharmony_ci unsigned long state, void *ptr) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 13078c2ecf20Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 13088c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev; 13098c2ecf20Sopenharmony_ci struct cfg80211_sched_scan_request *pos, *tmp; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (!wdev) 13128c2ecf20Sopenharmony_ci return NOTIFY_DONE; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci rdev = wiphy_to_rdev(wdev->wiphy); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci switch (state) { 13198c2ecf20Sopenharmony_ci case NETDEV_POST_INIT: 13208c2ecf20Sopenharmony_ci SET_NETDEV_DEVTYPE(dev, &wiphy_type); 13218c2ecf20Sopenharmony_ci wdev->netdev = dev; 13228c2ecf20Sopenharmony_ci /* can only change netns with wiphy */ 13238c2ecf20Sopenharmony_ci dev->features |= NETIF_F_NETNS_LOCAL; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci cfg80211_init_wdev(wdev); 13268c2ecf20Sopenharmony_ci break; 13278c2ecf20Sopenharmony_ci case NETDEV_REGISTER: 13288c2ecf20Sopenharmony_ci /* 13298c2ecf20Sopenharmony_ci * NB: cannot take rdev->mtx here because this may be 13308c2ecf20Sopenharmony_ci * called within code protected by it when interfaces 13318c2ecf20Sopenharmony_ci * are added with nl80211. 13328c2ecf20Sopenharmony_ci */ 13338c2ecf20Sopenharmony_ci if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, 13348c2ecf20Sopenharmony_ci "phy80211")) { 13358c2ecf20Sopenharmony_ci pr_err("failed to add phy80211 symlink to netdev!\n"); 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci cfg80211_register_wdev(rdev, wdev); 13398c2ecf20Sopenharmony_ci break; 13408c2ecf20Sopenharmony_ci case NETDEV_GOING_DOWN: 13418c2ecf20Sopenharmony_ci cfg80211_leave(rdev, wdev); 13428c2ecf20Sopenharmony_ci break; 13438c2ecf20Sopenharmony_ci case NETDEV_DOWN: 13448c2ecf20Sopenharmony_ci cfg80211_update_iface_num(rdev, wdev->iftype, -1); 13458c2ecf20Sopenharmony_ci if (rdev->scan_req && rdev->scan_req->wdev == wdev) { 13468c2ecf20Sopenharmony_ci if (WARN_ON(!rdev->scan_req->notified && 13478c2ecf20Sopenharmony_ci (!rdev->int_scan_req || 13488c2ecf20Sopenharmony_ci !rdev->int_scan_req->notified))) 13498c2ecf20Sopenharmony_ci rdev->scan_req->info.aborted = true; 13508c2ecf20Sopenharmony_ci ___cfg80211_scan_done(rdev, false); 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci list_for_each_entry_safe(pos, tmp, 13548c2ecf20Sopenharmony_ci &rdev->sched_scan_req_list, list) { 13558c2ecf20Sopenharmony_ci if (WARN_ON(pos->dev == wdev->netdev)) 13568c2ecf20Sopenharmony_ci cfg80211_stop_sched_scan_req(rdev, pos, false); 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci rdev->opencount--; 13608c2ecf20Sopenharmony_ci wake_up(&rdev->dev_wait); 13618c2ecf20Sopenharmony_ci break; 13628c2ecf20Sopenharmony_ci case NETDEV_UP: 13638c2ecf20Sopenharmony_ci cfg80211_update_iface_num(rdev, wdev->iftype, 1); 13648c2ecf20Sopenharmony_ci wdev_lock(wdev); 13658c2ecf20Sopenharmony_ci switch (wdev->iftype) { 13668c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 13678c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 13688c2ecf20Sopenharmony_ci cfg80211_ibss_wext_join(rdev, wdev); 13698c2ecf20Sopenharmony_ci break; 13708c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 13718c2ecf20Sopenharmony_ci cfg80211_mgd_wext_connect(rdev, wdev); 13728c2ecf20Sopenharmony_ci break; 13738c2ecf20Sopenharmony_ci#endif 13748c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH 13758c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 13768c2ecf20Sopenharmony_ci { 13778c2ecf20Sopenharmony_ci /* backward compat code... */ 13788c2ecf20Sopenharmony_ci struct mesh_setup setup; 13798c2ecf20Sopenharmony_ci memcpy(&setup, &default_mesh_setup, 13808c2ecf20Sopenharmony_ci sizeof(setup)); 13818c2ecf20Sopenharmony_ci /* back compat only needed for mesh_id */ 13828c2ecf20Sopenharmony_ci setup.mesh_id = wdev->ssid; 13838c2ecf20Sopenharmony_ci setup.mesh_id_len = wdev->mesh_id_up_len; 13848c2ecf20Sopenharmony_ci if (wdev->mesh_id_up_len) 13858c2ecf20Sopenharmony_ci __cfg80211_join_mesh(rdev, dev, 13868c2ecf20Sopenharmony_ci &setup, 13878c2ecf20Sopenharmony_ci &default_mesh_config); 13888c2ecf20Sopenharmony_ci break; 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci#endif 13918c2ecf20Sopenharmony_ci default: 13928c2ecf20Sopenharmony_ci break; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci wdev_unlock(wdev); 13958c2ecf20Sopenharmony_ci rdev->opencount++; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci /* 13988c2ecf20Sopenharmony_ci * Configure power management to the driver here so that its 13998c2ecf20Sopenharmony_ci * correctly set also after interface type changes etc. 14008c2ecf20Sopenharmony_ci */ 14018c2ecf20Sopenharmony_ci if ((wdev->iftype == NL80211_IFTYPE_STATION || 14028c2ecf20Sopenharmony_ci wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && 14038c2ecf20Sopenharmony_ci rdev->ops->set_power_mgmt && 14048c2ecf20Sopenharmony_ci rdev_set_power_mgmt(rdev, dev, wdev->ps, 14058c2ecf20Sopenharmony_ci wdev->ps_timeout)) { 14068c2ecf20Sopenharmony_ci /* assume this means it's off */ 14078c2ecf20Sopenharmony_ci wdev->ps = false; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci break; 14108c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 14118c2ecf20Sopenharmony_ci /* 14128c2ecf20Sopenharmony_ci * It is possible to get NETDEV_UNREGISTER 14138c2ecf20Sopenharmony_ci * multiple times. To detect that, check 14148c2ecf20Sopenharmony_ci * that the interface is still on the list 14158c2ecf20Sopenharmony_ci * of registered interfaces, and only then 14168c2ecf20Sopenharmony_ci * remove and clean it up. 14178c2ecf20Sopenharmony_ci */ 14188c2ecf20Sopenharmony_ci if (!list_empty(&wdev->list)) { 14198c2ecf20Sopenharmony_ci __cfg80211_unregister_wdev(wdev, false); 14208c2ecf20Sopenharmony_ci sysfs_remove_link(&dev->dev.kobj, "phy80211"); 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci /* 14238c2ecf20Sopenharmony_ci * synchronise (so that we won't find this netdev 14248c2ecf20Sopenharmony_ci * from other code any more) and then clear the list 14258c2ecf20Sopenharmony_ci * head so that the above code can safely check for 14268c2ecf20Sopenharmony_ci * !list_empty() to avoid double-cleanup. 14278c2ecf20Sopenharmony_ci */ 14288c2ecf20Sopenharmony_ci synchronize_rcu(); 14298c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&wdev->list); 14308c2ecf20Sopenharmony_ci /* 14318c2ecf20Sopenharmony_ci * Ensure that all events have been processed and 14328c2ecf20Sopenharmony_ci * freed. 14338c2ecf20Sopenharmony_ci */ 14348c2ecf20Sopenharmony_ci cfg80211_process_wdev_events(wdev); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci if (WARN_ON(wdev->current_bss)) { 14378c2ecf20Sopenharmony_ci cfg80211_unhold_bss(wdev->current_bss); 14388c2ecf20Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); 14398c2ecf20Sopenharmony_ci wdev->current_bss = NULL; 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci break; 14428c2ecf20Sopenharmony_ci case NETDEV_PRE_UP: 14438c2ecf20Sopenharmony_ci if (!cfg80211_iftype_allowed(wdev->wiphy, wdev->iftype, 14448c2ecf20Sopenharmony_ci wdev->use_4addr, 0)) 14458c2ecf20Sopenharmony_ci return notifier_from_errno(-EOPNOTSUPP); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (rfkill_blocked(rdev->rfkill)) 14488c2ecf20Sopenharmony_ci return notifier_from_errno(-ERFKILL); 14498c2ecf20Sopenharmony_ci break; 14508c2ecf20Sopenharmony_ci default: 14518c2ecf20Sopenharmony_ci return NOTIFY_DONE; 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci wireless_nlevent_flush(); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci return NOTIFY_OK; 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistatic struct notifier_block cfg80211_netdev_notifier = { 14608c2ecf20Sopenharmony_ci .notifier_call = cfg80211_netdev_notifier_call, 14618c2ecf20Sopenharmony_ci}; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_cistatic void __net_exit cfg80211_pernet_exit(struct net *net) 14648c2ecf20Sopenharmony_ci{ 14658c2ecf20Sopenharmony_ci struct cfg80211_registered_device *rdev; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci rtnl_lock(); 14688c2ecf20Sopenharmony_ci list_for_each_entry(rdev, &cfg80211_rdev_list, list) { 14698c2ecf20Sopenharmony_ci if (net_eq(wiphy_net(&rdev->wiphy), net)) 14708c2ecf20Sopenharmony_ci WARN_ON(cfg80211_switch_netns(rdev, &init_net)); 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci rtnl_unlock(); 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_cistatic struct pernet_operations cfg80211_pernet_ops = { 14768c2ecf20Sopenharmony_ci .exit = cfg80211_pernet_exit, 14778c2ecf20Sopenharmony_ci}; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_cistatic int __init cfg80211_init(void) 14808c2ecf20Sopenharmony_ci{ 14818c2ecf20Sopenharmony_ci int err; 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci err = register_pernet_device(&cfg80211_pernet_ops); 14848c2ecf20Sopenharmony_ci if (err) 14858c2ecf20Sopenharmony_ci goto out_fail_pernet; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci err = wiphy_sysfs_init(); 14888c2ecf20Sopenharmony_ci if (err) 14898c2ecf20Sopenharmony_ci goto out_fail_sysfs; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci err = register_netdevice_notifier(&cfg80211_netdev_notifier); 14928c2ecf20Sopenharmony_ci if (err) 14938c2ecf20Sopenharmony_ci goto out_fail_notifier; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci err = nl80211_init(); 14968c2ecf20Sopenharmony_ci if (err) 14978c2ecf20Sopenharmony_ci goto out_fail_nl80211; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci err = regulatory_init(); 15028c2ecf20Sopenharmony_ci if (err) 15038c2ecf20Sopenharmony_ci goto out_fail_reg; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci cfg80211_wq = alloc_ordered_workqueue("cfg80211", WQ_MEM_RECLAIM); 15068c2ecf20Sopenharmony_ci if (!cfg80211_wq) { 15078c2ecf20Sopenharmony_ci err = -ENOMEM; 15088c2ecf20Sopenharmony_ci goto out_fail_wq; 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci return 0; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ciout_fail_wq: 15148c2ecf20Sopenharmony_ci regulatory_exit(); 15158c2ecf20Sopenharmony_ciout_fail_reg: 15168c2ecf20Sopenharmony_ci debugfs_remove(ieee80211_debugfs_dir); 15178c2ecf20Sopenharmony_ci nl80211_exit(); 15188c2ecf20Sopenharmony_ciout_fail_nl80211: 15198c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&cfg80211_netdev_notifier); 15208c2ecf20Sopenharmony_ciout_fail_notifier: 15218c2ecf20Sopenharmony_ci wiphy_sysfs_exit(); 15228c2ecf20Sopenharmony_ciout_fail_sysfs: 15238c2ecf20Sopenharmony_ci unregister_pernet_device(&cfg80211_pernet_ops); 15248c2ecf20Sopenharmony_ciout_fail_pernet: 15258c2ecf20Sopenharmony_ci return err; 15268c2ecf20Sopenharmony_ci} 15278c2ecf20Sopenharmony_cifs_initcall(cfg80211_init); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_cistatic void __exit cfg80211_exit(void) 15308c2ecf20Sopenharmony_ci{ 15318c2ecf20Sopenharmony_ci debugfs_remove(ieee80211_debugfs_dir); 15328c2ecf20Sopenharmony_ci nl80211_exit(); 15338c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&cfg80211_netdev_notifier); 15348c2ecf20Sopenharmony_ci wiphy_sysfs_exit(); 15358c2ecf20Sopenharmony_ci regulatory_exit(); 15368c2ecf20Sopenharmony_ci unregister_pernet_device(&cfg80211_pernet_ops); 15378c2ecf20Sopenharmony_ci destroy_workqueue(cfg80211_wq); 15388c2ecf20Sopenharmony_ci} 15398c2ecf20Sopenharmony_cimodule_exit(cfg80211_exit); 1540