| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * This is the linux wireless configuration interface. | 
|  | 3 | * | 
| Johannes Berg | 5f2aa25 | 2010-01-17 15:49:02 +0100 | [diff] [blame] | 4 | * Copyright 2006-2010		Johannes Berg <johannes@sipsolutions.net> | 
| Johannes Berg | 2740f0c | 2014-09-03 15:24:58 +0300 | [diff] [blame] | 5 | * Copyright 2013-2014  Intel Mobile Communications GmbH | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 6 | */ | 
|  | 7 |  | 
| Joe Perches | e9c0268 | 2010-11-16 19:56:49 -0800 | [diff] [blame] | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|  | 9 |  | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 10 | #include <linux/if.h> | 
|  | 11 | #include <linux/module.h> | 
|  | 12 | #include <linux/err.h> | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 13 | #include <linux/list.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 14 | #include <linux/slab.h> | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 15 | #include <linux/nl80211.h> | 
|  | 16 | #include <linux/debugfs.h> | 
|  | 17 | #include <linux/notifier.h> | 
|  | 18 | #include <linux/device.h> | 
| Zhu Yi | 16a832e | 2009-08-19 16:08:22 +0800 | [diff] [blame] | 19 | #include <linux/etherdevice.h> | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 20 | #include <linux/rtnetlink.h> | 
| Alexey Dobriyan | d43c36d | 2009-10-07 17:09:06 +0400 | [diff] [blame] | 21 | #include <linux/sched.h> | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 22 | #include <net/genetlink.h> | 
|  | 23 | #include <net/cfg80211.h> | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 24 | #include "nl80211.h" | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 25 | #include "core.h" | 
|  | 26 | #include "sysfs.h" | 
| Luis R. Rodriguez | 1ac6130 | 2009-05-02 00:37:21 -0400 | [diff] [blame] | 27 | #include "debugfs.h" | 
| Johannes Berg | a9a1162 | 2009-07-27 12:01:53 +0200 | [diff] [blame] | 28 | #include "wext-compat.h" | 
| Hila Gonen | e35e4d2 | 2012-06-27 17:19:42 +0300 | [diff] [blame] | 29 | #include "rdev-ops.h" | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 30 |  | 
|  | 31 | /* name for sysfs, %d is appended */ | 
|  | 32 | #define PHY_NAME "phy" | 
|  | 33 |  | 
|  | 34 | MODULE_AUTHOR("Johannes Berg"); | 
|  | 35 | MODULE_LICENSE("GPL"); | 
|  | 36 | MODULE_DESCRIPTION("wireless configuration support"); | 
| Marcel Holtmann | fb4e156 | 2013-04-28 16:22:06 -0700 | [diff] [blame] | 37 | MODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 38 |  | 
| Johannes Berg | 5fe231e | 2013-05-08 21:45:15 +0200 | [diff] [blame] | 39 | /* RCU-protected (and RTNL for writers) */ | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 40 | LIST_HEAD(cfg80211_rdev_list); | 
| Johannes Berg | f5ea912 | 2009-08-07 16:17:38 +0200 | [diff] [blame] | 41 | int cfg80211_rdev_list_generation; | 
| Luis R. Rodriguez | a179439 | 2009-02-21 00:04:21 -0500 | [diff] [blame] | 42 |  | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 43 | /* for debugfs */ | 
|  | 44 | static struct dentry *ieee80211_debugfs_dir; | 
|  | 45 |  | 
| Alban Browaeys | e60d744 | 2009-11-25 15:13:00 +0100 | [diff] [blame] | 46 | /* for the cleanup, scan and event works */ | 
|  | 47 | struct workqueue_struct *cfg80211_wq; | 
|  | 48 |  | 
| Amitkumar Karwar | 40db6c7 | 2011-04-21 14:10:27 -0700 | [diff] [blame] | 49 | static bool cfg80211_disable_40mhz_24ghz; | 
|  | 50 | module_param(cfg80211_disable_40mhz_24ghz, bool, 0644); | 
|  | 51 | MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, | 
|  | 52 | "Disable 40MHz support in the 2.4GHz band"); | 
|  | 53 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 54 | struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 55 | { | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 56 | struct cfg80211_registered_device *result = NULL, *rdev; | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 57 |  | 
| Johannes Berg | 5fe231e | 2013-05-08 21:45:15 +0200 | [diff] [blame] | 58 | ASSERT_RTNL(); | 
| Luis R. Rodriguez | 761cf7e | 2009-02-21 00:04:25 -0500 | [diff] [blame] | 59 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 60 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 
|  | 61 | if (rdev->wiphy_idx == wiphy_idx) { | 
|  | 62 | result = rdev; | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 63 | break; | 
|  | 64 | } | 
|  | 65 | } | 
|  | 66 |  | 
|  | 67 | return result; | 
|  | 68 | } | 
|  | 69 |  | 
| Luis R. Rodriguez | 806a9e3 | 2009-02-21 00:04:26 -0500 | [diff] [blame] | 70 | int get_wiphy_idx(struct wiphy *wiphy) | 
|  | 71 | { | 
| Zhao, Gang | f26cbf4 | 2014-04-21 12:53:03 +0800 | [diff] [blame] | 72 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
| Johannes Berg | f417376 | 2012-12-03 18:23:37 +0100 | [diff] [blame] | 73 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 74 | return rdev->wiphy_idx; | 
| Luis R. Rodriguez | 806a9e3 | 2009-02-21 00:04:26 -0500 | [diff] [blame] | 75 | } | 
|  | 76 |  | 
| Luis R. Rodriguez | 806a9e3 | 2009-02-21 00:04:26 -0500 | [diff] [blame] | 77 | struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) | 
|  | 78 | { | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 79 | struct cfg80211_registered_device *rdev; | 
| Luis R. Rodriguez | 806a9e3 | 2009-02-21 00:04:26 -0500 | [diff] [blame] | 80 |  | 
| Johannes Berg | 5fe231e | 2013-05-08 21:45:15 +0200 | [diff] [blame] | 81 | ASSERT_RTNL(); | 
| Luis R. Rodriguez | 806a9e3 | 2009-02-21 00:04:26 -0500 | [diff] [blame] | 82 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 83 | rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); | 
|  | 84 | if (!rdev) | 
| Luis R. Rodriguez | 806a9e3 | 2009-02-21 00:04:26 -0500 | [diff] [blame] | 85 | return NULL; | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 86 | return &rdev->wiphy; | 
| Luis R. Rodriguez | 806a9e3 | 2009-02-21 00:04:26 -0500 | [diff] [blame] | 87 | } | 
|  | 88 |  | 
| Ben Greear | 1998d90 | 2014-10-22 12:23:00 -0700 | [diff] [blame] | 89 | static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev, | 
|  | 90 | const char *newname) | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 91 | { | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 92 | struct cfg80211_registered_device *rdev2; | 
| Ben Greear | 1998d90 | 2014-10-22 12:23:00 -0700 | [diff] [blame] | 93 | int wiphy_idx, taken = -1, digits; | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 94 |  | 
| Johannes Berg | 5fe231e | 2013-05-08 21:45:15 +0200 | [diff] [blame] | 95 | ASSERT_RTNL(); | 
| Eric W. Biederman | 2940bb6 | 2008-05-08 14:30:18 -0700 | [diff] [blame] | 96 |  | 
| Johannes Berg | 7623225 | 2010-10-11 14:46:52 -0400 | [diff] [blame] | 97 | /* prohibit calling the thing phy%d when %d is not its number */ | 
|  | 98 | sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); | 
|  | 99 | if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) { | 
|  | 100 | /* count number of places needed to print wiphy_idx */ | 
|  | 101 | digits = 1; | 
|  | 102 | while (wiphy_idx /= 10) | 
|  | 103 | digits++; | 
|  | 104 | /* | 
|  | 105 | * deny the name if it is phy<idx> where <idx> is printed | 
|  | 106 | * without leading zeroes. taken == strlen(newname) here | 
|  | 107 | */ | 
|  | 108 | if (taken == strlen(PHY_NAME) + digits) | 
|  | 109 | return -EINVAL; | 
|  | 110 | } | 
|  | 111 |  | 
| Eric W. Biederman | 2940bb6 | 2008-05-08 14:30:18 -0700 | [diff] [blame] | 112 | /* Ensure another device does not already have this name. */ | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 113 | list_for_each_entry(rdev2, &cfg80211_rdev_list, list) | 
| Ben Greear | 1998d90 | 2014-10-22 12:23:00 -0700 | [diff] [blame] | 114 | if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0) | 
| Johannes Berg | 7623225 | 2010-10-11 14:46:52 -0400 | [diff] [blame] | 115 | return -EINVAL; | 
| Eric W. Biederman | 2940bb6 | 2008-05-08 14:30:18 -0700 | [diff] [blame] | 116 |  | 
| Ben Greear | 1998d90 | 2014-10-22 12:23:00 -0700 | [diff] [blame] | 117 | return 0; | 
|  | 118 | } | 
|  | 119 |  | 
|  | 120 | int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, | 
|  | 121 | char *newname) | 
|  | 122 | { | 
|  | 123 | int result; | 
|  | 124 |  | 
|  | 125 | ASSERT_RTNL(); | 
|  | 126 |  | 
|  | 127 | /* Ignore nop renames */ | 
|  | 128 | if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0) | 
|  | 129 | return 0; | 
|  | 130 |  | 
|  | 131 | result = cfg80211_dev_check_name(rdev, newname); | 
|  | 132 | if (result < 0) | 
|  | 133 | return result; | 
|  | 134 |  | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 135 | result = device_rename(&rdev->wiphy.dev, newname); | 
|  | 136 | if (result) | 
| Johannes Berg | 4bbf4d5 | 2009-03-24 09:35:46 +0100 | [diff] [blame] | 137 | return result; | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 138 |  | 
| Johannes Berg | 33c0360 | 2008-10-08 10:23:48 +0200 | [diff] [blame] | 139 | if (rdev->wiphy.debugfsdir && | 
|  | 140 | !debugfs_rename(rdev->wiphy.debugfsdir->d_parent, | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 141 | rdev->wiphy.debugfsdir, | 
|  | 142 | rdev->wiphy.debugfsdir->d_parent, | 
|  | 143 | newname)) | 
| Joe Perches | e9c0268 | 2010-11-16 19:56:49 -0800 | [diff] [blame] | 144 | pr_err("failed to rename debugfs dir to %s!\n", newname); | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 145 |  | 
| Johannes Berg | 3bb2055 | 2014-05-26 13:52:25 +0200 | [diff] [blame] | 146 | nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 147 |  | 
| Johannes Berg | 4bbf4d5 | 2009-03-24 09:35:46 +0100 | [diff] [blame] | 148 | return 0; | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 149 | } | 
|  | 150 |  | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 151 | int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, | 
|  | 152 | struct net *net) | 
|  | 153 | { | 
|  | 154 | struct wireless_dev *wdev; | 
|  | 155 | int err = 0; | 
|  | 156 |  | 
| Johannes Berg | 5be83de | 2009-11-19 00:56:28 +0100 | [diff] [blame] | 157 | if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK)) | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 158 | return -EOPNOTSUPP; | 
|  | 159 |  | 
| Johannes Berg | 89a54e4 | 2012-06-15 14:33:17 +0200 | [diff] [blame] | 160 | list_for_each_entry(wdev, &rdev->wdev_list, list) { | 
| Johannes Berg | ba22fb5 | 2012-06-15 18:00:00 +0200 | [diff] [blame] | 161 | if (!wdev->netdev) | 
|  | 162 | continue; | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 163 | wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; | 
|  | 164 | err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); | 
|  | 165 | if (err) | 
|  | 166 | break; | 
|  | 167 | wdev->netdev->features |= NETIF_F_NETNS_LOCAL; | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 | if (err) { | 
|  | 171 | /* failed -- clean up to old netns */ | 
|  | 172 | net = wiphy_net(&rdev->wiphy); | 
|  | 173 |  | 
| Johannes Berg | 89a54e4 | 2012-06-15 14:33:17 +0200 | [diff] [blame] | 174 | list_for_each_entry_continue_reverse(wdev, &rdev->wdev_list, | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 175 | list) { | 
| Johannes Berg | ba22fb5 | 2012-06-15 18:00:00 +0200 | [diff] [blame] | 176 | if (!wdev->netdev) | 
|  | 177 | continue; | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 178 | wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; | 
|  | 179 | err = dev_change_net_namespace(wdev->netdev, net, | 
|  | 180 | "wlan%d"); | 
|  | 181 | WARN_ON(err); | 
|  | 182 | wdev->netdev->features |= NETIF_F_NETNS_LOCAL; | 
|  | 183 | } | 
| Johannes Berg | 0460079 | 2010-08-05 17:45:15 +0200 | [diff] [blame] | 184 |  | 
|  | 185 | return err; | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 186 | } | 
|  | 187 |  | 
|  | 188 | wiphy_net_set(&rdev->wiphy, net); | 
|  | 189 |  | 
| Johannes Berg | 0460079 | 2010-08-05 17:45:15 +0200 | [diff] [blame] | 190 | err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev)); | 
|  | 191 | WARN_ON(err); | 
|  | 192 |  | 
|  | 193 | return 0; | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 194 | } | 
|  | 195 |  | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 196 | static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) | 
|  | 197 | { | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 198 | struct cfg80211_registered_device *rdev = data; | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 199 |  | 
| Hila Gonen | e35e4d2 | 2012-06-27 17:19:42 +0300 | [diff] [blame] | 200 | rdev_rfkill_poll(rdev); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 201 | } | 
|  | 202 |  | 
| Johannes Berg | f9f4752 | 2013-03-19 15:04:07 +0100 | [diff] [blame] | 203 | void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, | 
|  | 204 | struct wireless_dev *wdev) | 
|  | 205 | { | 
| Johannes Berg | 5fe231e | 2013-05-08 21:45:15 +0200 | [diff] [blame] | 206 | ASSERT_RTNL(); | 
| Johannes Berg | f9f4752 | 2013-03-19 15:04:07 +0100 | [diff] [blame] | 207 |  | 
|  | 208 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)) | 
|  | 209 | return; | 
|  | 210 |  | 
|  | 211 | if (!wdev->p2p_started) | 
|  | 212 | return; | 
|  | 213 |  | 
|  | 214 | rdev_stop_p2p_device(rdev, wdev); | 
|  | 215 | wdev->p2p_started = false; | 
|  | 216 |  | 
|  | 217 | rdev->opencount--; | 
|  | 218 |  | 
| Johannes Berg | a617302 | 2014-01-22 11:14:18 +0200 | [diff] [blame] | 219 | if (rdev->scan_req && rdev->scan_req->wdev == wdev) { | 
|  | 220 | if (WARN_ON(!rdev->scan_req->notified)) | 
|  | 221 | rdev->scan_req->aborted = true; | 
| Johannes Berg | f9d15d1 | 2014-01-22 11:14:19 +0200 | [diff] [blame] | 222 | ___cfg80211_scan_done(rdev, false); | 
| Johannes Berg | a617302 | 2014-01-22 11:14:18 +0200 | [diff] [blame] | 223 | } | 
| Johannes Berg | f9f4752 | 2013-03-19 15:04:07 +0100 | [diff] [blame] | 224 | } | 
|  | 225 |  | 
| Johannes Berg | f6837ba | 2014-04-30 14:19:04 +0200 | [diff] [blame] | 226 | void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 227 | { | 
| Johannes Berg | f6837ba | 2014-04-30 14:19:04 +0200 | [diff] [blame] | 228 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 229 | struct wireless_dev *wdev; | 
|  | 230 |  | 
| Johannes Berg | f6837ba | 2014-04-30 14:19:04 +0200 | [diff] [blame] | 231 | ASSERT_RTNL(); | 
| Johannes Berg | f9f4752 | 2013-03-19 15:04:07 +0100 | [diff] [blame] | 232 |  | 
| Johannes Berg | 98104fde | 2012-06-16 00:19:54 +0200 | [diff] [blame] | 233 | list_for_each_entry(wdev, &rdev->wdev_list, list) { | 
|  | 234 | if (wdev->netdev) { | 
| Johannes Berg | ba22fb5 | 2012-06-15 18:00:00 +0200 | [diff] [blame] | 235 | dev_close(wdev->netdev); | 
| Johannes Berg | 98104fde | 2012-06-16 00:19:54 +0200 | [diff] [blame] | 236 | continue; | 
|  | 237 | } | 
|  | 238 | /* otherwise, check iftype */ | 
|  | 239 | switch (wdev->iftype) { | 
|  | 240 | case NL80211_IFTYPE_P2P_DEVICE: | 
| Johannes Berg | f9f4752 | 2013-03-19 15:04:07 +0100 | [diff] [blame] | 241 | cfg80211_stop_p2p_device(rdev, wdev); | 
| Johannes Berg | 98104fde | 2012-06-16 00:19:54 +0200 | [diff] [blame] | 242 | break; | 
|  | 243 | default: | 
|  | 244 | break; | 
|  | 245 | } | 
|  | 246 | } | 
| Johannes Berg | f6837ba | 2014-04-30 14:19:04 +0200 | [diff] [blame] | 247 | } | 
|  | 248 | EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 249 |  | 
| Johannes Berg | f6837ba | 2014-04-30 14:19:04 +0200 | [diff] [blame] | 250 | static int cfg80211_rfkill_set_block(void *data, bool blocked) | 
|  | 251 | { | 
|  | 252 | struct cfg80211_registered_device *rdev = data; | 
|  | 253 |  | 
|  | 254 | if (!blocked) | 
|  | 255 | return 0; | 
|  | 256 |  | 
|  | 257 | rtnl_lock(); | 
|  | 258 | cfg80211_shutdown_all_interfaces(&rdev->wiphy); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 259 | rtnl_unlock(); | 
|  | 260 |  | 
|  | 261 | return 0; | 
|  | 262 | } | 
|  | 263 |  | 
|  | 264 | static void cfg80211_rfkill_sync_work(struct work_struct *work) | 
|  | 265 | { | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 266 | struct cfg80211_registered_device *rdev; | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 267 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 268 | rdev = container_of(work, struct cfg80211_registered_device, rfkill_sync); | 
|  | 269 | cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill)); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 270 | } | 
|  | 271 |  | 
| Johannes Berg | 667503dd | 2009-07-07 03:56:11 +0200 | [diff] [blame] | 272 | static void cfg80211_event_work(struct work_struct *work) | 
|  | 273 | { | 
|  | 274 | struct cfg80211_registered_device *rdev; | 
| Johannes Berg | 667503dd | 2009-07-07 03:56:11 +0200 | [diff] [blame] | 275 |  | 
|  | 276 | rdev = container_of(work, struct cfg80211_registered_device, | 
|  | 277 | event_work); | 
|  | 278 |  | 
|  | 279 | rtnl_lock(); | 
| Johannes Berg | 3d54d25 | 2009-08-21 14:51:05 +0200 | [diff] [blame] | 280 | cfg80211_process_rdev_events(rdev); | 
| Johannes Berg | 667503dd | 2009-07-07 03:56:11 +0200 | [diff] [blame] | 281 | rtnl_unlock(); | 
|  | 282 | } | 
|  | 283 |  | 
| Johannes Berg | 78f22b6 | 2014-03-24 17:57:27 +0100 | [diff] [blame] | 284 | void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) | 
|  | 285 | { | 
|  | 286 | struct cfg80211_iface_destroy *item; | 
|  | 287 |  | 
|  | 288 | ASSERT_RTNL(); | 
|  | 289 |  | 
|  | 290 | spin_lock_irq(&rdev->destroy_list_lock); | 
|  | 291 | while ((item = list_first_entry_or_null(&rdev->destroy_list, | 
|  | 292 | struct cfg80211_iface_destroy, | 
|  | 293 | list))) { | 
|  | 294 | struct wireless_dev *wdev, *tmp; | 
|  | 295 | u32 nlportid = item->nlportid; | 
|  | 296 |  | 
|  | 297 | list_del(&item->list); | 
|  | 298 | kfree(item); | 
|  | 299 | spin_unlock_irq(&rdev->destroy_list_lock); | 
|  | 300 |  | 
|  | 301 | list_for_each_entry_safe(wdev, tmp, &rdev->wdev_list, list) { | 
|  | 302 | if (nlportid == wdev->owner_nlportid) | 
|  | 303 | rdev_del_virtual_intf(rdev, wdev); | 
|  | 304 | } | 
|  | 305 |  | 
|  | 306 | spin_lock_irq(&rdev->destroy_list_lock); | 
|  | 307 | } | 
|  | 308 | spin_unlock_irq(&rdev->destroy_list_lock); | 
|  | 309 | } | 
|  | 310 |  | 
|  | 311 | static void cfg80211_destroy_iface_wk(struct work_struct *work) | 
|  | 312 | { | 
|  | 313 | struct cfg80211_registered_device *rdev; | 
|  | 314 |  | 
|  | 315 | rdev = container_of(work, struct cfg80211_registered_device, | 
|  | 316 | destroy_work); | 
|  | 317 |  | 
|  | 318 | rtnl_lock(); | 
|  | 319 | cfg80211_destroy_ifaces(rdev); | 
|  | 320 | rtnl_unlock(); | 
|  | 321 | } | 
|  | 322 |  | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 323 | /* exported functions */ | 
|  | 324 |  | 
| Ben Greear | 1998d90 | 2014-10-22 12:23:00 -0700 | [diff] [blame] | 325 | struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, | 
|  | 326 | const char *requested_name) | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 327 | { | 
| Johannes Berg | 73810b7 | 2013-05-08 21:49:02 +0200 | [diff] [blame] | 328 | static atomic_t wiphy_counter = ATOMIC_INIT(0); | 
| Johannes Berg | 7623225 | 2010-10-11 14:46:52 -0400 | [diff] [blame] | 329 |  | 
|  | 330 | struct cfg80211_registered_device *rdev; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 331 | int alloc_size; | 
|  | 332 |  | 
| Johannes Berg | 0b20633d | 2009-07-07 03:56:13 +0200 | [diff] [blame] | 333 | WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); | 
|  | 334 | WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); | 
|  | 335 | WARN_ON(ops->connect && !ops->disconnect); | 
|  | 336 | WARN_ON(ops->join_ibss && !ops->leave_ibss); | 
|  | 337 | WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf); | 
|  | 338 | WARN_ON(ops->add_station && !ops->del_station); | 
|  | 339 | WARN_ON(ops->add_mpath && !ops->del_mpath); | 
| Johannes Berg | 29cbe68 | 2010-12-03 09:20:44 +0100 | [diff] [blame] | 340 | WARN_ON(ops->join_mesh && !ops->leave_mesh); | 
| Johannes Berg | 41ade00 | 2007-12-19 02:03:29 +0100 | [diff] [blame] | 341 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 342 | alloc_size = sizeof(*rdev) + sizeof_priv; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 343 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 344 | rdev = kzalloc(alloc_size, GFP_KERNEL); | 
|  | 345 | if (!rdev) | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 346 | return NULL; | 
|  | 347 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 348 | rdev->ops = ops; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 349 |  | 
| Johannes Berg | 73810b7 | 2013-05-08 21:49:02 +0200 | [diff] [blame] | 350 | rdev->wiphy_idx = atomic_inc_return(&wiphy_counter); | 
| Johannes Berg | a4d73ee | 2007-04-26 20:50:35 -0700 | [diff] [blame] | 351 |  | 
| Johannes Berg | f417376 | 2012-12-03 18:23:37 +0100 | [diff] [blame] | 352 | if (unlikely(rdev->wiphy_idx < 0)) { | 
| Johannes Berg | 7623225 | 2010-10-11 14:46:52 -0400 | [diff] [blame] | 353 | /* ugh, wrapped! */ | 
| Johannes Berg | 73810b7 | 2013-05-08 21:49:02 +0200 | [diff] [blame] | 354 | atomic_dec(&wiphy_counter); | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 355 | kfree(rdev); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 356 | return NULL; | 
|  | 357 | } | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 358 |  | 
| Johannes Berg | 9b88196 | 2013-06-04 22:23:36 +0200 | [diff] [blame] | 359 | /* atomic_inc_return makes it start at 1, make it start at 0 */ | 
|  | 360 | rdev->wiphy_idx--; | 
|  | 361 |  | 
| Johannes Berg | 7623225 | 2010-10-11 14:46:52 -0400 | [diff] [blame] | 362 | /* give it a proper name */ | 
| Ben Greear | 1998d90 | 2014-10-22 12:23:00 -0700 | [diff] [blame] | 363 | if (requested_name && requested_name[0]) { | 
|  | 364 | int rv; | 
|  | 365 |  | 
|  | 366 | rtnl_lock(); | 
|  | 367 | rv = cfg80211_dev_check_name(rdev, requested_name); | 
|  | 368 |  | 
|  | 369 | if (rv < 0) { | 
|  | 370 | rtnl_unlock(); | 
|  | 371 | goto use_default_name; | 
|  | 372 | } | 
|  | 373 |  | 
|  | 374 | rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name); | 
|  | 375 | rtnl_unlock(); | 
|  | 376 | if (rv) | 
|  | 377 | goto use_default_name; | 
|  | 378 | } else { | 
|  | 379 | use_default_name: | 
|  | 380 | /* NOTE:  This is *probably* safe w/out holding rtnl because of | 
|  | 381 | * the restrictions on phy names.  Probably this call could | 
|  | 382 | * fail if some other part of the kernel (re)named a device | 
|  | 383 | * phyX.  But, might should add some locking and check return | 
|  | 384 | * value, and use a different name if this one exists? | 
|  | 385 | */ | 
|  | 386 | dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); | 
|  | 387 | } | 
| Johannes Berg | 7623225 | 2010-10-11 14:46:52 -0400 | [diff] [blame] | 388 |  | 
| Johannes Berg | 89a54e4 | 2012-06-15 14:33:17 +0200 | [diff] [blame] | 389 | INIT_LIST_HEAD(&rdev->wdev_list); | 
| Ben Greear | 37c73b5 | 2012-10-26 14:49:25 -0700 | [diff] [blame] | 390 | INIT_LIST_HEAD(&rdev->beacon_registrations); | 
|  | 391 | spin_lock_init(&rdev->beacon_registrations_lock); | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 392 | spin_lock_init(&rdev->bss_lock); | 
|  | 393 | INIT_LIST_HEAD(&rdev->bss_list); | 
|  | 394 | INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); | 
| Luciano Coelho | 807f8a8 | 2011-05-11 17:09:35 +0300 | [diff] [blame] | 395 | INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); | 
| Simon Wunderlich | 04f3904 | 2013-02-08 18:16:19 +0100 | [diff] [blame] | 396 | INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk, | 
|  | 397 | cfg80211_dfs_channels_update_work); | 
| Johannes Berg | 3d23e34 | 2009-09-29 23:27:28 +0200 | [diff] [blame] | 398 | #ifdef CONFIG_CFG80211_WEXT | 
|  | 399 | rdev->wiphy.wext = &cfg80211_wext_handler; | 
|  | 400 | #endif | 
|  | 401 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 402 | device_initialize(&rdev->wiphy.dev); | 
|  | 403 | rdev->wiphy.dev.class = &ieee80211_class; | 
|  | 404 | rdev->wiphy.dev.platform_data = rdev; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 405 |  | 
| Johannes Berg | 78f22b6 | 2014-03-24 17:57:27 +0100 | [diff] [blame] | 406 | INIT_LIST_HEAD(&rdev->destroy_list); | 
|  | 407 | spin_lock_init(&rdev->destroy_list_lock); | 
|  | 408 | INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); | 
|  | 409 |  | 
| Johannes Berg | 5be83de | 2009-11-19 00:56:28 +0100 | [diff] [blame] | 410 | #ifdef CONFIG_CFG80211_DEFAULT_PS | 
|  | 411 | rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; | 
|  | 412 | #endif | 
| Johannes Berg | 16cb9d4 | 2009-08-12 23:33:20 +0200 | [diff] [blame] | 413 |  | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 414 | wiphy_net_set(&rdev->wiphy, &init_net); | 
|  | 415 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 416 | rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; | 
|  | 417 | rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), | 
|  | 418 | &rdev->wiphy.dev, RFKILL_TYPE_WLAN, | 
|  | 419 | &rdev->rfkill_ops, rdev); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 420 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 421 | if (!rdev->rfkill) { | 
|  | 422 | kfree(rdev); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 423 | return NULL; | 
|  | 424 | } | 
|  | 425 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 426 | INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work); | 
|  | 427 | INIT_WORK(&rdev->conn_work, cfg80211_conn_work); | 
|  | 428 | INIT_WORK(&rdev->event_work, cfg80211_event_work); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 429 |  | 
| Johannes Berg | ad00239 | 2009-08-18 19:51:57 +0200 | [diff] [blame] | 430 | init_waitqueue_head(&rdev->dev_wait); | 
|  | 431 |  | 
| Jouni Malinen | b9a5f8ca | 2009-04-20 18:39:05 +0200 | [diff] [blame] | 432 | /* | 
|  | 433 | * Initialize wiphy parameters to IEEE 802.11 MIB default values. | 
|  | 434 | * Fragmentation and RTS threshold are disabled by default with the | 
|  | 435 | * special -1 value. | 
|  | 436 | */ | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 437 | rdev->wiphy.retry_short = 7; | 
|  | 438 | rdev->wiphy.retry_long = 4; | 
|  | 439 | rdev->wiphy.frag_threshold = (u32) -1; | 
|  | 440 | rdev->wiphy.rts_threshold = (u32) -1; | 
| Lukáš Turek | 81077e8 | 2009-12-21 22:50:47 +0100 | [diff] [blame] | 441 | rdev->wiphy.coverage_class = 0; | 
| Jouni Malinen | b9a5f8ca | 2009-04-20 18:39:05 +0200 | [diff] [blame] | 442 |  | 
| Andrei Otcheretianski | 9a774c7 | 2014-05-09 14:11:46 +0300 | [diff] [blame] | 443 | rdev->wiphy.max_num_csa_counters = 1; | 
|  | 444 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 445 | return &rdev->wiphy; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 446 | } | 
| Ben Greear | 1998d90 | 2014-10-22 12:23:00 -0700 | [diff] [blame] | 447 | EXPORT_SYMBOL(wiphy_new_nm); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 448 |  | 
| Johannes Berg | 7527a78 | 2011-05-13 10:58:57 +0200 | [diff] [blame] | 449 | static int wiphy_verify_combinations(struct wiphy *wiphy) | 
|  | 450 | { | 
|  | 451 | const struct ieee80211_iface_combination *c; | 
|  | 452 | int i, j; | 
|  | 453 |  | 
| Johannes Berg | 7527a78 | 2011-05-13 10:58:57 +0200 | [diff] [blame] | 454 | for (i = 0; i < wiphy->n_iface_combinations; i++) { | 
|  | 455 | u32 cnt = 0; | 
|  | 456 | u16 all_iftypes = 0; | 
|  | 457 |  | 
|  | 458 | c = &wiphy->iface_combinations[i]; | 
|  | 459 |  | 
| Simon Wunderlich | 11c4a07 | 2013-01-08 14:04:07 +0100 | [diff] [blame] | 460 | /* | 
|  | 461 | * Combinations with just one interface aren't real, | 
|  | 462 | * however we make an exception for DFS. | 
|  | 463 | */ | 
|  | 464 | if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths)) | 
| Johannes Berg | 7527a78 | 2011-05-13 10:58:57 +0200 | [diff] [blame] | 465 | return -EINVAL; | 
|  | 466 |  | 
|  | 467 | /* Need at least one channel */ | 
|  | 468 | if (WARN_ON(!c->num_different_channels)) | 
|  | 469 | return -EINVAL; | 
|  | 470 |  | 
| Michal Kazior | d4e50c5 | 2012-06-29 12:47:07 +0200 | [diff] [blame] | 471 | /* | 
|  | 472 | * Put a sane limit on maximum number of different | 
|  | 473 | * channels to simplify channel accounting code. | 
|  | 474 | */ | 
|  | 475 | if (WARN_ON(c->num_different_channels > | 
|  | 476 | CFG80211_MAX_NUM_DIFFERENT_CHANNELS)) | 
|  | 477 | return -EINVAL; | 
|  | 478 |  | 
| Simon Wunderlich | 11c4a07 | 2013-01-08 14:04:07 +0100 | [diff] [blame] | 479 | /* DFS only works on one channel. */ | 
|  | 480 | if (WARN_ON(c->radar_detect_widths && | 
|  | 481 | (c->num_different_channels > 1))) | 
|  | 482 | return -EINVAL; | 
|  | 483 |  | 
| Johannes Berg | 7527a78 | 2011-05-13 10:58:57 +0200 | [diff] [blame] | 484 | if (WARN_ON(!c->n_limits)) | 
|  | 485 | return -EINVAL; | 
|  | 486 |  | 
|  | 487 | for (j = 0; j < c->n_limits; j++) { | 
|  | 488 | u16 types = c->limits[j].types; | 
|  | 489 |  | 
| Luciano Coelho | b6a5501 | 2014-02-27 11:07:21 +0200 | [diff] [blame] | 490 | /* interface types shouldn't overlap */ | 
| Johannes Berg | 7527a78 | 2011-05-13 10:58:57 +0200 | [diff] [blame] | 491 | if (WARN_ON(types & all_iftypes)) | 
|  | 492 | return -EINVAL; | 
|  | 493 | all_iftypes |= types; | 
|  | 494 |  | 
|  | 495 | if (WARN_ON(!c->limits[j].max)) | 
|  | 496 | return -EINVAL; | 
|  | 497 |  | 
|  | 498 | /* Shouldn't list software iftypes in combinations! */ | 
|  | 499 | if (WARN_ON(wiphy->software_iftypes & types)) | 
|  | 500 | return -EINVAL; | 
|  | 501 |  | 
| Johannes Berg | 98104fde | 2012-06-16 00:19:54 +0200 | [diff] [blame] | 502 | /* Only a single P2P_DEVICE can be allowed */ | 
|  | 503 | if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) && | 
|  | 504 | c->limits[j].max > 1)) | 
|  | 505 | return -EINVAL; | 
|  | 506 |  | 
| Johannes Berg | 7527a78 | 2011-05-13 10:58:57 +0200 | [diff] [blame] | 507 | cnt += c->limits[j].max; | 
|  | 508 | /* | 
|  | 509 | * Don't advertise an unsupported type | 
|  | 510 | * in a combination. | 
|  | 511 | */ | 
|  | 512 | if (WARN_ON((wiphy->interface_modes & types) != types)) | 
|  | 513 | return -EINVAL; | 
|  | 514 | } | 
|  | 515 |  | 
|  | 516 | /* You can't even choose that many! */ | 
|  | 517 | if (WARN_ON(cnt < c->max_interfaces)) | 
|  | 518 | return -EINVAL; | 
|  | 519 | } | 
|  | 520 |  | 
|  | 521 | return 0; | 
|  | 522 | } | 
|  | 523 |  | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 524 | int wiphy_register(struct wiphy *wiphy) | 
|  | 525 | { | 
| Zhao, Gang | f26cbf4 | 2014-04-21 12:53:03 +0800 | [diff] [blame] | 526 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 527 | int res; | 
| Johannes Berg | 8318d78 | 2008-01-24 19:38:38 +0100 | [diff] [blame] | 528 | enum ieee80211_band band; | 
|  | 529 | struct ieee80211_supported_band *sband; | 
|  | 530 | bool have_band = false; | 
|  | 531 | int i; | 
| Luis R. Rodriguez | f59ac04 | 2008-08-29 16:26:43 -0700 | [diff] [blame] | 532 | u16 ifmodes = wiphy->interface_modes; | 
|  | 533 |  | 
| Johannes Berg | dfb89c5 | 2012-06-27 09:23:48 +0200 | [diff] [blame] | 534 | #ifdef CONFIG_PM | 
| Johannes Berg | 964dc9e | 2013-06-03 17:25:34 +0200 | [diff] [blame] | 535 | if (WARN_ON(wiphy->wowlan && | 
|  | 536 | (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && | 
|  | 537 | !(wiphy->wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) | 
|  | 538 | return -EINVAL; | 
|  | 539 | if (WARN_ON(wiphy->wowlan && | 
|  | 540 | !wiphy->wowlan->flags && !wiphy->wowlan->n_patterns && | 
|  | 541 | !wiphy->wowlan->tcp)) | 
| Johannes Berg | 77dbbb1 | 2011-07-13 10:48:55 +0200 | [diff] [blame] | 542 | return -EINVAL; | 
| Johannes Berg | dfb89c5 | 2012-06-27 09:23:48 +0200 | [diff] [blame] | 543 | #endif | 
| Arik Nemtsov | 1057d35 | 2014-11-19 12:54:26 +0200 | [diff] [blame] | 544 | if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) && | 
|  | 545 | (!rdev->ops->tdls_channel_switch || | 
|  | 546 | !rdev->ops->tdls_cancel_channel_switch))) | 
|  | 547 | return -EINVAL; | 
| Johannes Berg | 77dbbb1 | 2011-07-13 10:48:55 +0200 | [diff] [blame] | 548 |  | 
| Arik Nemtsov | ad932f0 | 2014-11-27 09:44:55 +0200 | [diff] [blame] | 549 | /* | 
|  | 550 | * if a wiphy has unsupported modes for regulatory channel enforcement, | 
|  | 551 | * opt-out of enforcement checking | 
|  | 552 | */ | 
|  | 553 | if (wiphy->interface_modes & ~(BIT(NL80211_IFTYPE_STATION) | | 
|  | 554 | BIT(NL80211_IFTYPE_P2P_CLIENT) | | 
|  | 555 | BIT(NL80211_IFTYPE_AP) | | 
|  | 556 | BIT(NL80211_IFTYPE_P2P_GO) | | 
|  | 557 | BIT(NL80211_IFTYPE_ADHOC) | | 
|  | 558 | BIT(NL80211_IFTYPE_P2P_DEVICE) | | 
|  | 559 | BIT(NL80211_IFTYPE_AP_VLAN) | | 
|  | 560 | BIT(NL80211_IFTYPE_MONITOR))) | 
|  | 561 | wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF; | 
|  | 562 |  | 
| Amitkumar Karwar | be29b99 | 2013-06-28 11:51:26 -0700 | [diff] [blame] | 563 | if (WARN_ON(wiphy->coalesce && | 
|  | 564 | (!wiphy->coalesce->n_rules || | 
|  | 565 | !wiphy->coalesce->n_patterns) && | 
|  | 566 | (!wiphy->coalesce->pattern_min_len || | 
|  | 567 | wiphy->coalesce->pattern_min_len > | 
|  | 568 | wiphy->coalesce->pattern_max_len))) | 
|  | 569 | return -EINVAL; | 
|  | 570 |  | 
| Johannes Berg | 562a748 | 2011-11-07 12:39:33 +0100 | [diff] [blame] | 571 | if (WARN_ON(wiphy->ap_sme_capa && | 
|  | 572 | !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) | 
|  | 573 | return -EINVAL; | 
|  | 574 |  | 
| Johannes Berg | ef15aac | 2010-01-20 12:02:33 +0100 | [diff] [blame] | 575 | if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) | 
|  | 576 | return -EINVAL; | 
|  | 577 |  | 
|  | 578 | if (WARN_ON(wiphy->addresses && | 
|  | 579 | !is_zero_ether_addr(wiphy->perm_addr) && | 
|  | 580 | memcmp(wiphy->perm_addr, wiphy->addresses[0].addr, | 
|  | 581 | ETH_ALEN))) | 
|  | 582 | return -EINVAL; | 
|  | 583 |  | 
| Vasanthakumar Thiagarajan | 77765ea | 2013-01-18 11:18:45 +0530 | [diff] [blame] | 584 | if (WARN_ON(wiphy->max_acl_mac_addrs && | 
|  | 585 | (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) || | 
|  | 586 | !rdev->ops->set_mac_acl))) | 
|  | 587 | return -EINVAL; | 
|  | 588 |  | 
| Johannes Berg | ef15aac | 2010-01-20 12:02:33 +0100 | [diff] [blame] | 589 | if (wiphy->addresses) | 
|  | 590 | memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); | 
|  | 591 |  | 
| Luis R. Rodriguez | f59ac04 | 2008-08-29 16:26:43 -0700 | [diff] [blame] | 592 | /* sanity check ifmodes */ | 
|  | 593 | WARN_ON(!ifmodes); | 
| Johannes Berg | 2e161f7 | 2010-08-12 15:38:38 +0200 | [diff] [blame] | 594 | ifmodes &= ((1 << NUM_NL80211_IFTYPES) - 1) & ~1; | 
| Luis R. Rodriguez | f59ac04 | 2008-08-29 16:26:43 -0700 | [diff] [blame] | 595 | if (WARN_ON(ifmodes != wiphy->interface_modes)) | 
|  | 596 | wiphy->interface_modes = ifmodes; | 
| Johannes Berg | 8318d78 | 2008-01-24 19:38:38 +0100 | [diff] [blame] | 597 |  | 
| Johannes Berg | 7527a78 | 2011-05-13 10:58:57 +0200 | [diff] [blame] | 598 | res = wiphy_verify_combinations(wiphy); | 
|  | 599 | if (res) | 
|  | 600 | return res; | 
|  | 601 |  | 
| Johannes Berg | 8318d78 | 2008-01-24 19:38:38 +0100 | [diff] [blame] | 602 | /* sanity check supported bands/channels */ | 
|  | 603 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 
|  | 604 | sband = wiphy->bands[band]; | 
|  | 605 | if (!sband) | 
|  | 606 | continue; | 
|  | 607 |  | 
|  | 608 | sband->band = band; | 
| Vladimir Kondratiev | 3a0c52a | 2012-07-02 09:32:32 +0300 | [diff] [blame] | 609 | if (WARN_ON(!sband->n_channels)) | 
|  | 610 | return -EINVAL; | 
|  | 611 | /* | 
|  | 612 | * on 60gHz band, there are no legacy rates, so | 
|  | 613 | * n_bitrates is 0 | 
|  | 614 | */ | 
|  | 615 | if (WARN_ON(band != IEEE80211_BAND_60GHZ && | 
|  | 616 | !sband->n_bitrates)) | 
| Johannes Berg | 8318d78 | 2008-01-24 19:38:38 +0100 | [diff] [blame] | 617 | return -EINVAL; | 
| Johannes Berg | 881d948 | 2009-01-21 15:13:48 +0100 | [diff] [blame] | 618 |  | 
|  | 619 | /* | 
| Amitkumar Karwar | 40db6c7 | 2011-04-21 14:10:27 -0700 | [diff] [blame] | 620 | * Since cfg80211_disable_40mhz_24ghz is global, we can | 
|  | 621 | * modify the sband's ht data even if the driver uses a | 
|  | 622 | * global structure for that. | 
|  | 623 | */ | 
|  | 624 | if (cfg80211_disable_40mhz_24ghz && | 
|  | 625 | band == IEEE80211_BAND_2GHZ && | 
|  | 626 | sband->ht_cap.ht_supported) { | 
|  | 627 | sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | 
|  | 628 | sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; | 
|  | 629 | } | 
|  | 630 |  | 
|  | 631 | /* | 
| Johannes Berg | 881d948 | 2009-01-21 15:13:48 +0100 | [diff] [blame] | 632 | * Since we use a u32 for rate bitmaps in | 
|  | 633 | * ieee80211_get_response_rate, we cannot | 
|  | 634 | * have more than 32 legacy rates. | 
|  | 635 | */ | 
|  | 636 | if (WARN_ON(sband->n_bitrates > 32)) | 
|  | 637 | return -EINVAL; | 
| Johannes Berg | 8318d78 | 2008-01-24 19:38:38 +0100 | [diff] [blame] | 638 |  | 
|  | 639 | for (i = 0; i < sband->n_channels; i++) { | 
|  | 640 | sband->channels[i].orig_flags = | 
|  | 641 | sband->channels[i].flags; | 
| Felix Fietkau | c4a9faf | 2012-10-17 13:56:19 +0200 | [diff] [blame] | 642 | sband->channels[i].orig_mag = INT_MAX; | 
| Johannes Berg | 8318d78 | 2008-01-24 19:38:38 +0100 | [diff] [blame] | 643 | sband->channels[i].orig_mpwr = | 
|  | 644 | sband->channels[i].max_power; | 
|  | 645 | sband->channels[i].band = band; | 
|  | 646 | } | 
|  | 647 |  | 
|  | 648 | have_band = true; | 
|  | 649 | } | 
|  | 650 |  | 
|  | 651 | if (!have_band) { | 
|  | 652 | WARN_ON(1); | 
|  | 653 | return -EINVAL; | 
|  | 654 | } | 
|  | 655 |  | 
| Johannes Berg | dfb89c5 | 2012-06-27 09:23:48 +0200 | [diff] [blame] | 656 | #ifdef CONFIG_PM | 
| Johannes Berg | 964dc9e | 2013-06-03 17:25:34 +0200 | [diff] [blame] | 657 | if (WARN_ON(rdev->wiphy.wowlan && rdev->wiphy.wowlan->n_patterns && | 
|  | 658 | (!rdev->wiphy.wowlan->pattern_min_len || | 
|  | 659 | rdev->wiphy.wowlan->pattern_min_len > | 
|  | 660 | rdev->wiphy.wowlan->pattern_max_len))) | 
|  | 661 | return -EINVAL; | 
| Johannes Berg | dfb89c5 | 2012-06-27 09:23:48 +0200 | [diff] [blame] | 662 | #endif | 
| Johannes Berg | ff1b6e6 | 2011-05-04 15:37:28 +0200 | [diff] [blame] | 663 |  | 
| Johannes Berg | 8318d78 | 2008-01-24 19:38:38 +0100 | [diff] [blame] | 664 | /* check and set up bitrates */ | 
|  | 665 | ieee80211_set_bitrate_flags(wiphy); | 
|  | 666 |  | 
| Johannes Berg | 00c3a6e | 2013-10-26 17:14:38 +0200 | [diff] [blame] | 667 | rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH; | 
|  | 668 |  | 
| Johannes Berg | aa5f66d | 2013-09-26 20:03:45 +0200 | [diff] [blame] | 669 | rtnl_lock(); | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 670 | res = device_add(&rdev->wiphy.dev); | 
| John W. Linville | c3d34d5 | 2010-08-30 17:36:40 -0400 | [diff] [blame] | 671 | if (res) { | 
| Johannes Berg | aa5f66d | 2013-09-26 20:03:45 +0200 | [diff] [blame] | 672 | rtnl_unlock(); | 
| John W. Linville | c3d34d5 | 2010-08-30 17:36:40 -0400 | [diff] [blame] | 673 | return res; | 
|  | 674 | } | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 675 |  | 
| Johannes Berg | 2f0accc | 2009-06-10 16:50:29 +0200 | [diff] [blame] | 676 | /* set up regulatory info */ | 
| Luis R. Rodriguez | 57b5ce0 | 2012-07-12 11:49:18 -0700 | [diff] [blame] | 677 | wiphy_regulatory_register(wiphy); | 
| Johannes Berg | 2f0accc | 2009-06-10 16:50:29 +0200 | [diff] [blame] | 678 |  | 
| Johannes Berg | 5f2aa25 | 2010-01-17 15:49:02 +0100 | [diff] [blame] | 679 | list_add_rcu(&rdev->list, &cfg80211_rdev_list); | 
| Johannes Berg | f5ea912 | 2009-08-07 16:17:38 +0200 | [diff] [blame] | 680 | cfg80211_rdev_list_generation++; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 681 |  | 
|  | 682 | /* add to debugfs */ | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 683 | rdev->wiphy.debugfsdir = | 
|  | 684 | debugfs_create_dir(wiphy_name(&rdev->wiphy), | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 685 | ieee80211_debugfs_dir); | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 686 | if (IS_ERR(rdev->wiphy.debugfsdir)) | 
|  | 687 | rdev->wiphy.debugfsdir = NULL; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 688 |  | 
| Tomasz Bursztyka | a796dac | 2014-08-13 16:04:51 +0300 | [diff] [blame] | 689 | cfg80211_debugfs_rdev_add(rdev); | 
|  | 690 | nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); | 
|  | 691 |  | 
| Luis R. Rodriguez | a2f73b6 | 2013-11-11 22:15:29 +0100 | [diff] [blame] | 692 | if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { | 
| Luis R. Rodriguez | 73d54c9 | 2009-03-09 22:07:42 -0400 | [diff] [blame] | 693 | struct regulatory_request request; | 
|  | 694 |  | 
|  | 695 | request.wiphy_idx = get_wiphy_idx(wiphy); | 
|  | 696 | request.initiator = NL80211_REGDOM_SET_BY_DRIVER; | 
|  | 697 | request.alpha2[0] = '9'; | 
|  | 698 | request.alpha2[1] = '9'; | 
|  | 699 |  | 
|  | 700 | nl80211_send_reg_change_event(&request); | 
|  | 701 | } | 
|  | 702 |  | 
| Stanislaw Gruszka | ecb4433 | 2011-08-12 14:00:59 +0200 | [diff] [blame] | 703 | rdev->wiphy.registered = true; | 
|  | 704 | rtnl_unlock(); | 
| Johannes Berg | aa5f66d | 2013-09-26 20:03:45 +0200 | [diff] [blame] | 705 |  | 
|  | 706 | res = rfkill_register(rdev->rfkill); | 
|  | 707 | if (res) { | 
|  | 708 | rfkill_destroy(rdev->rfkill); | 
|  | 709 | rdev->rfkill = NULL; | 
|  | 710 | wiphy_unregister(&rdev->wiphy); | 
|  | 711 | return res; | 
|  | 712 | } | 
|  | 713 |  | 
| Johannes Berg | 2f0accc | 2009-06-10 16:50:29 +0200 | [diff] [blame] | 714 | return 0; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 715 | } | 
|  | 716 | EXPORT_SYMBOL(wiphy_register); | 
|  | 717 |  | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 718 | void wiphy_rfkill_start_polling(struct wiphy *wiphy) | 
|  | 719 | { | 
| Zhao, Gang | f26cbf4 | 2014-04-21 12:53:03 +0800 | [diff] [blame] | 720 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 721 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 722 | if (!rdev->ops->rfkill_poll) | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 723 | return; | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 724 | rdev->rfkill_ops.poll = cfg80211_rfkill_poll; | 
|  | 725 | rfkill_resume_polling(rdev->rfkill); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 726 | } | 
|  | 727 | EXPORT_SYMBOL(wiphy_rfkill_start_polling); | 
|  | 728 |  | 
|  | 729 | void wiphy_rfkill_stop_polling(struct wiphy *wiphy) | 
|  | 730 | { | 
| Zhao, Gang | f26cbf4 | 2014-04-21 12:53:03 +0800 | [diff] [blame] | 731 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 732 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 733 | rfkill_pause_polling(rdev->rfkill); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 734 | } | 
|  | 735 | EXPORT_SYMBOL(wiphy_rfkill_stop_polling); | 
|  | 736 |  | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 737 | void wiphy_unregister(struct wiphy *wiphy) | 
|  | 738 | { | 
| Zhao, Gang | f26cbf4 | 2014-04-21 12:53:03 +0800 | [diff] [blame] | 739 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 740 |  | 
| Johannes Berg | 5fe231e | 2013-05-08 21:45:15 +0200 | [diff] [blame] | 741 | wait_event(rdev->dev_wait, ({ | 
|  | 742 | int __count; | 
|  | 743 | rtnl_lock(); | 
|  | 744 | __count = rdev->opencount; | 
|  | 745 | rtnl_unlock(); | 
|  | 746 | __count == 0; })); | 
|  | 747 |  | 
| Johannes Berg | aa5f66d | 2013-09-26 20:03:45 +0200 | [diff] [blame] | 748 | if (rdev->rfkill) | 
|  | 749 | rfkill_unregister(rdev->rfkill); | 
| Johannes Berg | 256c90d | 2013-06-04 19:21:08 +0200 | [diff] [blame] | 750 |  | 
| Stanislaw Gruszka | ecb4433 | 2011-08-12 14:00:59 +0200 | [diff] [blame] | 751 | rtnl_lock(); | 
| Johannes Berg | 3bb2055 | 2014-05-26 13:52:25 +0200 | [diff] [blame] | 752 | nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); | 
| Stanislaw Gruszka | ecb4433 | 2011-08-12 14:00:59 +0200 | [diff] [blame] | 753 | rdev->wiphy.registered = false; | 
| Stanislaw Gruszka | ecb4433 | 2011-08-12 14:00:59 +0200 | [diff] [blame] | 754 |  | 
| Johannes Berg | 2fd0511 | 2014-04-29 17:52:36 +0200 | [diff] [blame] | 755 | WARN_ON(!list_empty(&rdev->wdev_list)); | 
| Johannes Berg | ad00239 | 2009-08-18 19:51:57 +0200 | [diff] [blame] | 756 |  | 
|  | 757 | /* | 
|  | 758 | * First remove the hardware from everywhere, this makes | 
|  | 759 | * it impossible to find from userspace. | 
|  | 760 | */ | 
| Johannes Berg | 7bcfaf2 | 2009-10-27 12:59:03 +0100 | [diff] [blame] | 761 | debugfs_remove_recursive(rdev->wiphy.debugfsdir); | 
| Johannes Berg | 5f2aa25 | 2010-01-17 15:49:02 +0100 | [diff] [blame] | 762 | list_del_rcu(&rdev->list); | 
|  | 763 | synchronize_rcu(); | 
| Johannes Berg | f16bfc1 | 2007-04-26 20:51:12 -0700 | [diff] [blame] | 764 |  | 
|  | 765 | /* | 
| Luis R. Rodriguez | bfead08 | 2012-07-12 11:49:19 -0700 | [diff] [blame] | 766 | * If this device got a regulatory hint tell core its | 
|  | 767 | * free to listen now to a new shiny device regulatory hint | 
|  | 768 | */ | 
|  | 769 | wiphy_regulatory_deregister(wiphy); | 
| Luis R. Rodriguez | 3f2355c | 2008-11-12 14:22:02 -0800 | [diff] [blame] | 770 |  | 
| Johannes Berg | f5ea912 | 2009-08-07 16:17:38 +0200 | [diff] [blame] | 771 | cfg80211_rdev_list_generation++; | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 772 | device_del(&rdev->wiphy.dev); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 773 |  | 
| Johannes Berg | 5fe231e | 2013-05-08 21:45:15 +0200 | [diff] [blame] | 774 | rtnl_unlock(); | 
| Johannes Berg | 6682588 | 2009-07-13 13:24:44 +0200 | [diff] [blame] | 775 |  | 
| Johannes Berg | 36e6fea | 2009-08-12 22:21:21 +0200 | [diff] [blame] | 776 | flush_work(&rdev->scan_done_wk); | 
| Johannes Berg | 6682588 | 2009-07-13 13:24:44 +0200 | [diff] [blame] | 777 | cancel_work_sync(&rdev->conn_work); | 
| Johannes Berg | 6682588 | 2009-07-13 13:24:44 +0200 | [diff] [blame] | 778 | flush_work(&rdev->event_work); | 
| Simon Wunderlich | 04f3904 | 2013-02-08 18:16:19 +0100 | [diff] [blame] | 779 | cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); | 
| Johannes Berg | 78f22b6 | 2014-03-24 17:57:27 +0100 | [diff] [blame] | 780 | flush_work(&rdev->destroy_work); | 
| Johannes Berg | 6d52563 | 2012-04-04 15:05:25 +0200 | [diff] [blame] | 781 |  | 
| Johannes Berg | 6abb9cb | 2013-05-15 09:30:07 +0200 | [diff] [blame] | 782 | #ifdef CONFIG_PM | 
|  | 783 | if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) | 
| Hila Gonen | e35e4d2 | 2012-06-27 17:19:42 +0300 | [diff] [blame] | 784 | rdev_set_wakeup(rdev, false); | 
| Johannes Berg | 6abb9cb | 2013-05-15 09:30:07 +0200 | [diff] [blame] | 785 | #endif | 
| Johannes Berg | 6d52563 | 2012-04-04 15:05:25 +0200 | [diff] [blame] | 786 | cfg80211_rdev_free_wowlan(rdev); | 
| Amitkumar Karwar | be29b99 | 2013-06-28 11:51:26 -0700 | [diff] [blame] | 787 | cfg80211_rdev_free_coalesce(rdev); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 788 | } | 
|  | 789 | EXPORT_SYMBOL(wiphy_unregister); | 
|  | 790 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 791 | void cfg80211_dev_free(struct cfg80211_registered_device *rdev) | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 792 | { | 
| Johannes Berg | 2a51931 | 2009-02-10 21:25:55 +0100 | [diff] [blame] | 793 | struct cfg80211_internal_bss *scan, *tmp; | 
| Ben Greear | 37c73b5 | 2012-10-26 14:49:25 -0700 | [diff] [blame] | 794 | struct cfg80211_beacon_registration *reg, *treg; | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 795 | rfkill_destroy(rdev->rfkill); | 
| Ben Greear | 37c73b5 | 2012-10-26 14:49:25 -0700 | [diff] [blame] | 796 | list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) { | 
|  | 797 | list_del(®->list); | 
|  | 798 | kfree(reg); | 
|  | 799 | } | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 800 | list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) | 
| Johannes Berg | 5b112d3 | 2013-02-01 01:49:58 +0100 | [diff] [blame] | 801 | cfg80211_put_bss(&rdev->wiphy, &scan->pub); | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 802 | kfree(rdev); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 803 | } | 
|  | 804 |  | 
|  | 805 | void wiphy_free(struct wiphy *wiphy) | 
|  | 806 | { | 
|  | 807 | put_device(&wiphy->dev); | 
|  | 808 | } | 
|  | 809 | EXPORT_SYMBOL(wiphy_free); | 
|  | 810 |  | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 811 | void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) | 
|  | 812 | { | 
| Zhao, Gang | f26cbf4 | 2014-04-21 12:53:03 +0800 | [diff] [blame] | 813 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 814 |  | 
| Johannes Berg | 79c97e9 | 2009-07-07 03:56:12 +0200 | [diff] [blame] | 815 | if (rfkill_set_hw_state(rdev->rfkill, blocked)) | 
|  | 816 | schedule_work(&rdev->rfkill_sync); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 817 | } | 
|  | 818 | EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); | 
|  | 819 |  | 
| Johannes Berg | 98104fde | 2012-06-16 00:19:54 +0200 | [diff] [blame] | 820 | void cfg80211_unregister_wdev(struct wireless_dev *wdev) | 
|  | 821 | { | 
| Zhao, Gang | f26cbf4 | 2014-04-21 12:53:03 +0800 | [diff] [blame] | 822 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | 
| Johannes Berg | 98104fde | 2012-06-16 00:19:54 +0200 | [diff] [blame] | 823 |  | 
|  | 824 | ASSERT_RTNL(); | 
|  | 825 |  | 
|  | 826 | if (WARN_ON(wdev->netdev)) | 
|  | 827 | return; | 
|  | 828 |  | 
| Johannes Berg | 98104fde | 2012-06-16 00:19:54 +0200 | [diff] [blame] | 829 | list_del_rcu(&wdev->list); | 
|  | 830 | rdev->devlist_generation++; | 
|  | 831 |  | 
|  | 832 | switch (wdev->iftype) { | 
|  | 833 | case NL80211_IFTYPE_P2P_DEVICE: | 
| Johannes Berg | f9f4752 | 2013-03-19 15:04:07 +0100 | [diff] [blame] | 834 | cfg80211_stop_p2p_device(rdev, wdev); | 
| Johannes Berg | 98104fde | 2012-06-16 00:19:54 +0200 | [diff] [blame] | 835 | break; | 
|  | 836 | default: | 
|  | 837 | WARN_ON_ONCE(1); | 
|  | 838 | break; | 
|  | 839 | } | 
| Johannes Berg | 98104fde | 2012-06-16 00:19:54 +0200 | [diff] [blame] | 840 | } | 
|  | 841 | EXPORT_SYMBOL(cfg80211_unregister_wdev); | 
|  | 842 |  | 
| Johannes Berg | f1e3d55 | 2014-01-21 00:00:56 +0100 | [diff] [blame] | 843 | static const struct device_type wiphy_type = { | 
| Marcel Holtmann | 053a93d | 2009-10-02 05:15:28 +0000 | [diff] [blame] | 844 | .name	= "wlan", | 
|  | 845 | }; | 
|  | 846 |  | 
| Michal Kazior | dbbae26 | 2012-06-29 12:47:01 +0200 | [diff] [blame] | 847 | void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, | 
|  | 848 | enum nl80211_iftype iftype, int num) | 
|  | 849 | { | 
| Johannes Berg | c5a7e58 | 2012-07-04 13:28:18 +0200 | [diff] [blame] | 850 | ASSERT_RTNL(); | 
| Michal Kazior | dbbae26 | 2012-06-29 12:47:01 +0200 | [diff] [blame] | 851 |  | 
|  | 852 | rdev->num_running_ifaces += num; | 
|  | 853 | if (iftype == NL80211_IFTYPE_MONITOR) | 
|  | 854 | rdev->num_running_monitor_ifaces += num; | 
| Michal Kazior | dbbae26 | 2012-06-29 12:47:01 +0200 | [diff] [blame] | 855 | } | 
|  | 856 |  | 
| Michal Kazior | f04c220 | 2014-04-09 15:11:01 +0200 | [diff] [blame] | 857 | void __cfg80211_leave(struct cfg80211_registered_device *rdev, | 
|  | 858 | struct wireless_dev *wdev) | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 859 | { | 
|  | 860 | struct net_device *dev = wdev->netdev; | 
|  | 861 |  | 
| Barak Bercovitz | 24d584d | 2013-12-05 11:21:26 +0200 | [diff] [blame] | 862 | ASSERT_RTNL(); | 
| Michal Kazior | f04c220 | 2014-04-09 15:11:01 +0200 | [diff] [blame] | 863 | ASSERT_WDEV_LOCK(wdev); | 
| Barak Bercovitz | 24d584d | 2013-12-05 11:21:26 +0200 | [diff] [blame] | 864 |  | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 865 | switch (wdev->iftype) { | 
|  | 866 | case NL80211_IFTYPE_ADHOC: | 
| Michal Kazior | f04c220 | 2014-04-09 15:11:01 +0200 | [diff] [blame] | 867 | __cfg80211_leave_ibss(rdev, dev, true); | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 868 | break; | 
|  | 869 | case NL80211_IFTYPE_P2P_CLIENT: | 
|  | 870 | case NL80211_IFTYPE_STATION: | 
| Barak Bercovitz | 24d584d | 2013-12-05 11:21:26 +0200 | [diff] [blame] | 871 | if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev) | 
|  | 872 | __cfg80211_stop_sched_scan(rdev, false); | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 873 |  | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 874 | #ifdef CONFIG_CFG80211_WEXT | 
|  | 875 | kfree(wdev->wext.ie); | 
|  | 876 | wdev->wext.ie = NULL; | 
|  | 877 | wdev->wext.ie_len = 0; | 
|  | 878 | wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; | 
|  | 879 | #endif | 
| Johannes Berg | 83739b0 | 2013-05-15 17:44:01 +0200 | [diff] [blame] | 880 | cfg80211_disconnect(rdev, dev, | 
|  | 881 | WLAN_REASON_DEAUTH_LEAVING, true); | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 882 | break; | 
|  | 883 | case NL80211_IFTYPE_MESH_POINT: | 
| Michal Kazior | f04c220 | 2014-04-09 15:11:01 +0200 | [diff] [blame] | 884 | __cfg80211_leave_mesh(rdev, dev); | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 885 | break; | 
|  | 886 | case NL80211_IFTYPE_AP: | 
| Johannes Berg | 74418ed | 2013-07-30 10:11:25 +0200 | [diff] [blame] | 887 | case NL80211_IFTYPE_P2P_GO: | 
| Michal Kazior | f04c220 | 2014-04-09 15:11:01 +0200 | [diff] [blame] | 888 | __cfg80211_stop_ap(rdev, dev, true); | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 889 | break; | 
| Rostislav Lisovy | 6e0bd6c | 2014-11-03 10:33:18 +0100 | [diff] [blame] | 890 | case NL80211_IFTYPE_OCB: | 
|  | 891 | __cfg80211_leave_ocb(rdev, dev); | 
|  | 892 | break; | 
| Johannes Berg | de4fcba | 2014-10-31 14:16:12 +0100 | [diff] [blame] | 893 | case NL80211_IFTYPE_WDS: | 
|  | 894 | /* must be handled by mac80211/driver, has no APIs */ | 
|  | 895 | break; | 
|  | 896 | case NL80211_IFTYPE_P2P_DEVICE: | 
|  | 897 | /* cannot happen, has no netdev */ | 
|  | 898 | break; | 
|  | 899 | case NL80211_IFTYPE_AP_VLAN: | 
|  | 900 | case NL80211_IFTYPE_MONITOR: | 
|  | 901 | /* nothing to do */ | 
|  | 902 | break; | 
|  | 903 | case NL80211_IFTYPE_UNSPECIFIED: | 
|  | 904 | case NUM_NL80211_IFTYPES: | 
|  | 905 | /* invalid */ | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 906 | break; | 
|  | 907 | } | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 908 | } | 
|  | 909 |  | 
| Michal Kazior | f04c220 | 2014-04-09 15:11:01 +0200 | [diff] [blame] | 910 | void cfg80211_leave(struct cfg80211_registered_device *rdev, | 
|  | 911 | struct wireless_dev *wdev) | 
|  | 912 | { | 
|  | 913 | wdev_lock(wdev); | 
|  | 914 | __cfg80211_leave(rdev, wdev); | 
|  | 915 | wdev_unlock(wdev); | 
|  | 916 | } | 
|  | 917 |  | 
|  | 918 | void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, | 
|  | 919 | gfp_t gfp) | 
|  | 920 | { | 
|  | 921 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|  | 922 | struct cfg80211_event *ev; | 
|  | 923 | unsigned long flags; | 
|  | 924 |  | 
|  | 925 | trace_cfg80211_stop_iface(wiphy, wdev); | 
|  | 926 |  | 
|  | 927 | ev = kzalloc(sizeof(*ev), gfp); | 
|  | 928 | if (!ev) | 
|  | 929 | return; | 
|  | 930 |  | 
|  | 931 | ev->type = EVENT_STOPPED; | 
|  | 932 |  | 
|  | 933 | spin_lock_irqsave(&wdev->event_lock, flags); | 
|  | 934 | list_add_tail(&ev->list, &wdev->event_list); | 
|  | 935 | spin_unlock_irqrestore(&wdev->event_lock, flags); | 
|  | 936 | queue_work(cfg80211_wq, &rdev->event_work); | 
|  | 937 | } | 
|  | 938 | EXPORT_SYMBOL(cfg80211_stop_iface); | 
|  | 939 |  | 
| Cristian Chilipirea | c4f6084 | 2012-05-08 12:38:53 +0300 | [diff] [blame] | 940 | static int cfg80211_netdev_notifier_call(struct notifier_block *nb, | 
| Jiri Pirko | 351638e | 2013-05-28 01:30:21 +0000 | [diff] [blame] | 941 | unsigned long state, void *ptr) | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 942 | { | 
| Jiri Pirko | 351638e | 2013-05-28 01:30:21 +0000 | [diff] [blame] | 943 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | 
| Johannes Berg | 2a783c1 | 2009-07-01 21:26:45 +0200 | [diff] [blame] | 944 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 945 | struct cfg80211_registered_device *rdev; | 
|  | 946 |  | 
| Johannes Berg | 2a783c1 | 2009-07-01 21:26:45 +0200 | [diff] [blame] | 947 | if (!wdev) | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 948 | return NOTIFY_DONE; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 949 |  | 
| Zhao, Gang | f26cbf4 | 2014-04-21 12:53:03 +0800 | [diff] [blame] | 950 | rdev = wiphy_to_rdev(wdev->wiphy); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 951 |  | 
| Johannes Berg | 2a783c1 | 2009-07-01 21:26:45 +0200 | [diff] [blame] | 952 | WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); | 
| Johannes Berg | 60719ff | 2008-09-16 14:55:09 +0200 | [diff] [blame] | 953 |  | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 954 | switch (state) { | 
| Marcel Holtmann | 053a93d | 2009-10-02 05:15:28 +0000 | [diff] [blame] | 955 | case NETDEV_POST_INIT: | 
|  | 956 | SET_NETDEV_DEVTYPE(dev, &wiphy_type); | 
|  | 957 | break; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 958 | case NETDEV_REGISTER: | 
| Johannes Berg | 0ff6ce7 | 2009-08-17 12:25:37 +0200 | [diff] [blame] | 959 | /* | 
|  | 960 | * NB: cannot take rdev->mtx here because this may be | 
|  | 961 | * called within code protected by it when interfaces | 
|  | 962 | * are added with nl80211. | 
|  | 963 | */ | 
| Johannes Berg | 667503dd | 2009-07-07 03:56:11 +0200 | [diff] [blame] | 964 | mutex_init(&wdev->mtx); | 
|  | 965 | INIT_LIST_HEAD(&wdev->event_list); | 
|  | 966 | spin_lock_init(&wdev->event_lock); | 
| Johannes Berg | 2e161f7 | 2010-08-12 15:38:38 +0200 | [diff] [blame] | 967 | INIT_LIST_HEAD(&wdev->mgmt_registrations); | 
|  | 968 | spin_lock_init(&wdev->mgmt_registrations_lock); | 
| Jouni Malinen | 026331c | 2010-02-15 12:53:10 +0200 | [diff] [blame] | 969 |  | 
| Johannes Berg | 89a54e4 | 2012-06-15 14:33:17 +0200 | [diff] [blame] | 970 | wdev->identifier = ++rdev->wdev_id; | 
|  | 971 | list_add_rcu(&wdev->list, &rdev->wdev_list); | 
| Johannes Berg | f5ea912 | 2009-08-07 16:17:38 +0200 | [diff] [blame] | 972 | rdev->devlist_generation++; | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 973 | /* can only change netns with wiphy */ | 
|  | 974 | dev->features |= NETIF_F_NETNS_LOCAL; | 
|  | 975 |  | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 976 | if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, | 
|  | 977 | "phy80211")) { | 
| Joe Perches | e9c0268 | 2010-11-16 19:56:49 -0800 | [diff] [blame] | 978 | pr_err("failed to add phy80211 symlink to netdev!\n"); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 979 | } | 
| Johannes Berg | 2a783c1 | 2009-07-01 21:26:45 +0200 | [diff] [blame] | 980 | wdev->netdev = dev; | 
| Johannes Berg | 3d23e34 | 2009-09-29 23:27:28 +0200 | [diff] [blame] | 981 | #ifdef CONFIG_CFG80211_WEXT | 
| Johannes Berg | 2a783c1 | 2009-07-01 21:26:45 +0200 | [diff] [blame] | 982 | wdev->wext.default_key = -1; | 
|  | 983 | wdev->wext.default_mgmt_key = -1; | 
| Johannes Berg | f212935 | 2009-07-01 21:26:56 +0200 | [diff] [blame] | 984 | wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; | 
| Kalle Valo | ffb9eb3 | 2010-02-17 17:58:10 +0200 | [diff] [blame] | 985 | #endif | 
|  | 986 |  | 
| Johannes Berg | 5be83de | 2009-11-19 00:56:28 +0100 | [diff] [blame] | 987 | if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) | 
| Kalle Valo | ffb9eb3 | 2010-02-17 17:58:10 +0200 | [diff] [blame] | 988 | wdev->ps = true; | 
| Johannes Berg | 5be83de | 2009-11-19 00:56:28 +0100 | [diff] [blame] | 989 | else | 
| Kalle Valo | ffb9eb3 | 2010-02-17 17:58:10 +0200 | [diff] [blame] | 990 | wdev->ps = false; | 
| Juuso Oikarinen | 9043f3b | 2010-04-27 12:47:41 +0300 | [diff] [blame] | 991 | /* allow mac80211 to determine the timeout */ | 
|  | 992 | wdev->ps_timeout = -1; | 
| Kalle Valo | ffb9eb3 | 2010-02-17 17:58:10 +0200 | [diff] [blame] | 993 |  | 
| Johannes Berg | ad4bb6f | 2009-11-19 00:56:30 +0100 | [diff] [blame] | 994 | if ((wdev->iftype == NL80211_IFTYPE_STATION || | 
| Johannes Berg | 074ac8d | 2010-09-16 14:58:22 +0200 | [diff] [blame] | 995 | wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || | 
| Johannes Berg | ad4bb6f | 2009-11-19 00:56:30 +0100 | [diff] [blame] | 996 | wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) | 
|  | 997 | dev->priv_flags |= IFF_DONT_BRIDGE; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 998 | break; | 
| Johannes Berg | 04a773a | 2009-04-19 21:24:32 +0200 | [diff] [blame] | 999 | case NETDEV_GOING_DOWN: | 
| Stanislaw Gruszka | 8125696 | 2013-02-28 10:55:25 +0100 | [diff] [blame] | 1000 | cfg80211_leave(rdev, wdev); | 
| Johannes Berg | 01a0ac4 | 2009-08-20 21:36:16 +0200 | [diff] [blame] | 1001 | break; | 
|  | 1002 | case NETDEV_DOWN: | 
| Michal Kazior | dbbae26 | 2012-06-29 12:47:01 +0200 | [diff] [blame] | 1003 | cfg80211_update_iface_num(rdev, wdev->iftype, -1); | 
| Johannes Berg | a617302 | 2014-01-22 11:14:18 +0200 | [diff] [blame] | 1004 | if (rdev->scan_req && rdev->scan_req->wdev == wdev) { | 
|  | 1005 | if (WARN_ON(!rdev->scan_req->notified)) | 
|  | 1006 | rdev->scan_req->aborted = true; | 
| Johannes Berg | f9d15d1 | 2014-01-22 11:14:19 +0200 | [diff] [blame] | 1007 | ___cfg80211_scan_done(rdev, false); | 
| Johannes Berg | a617302 | 2014-01-22 11:14:18 +0200 | [diff] [blame] | 1008 | } | 
| Johannes Berg | 5fe231e | 2013-05-08 21:45:15 +0200 | [diff] [blame] | 1009 |  | 
|  | 1010 | if (WARN_ON(rdev->sched_scan_req && | 
|  | 1011 | rdev->sched_scan_req->dev == wdev->netdev)) { | 
|  | 1012 | __cfg80211_stop_sched_scan(rdev, false); | 
|  | 1013 | } | 
|  | 1014 |  | 
|  | 1015 | rdev->opencount--; | 
|  | 1016 | wake_up(&rdev->dev_wait); | 
| Johannes Berg | 04a773a | 2009-04-19 21:24:32 +0200 | [diff] [blame] | 1017 | break; | 
|  | 1018 | case NETDEV_UP: | 
| Johannes Berg | 4290cb4 | 2012-07-12 22:19:48 +0200 | [diff] [blame] | 1019 | cfg80211_update_iface_num(rdev, wdev->iftype, 1); | 
| Johannes Berg | 667503dd | 2009-07-07 03:56:11 +0200 | [diff] [blame] | 1020 | wdev_lock(wdev); | 
| Johannes Berg | f212935 | 2009-07-01 21:26:56 +0200 | [diff] [blame] | 1021 | switch (wdev->iftype) { | 
| Johannes Berg | 29cbe68 | 2010-12-03 09:20:44 +0100 | [diff] [blame] | 1022 | #ifdef CONFIG_CFG80211_WEXT | 
| Johannes Berg | f212935 | 2009-07-01 21:26:56 +0200 | [diff] [blame] | 1023 | case NL80211_IFTYPE_ADHOC: | 
| Johannes Berg | fffd093 | 2009-07-08 14:22:54 +0200 | [diff] [blame] | 1024 | cfg80211_ibss_wext_join(rdev, wdev); | 
| Johannes Berg | 04a773a | 2009-04-19 21:24:32 +0200 | [diff] [blame] | 1025 | break; | 
| Johannes Berg | f212935 | 2009-07-01 21:26:56 +0200 | [diff] [blame] | 1026 | case NL80211_IFTYPE_STATION: | 
| Johannes Berg | fffd093 | 2009-07-08 14:22:54 +0200 | [diff] [blame] | 1027 | cfg80211_mgd_wext_connect(rdev, wdev); | 
| Johannes Berg | 04a773a | 2009-04-19 21:24:32 +0200 | [diff] [blame] | 1028 | break; | 
| Johannes Berg | 29cbe68 | 2010-12-03 09:20:44 +0100 | [diff] [blame] | 1029 | #endif | 
| Javier Cardona | c80d545 | 2010-12-16 17:37:49 -0800 | [diff] [blame] | 1030 | #ifdef CONFIG_MAC80211_MESH | 
| Johannes Berg | 29cbe68 | 2010-12-03 09:20:44 +0100 | [diff] [blame] | 1031 | case NL80211_IFTYPE_MESH_POINT: | 
| Javier Cardona | c80d545 | 2010-12-16 17:37:49 -0800 | [diff] [blame] | 1032 | { | 
|  | 1033 | /* backward compat code... */ | 
|  | 1034 | struct mesh_setup setup; | 
|  | 1035 | memcpy(&setup, &default_mesh_setup, | 
|  | 1036 | sizeof(setup)); | 
|  | 1037 | /* back compat only needed for mesh_id */ | 
|  | 1038 | setup.mesh_id = wdev->ssid; | 
|  | 1039 | setup.mesh_id_len = wdev->mesh_id_up_len; | 
|  | 1040 | if (wdev->mesh_id_up_len) | 
|  | 1041 | __cfg80211_join_mesh(rdev, dev, | 
|  | 1042 | &setup, | 
|  | 1043 | &default_mesh_config); | 
|  | 1044 | break; | 
|  | 1045 | } | 
|  | 1046 | #endif | 
| Johannes Berg | f212935 | 2009-07-01 21:26:56 +0200 | [diff] [blame] | 1047 | default: | 
|  | 1048 | break; | 
|  | 1049 | } | 
| Johannes Berg | 667503dd | 2009-07-07 03:56:11 +0200 | [diff] [blame] | 1050 | wdev_unlock(wdev); | 
| Johannes Berg | ad00239 | 2009-08-18 19:51:57 +0200 | [diff] [blame] | 1051 | rdev->opencount++; | 
| Juuso Oikarinen | bf6a057 | 2011-01-31 15:52:58 +0200 | [diff] [blame] | 1052 |  | 
|  | 1053 | /* | 
|  | 1054 | * Configure power management to the driver here so that its | 
|  | 1055 | * correctly set also after interface type changes etc. | 
|  | 1056 | */ | 
| Eliad Peller | 5966f2d | 2011-07-19 12:57:13 +0300 | [diff] [blame] | 1057 | if ((wdev->iftype == NL80211_IFTYPE_STATION || | 
|  | 1058 | wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && | 
| Juuso Oikarinen | bf6a057 | 2011-01-31 15:52:58 +0200 | [diff] [blame] | 1059 | rdev->ops->set_power_mgmt) | 
| Hila Gonen | e35e4d2 | 2012-06-27 17:19:42 +0300 | [diff] [blame] | 1060 | if (rdev_set_power_mgmt(rdev, dev, wdev->ps, | 
|  | 1061 | wdev->ps_timeout)) { | 
| Juuso Oikarinen | bf6a057 | 2011-01-31 15:52:58 +0200 | [diff] [blame] | 1062 | /* assume this means it's off */ | 
|  | 1063 | wdev->ps = false; | 
|  | 1064 | } | 
| Johannes Berg | 2a783c1 | 2009-07-01 21:26:45 +0200 | [diff] [blame] | 1065 | break; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1066 | case NETDEV_UNREGISTER: | 
| Johannes Berg | 0ff6ce7 | 2009-08-17 12:25:37 +0200 | [diff] [blame] | 1067 | /* | 
| Johannes Berg | e40cbdac | 2009-07-30 14:04:01 +0200 | [diff] [blame] | 1068 | * It is possible to get NETDEV_UNREGISTER | 
|  | 1069 | * multiple times. To detect that, check | 
|  | 1070 | * that the interface is still on the list | 
|  | 1071 | * of registered interfaces, and only then | 
|  | 1072 | * remove and clean it up. | 
|  | 1073 | */ | 
| Johannes Berg | 2a783c1 | 2009-07-01 21:26:45 +0200 | [diff] [blame] | 1074 | if (!list_empty(&wdev->list)) { | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1075 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); | 
| Johannes Berg | 5f2aa25 | 2010-01-17 15:49:02 +0100 | [diff] [blame] | 1076 | list_del_rcu(&wdev->list); | 
| Johannes Berg | f5ea912 | 2009-08-07 16:17:38 +0200 | [diff] [blame] | 1077 | rdev->devlist_generation++; | 
| Johannes Berg | 2e161f7 | 2010-08-12 15:38:38 +0200 | [diff] [blame] | 1078 | cfg80211_mlme_purge_registrations(wdev); | 
| Johannes Berg | 3d23e34 | 2009-09-29 23:27:28 +0200 | [diff] [blame] | 1079 | #ifdef CONFIG_CFG80211_WEXT | 
| Johannes Berg | 538c9eb | 2014-09-10 13:39:56 +0300 | [diff] [blame] | 1080 | kzfree(wdev->wext.keys); | 
| Johannes Berg | e40cbdac | 2009-07-30 14:04:01 +0200 | [diff] [blame] | 1081 | #endif | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1082 | } | 
| Johannes Berg | 5f2aa25 | 2010-01-17 15:49:02 +0100 | [diff] [blame] | 1083 | /* | 
|  | 1084 | * synchronise (so that we won't find this netdev | 
|  | 1085 | * from other code any more) and then clear the list | 
|  | 1086 | * head so that the above code can safely check for | 
|  | 1087 | * !list_empty() to avoid double-cleanup. | 
|  | 1088 | */ | 
|  | 1089 | synchronize_rcu(); | 
|  | 1090 | INIT_LIST_HEAD(&wdev->list); | 
| Daniel Drake | 1f6fc43 | 2012-08-02 18:41:48 +0100 | [diff] [blame] | 1091 | /* | 
|  | 1092 | * Ensure that all events have been processed and | 
|  | 1093 | * freed. | 
|  | 1094 | */ | 
|  | 1095 | cfg80211_process_wdev_events(wdev); | 
| Ben Greear | f9bef3d | 2013-06-19 14:06:26 -0700 | [diff] [blame] | 1096 |  | 
|  | 1097 | if (WARN_ON(wdev->current_bss)) { | 
|  | 1098 | cfg80211_unhold_bss(wdev->current_bss); | 
|  | 1099 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); | 
|  | 1100 | wdev->current_bss = NULL; | 
|  | 1101 | } | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1102 | break; | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 1103 | case NETDEV_PRE_UP: | 
| Johannes Berg | 0b20633d | 2009-07-07 03:56:13 +0200 | [diff] [blame] | 1104 | if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) | 
|  | 1105 | return notifier_from_errno(-EOPNOTSUPP); | 
| Luciano Coelho | b6a5501 | 2014-02-27 11:07:21 +0200 | [diff] [blame] | 1106 | if (rfkill_blocked(rdev->rfkill)) | 
|  | 1107 | return notifier_from_errno(-ERFKILL); | 
| Johannes Berg | 1f87f7d | 2009-06-02 13:01:41 +0200 | [diff] [blame] | 1108 | break; | 
| Zhao, Gang | 6784c7d | 2014-04-21 12:53:04 +0800 | [diff] [blame] | 1109 | default: | 
|  | 1110 | return NOTIFY_DONE; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1111 | } | 
|  | 1112 |  | 
| Zhao, Gang | 6784c7d | 2014-04-21 12:53:04 +0800 | [diff] [blame] | 1113 | return NOTIFY_OK; | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1114 | } | 
|  | 1115 |  | 
|  | 1116 | static struct notifier_block cfg80211_netdev_notifier = { | 
|  | 1117 | .notifier_call = cfg80211_netdev_notifier_call, | 
|  | 1118 | }; | 
|  | 1119 |  | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 1120 | static void __net_exit cfg80211_pernet_exit(struct net *net) | 
|  | 1121 | { | 
|  | 1122 | struct cfg80211_registered_device *rdev; | 
|  | 1123 |  | 
|  | 1124 | rtnl_lock(); | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 1125 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 
|  | 1126 | if (net_eq(wiphy_net(&rdev->wiphy), net)) | 
|  | 1127 | WARN_ON(cfg80211_switch_netns(rdev, &init_net)); | 
|  | 1128 | } | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 1129 | rtnl_unlock(); | 
|  | 1130 | } | 
|  | 1131 |  | 
|  | 1132 | static struct pernet_operations cfg80211_pernet_ops = { | 
|  | 1133 | .exit = cfg80211_pernet_exit, | 
|  | 1134 | }; | 
|  | 1135 |  | 
|  | 1136 | static int __init cfg80211_init(void) | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1137 | { | 
| Luis R. Rodriguez | b2e1b30 | 2008-09-09 23:19:48 -0700 | [diff] [blame] | 1138 | int err; | 
|  | 1139 |  | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 1140 | err = register_pernet_device(&cfg80211_pernet_ops); | 
|  | 1141 | if (err) | 
|  | 1142 | goto out_fail_pernet; | 
|  | 1143 |  | 
| Luis R. Rodriguez | b2e1b30 | 2008-09-09 23:19:48 -0700 | [diff] [blame] | 1144 | err = wiphy_sysfs_init(); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1145 | if (err) | 
|  | 1146 | goto out_fail_sysfs; | 
|  | 1147 |  | 
|  | 1148 | err = register_netdevice_notifier(&cfg80211_netdev_notifier); | 
|  | 1149 | if (err) | 
|  | 1150 | goto out_fail_notifier; | 
|  | 1151 |  | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 1152 | err = nl80211_init(); | 
|  | 1153 | if (err) | 
|  | 1154 | goto out_fail_nl80211; | 
|  | 1155 |  | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1156 | ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); | 
|  | 1157 |  | 
| Luis R. Rodriguez | b2e1b30 | 2008-09-09 23:19:48 -0700 | [diff] [blame] | 1158 | err = regulatory_init(); | 
|  | 1159 | if (err) | 
|  | 1160 | goto out_fail_reg; | 
|  | 1161 |  | 
| Alban Browaeys | e60d744 | 2009-11-25 15:13:00 +0100 | [diff] [blame] | 1162 | cfg80211_wq = create_singlethread_workqueue("cfg80211"); | 
| Wei Yongjun | f00f188 | 2013-03-20 20:22:54 +0800 | [diff] [blame] | 1163 | if (!cfg80211_wq) { | 
|  | 1164 | err = -ENOMEM; | 
| Alban Browaeys | e60d744 | 2009-11-25 15:13:00 +0100 | [diff] [blame] | 1165 | goto out_fail_wq; | 
| Wei Yongjun | f00f188 | 2013-03-20 20:22:54 +0800 | [diff] [blame] | 1166 | } | 
| Alban Browaeys | e60d744 | 2009-11-25 15:13:00 +0100 | [diff] [blame] | 1167 |  | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1168 | return 0; | 
|  | 1169 |  | 
| Alban Browaeys | e60d744 | 2009-11-25 15:13:00 +0100 | [diff] [blame] | 1170 | out_fail_wq: | 
|  | 1171 | regulatory_exit(); | 
| Luis R. Rodriguez | b2e1b30 | 2008-09-09 23:19:48 -0700 | [diff] [blame] | 1172 | out_fail_reg: | 
|  | 1173 | debugfs_remove(ieee80211_debugfs_dir); | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 1174 | out_fail_nl80211: | 
|  | 1175 | unregister_netdevice_notifier(&cfg80211_netdev_notifier); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1176 | out_fail_notifier: | 
|  | 1177 | wiphy_sysfs_exit(); | 
|  | 1178 | out_fail_sysfs: | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 1179 | unregister_pernet_device(&cfg80211_pernet_ops); | 
|  | 1180 | out_fail_pernet: | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1181 | return err; | 
|  | 1182 | } | 
| Johannes Berg | 3a46246 | 2007-09-10 13:44:45 +0200 | [diff] [blame] | 1183 | subsys_initcall(cfg80211_init); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1184 |  | 
| Uwe Kleine-König | f884e38 | 2010-06-18 09:38:54 +0200 | [diff] [blame] | 1185 | static void __exit cfg80211_exit(void) | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1186 | { | 
|  | 1187 | debugfs_remove(ieee80211_debugfs_dir); | 
| Johannes Berg | 5568296 | 2007-09-20 13:09:35 -0400 | [diff] [blame] | 1188 | nl80211_exit(); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1189 | unregister_netdevice_notifier(&cfg80211_netdev_notifier); | 
|  | 1190 | wiphy_sysfs_exit(); | 
| Luis R. Rodriguez | b2e1b30 | 2008-09-09 23:19:48 -0700 | [diff] [blame] | 1191 | regulatory_exit(); | 
| Johannes Berg | 463d018 | 2009-07-14 00:33:35 +0200 | [diff] [blame] | 1192 | unregister_pernet_device(&cfg80211_pernet_ops); | 
| Alban Browaeys | e60d744 | 2009-11-25 15:13:00 +0100 | [diff] [blame] | 1193 | destroy_workqueue(cfg80211_wq); | 
| Johannes Berg | 704232c | 2007-04-23 12:20:05 -0700 | [diff] [blame] | 1194 | } | 
|  | 1195 | module_exit(cfg80211_exit); |