blob: e347093ccc73f8716a47489bb64e3907efc09133 [file] [log] [blame]
Johannes Berg704232c2007-04-23 12:20:05 -07001/*
2 * This is the linux wireless configuration interface.
3 *
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -07004 * Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
Johannes Berg704232c2007-04-23 12:20:05 -07005 */
6
7#include <linux/if.h>
8#include <linux/module.h>
9#include <linux/err.h>
Johannes Berg704232c2007-04-23 12:20:05 -070010#include <linux/list.h>
11#include <linux/nl80211.h>
12#include <linux/debugfs.h>
13#include <linux/notifier.h>
14#include <linux/device.h>
15#include <net/genetlink.h>
16#include <net/cfg80211.h>
17#include <net/wireless.h>
Johannes Berg55682962007-09-20 13:09:35 -040018#include "nl80211.h"
Johannes Berg704232c2007-04-23 12:20:05 -070019#include "core.h"
20#include "sysfs.h"
21
22/* name for sysfs, %d is appended */
23#define PHY_NAME "phy"
24
25MODULE_AUTHOR("Johannes Berg");
26MODULE_LICENSE("GPL");
27MODULE_DESCRIPTION("wireless configuration support");
28
29/* RCU might be appropriate here since we usually
30 * only read the list, and that can happen quite
31 * often because we need to do it for each command */
32LIST_HEAD(cfg80211_drv_list);
Luis R. Rodrigueza1794392009-02-21 00:04:21 -050033
34/*
35 * This is used to protect the cfg80211_drv_list, cfg80211_regdomain, and
36 * the last reguluatory request receipt in regd.c
37 */
38DEFINE_MUTEX(cfg80211_mutex);
Johannes Berg704232c2007-04-23 12:20:05 -070039
40/* for debugfs */
41static struct dentry *ieee80211_debugfs_dir;
42
Johannes Berg55682962007-09-20 13:09:35 -040043/* requires cfg80211_drv_mutex to be held! */
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -050044static struct cfg80211_registered_device *
45cfg80211_drv_by_wiphy_idx(int wiphy_idx)
Johannes Berg55682962007-09-20 13:09:35 -040046{
47 struct cfg80211_registered_device *result = NULL, *drv;
48
Luis R. Rodriguez85fd1292009-02-21 00:04:20 -050049 if (!wiphy_idx_valid(wiphy_idx))
50 return NULL;
51
Luis R. Rodriguez761cf7e2009-02-21 00:04:25 -050052 assert_cfg80211_lock();
53
Johannes Berg55682962007-09-20 13:09:35 -040054 list_for_each_entry(drv, &cfg80211_drv_list, list) {
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -050055 if (drv->wiphy_idx == wiphy_idx) {
Johannes Berg55682962007-09-20 13:09:35 -040056 result = drv;
57 break;
58 }
59 }
60
61 return result;
62}
63
Luis R. Rodrigueza1794392009-02-21 00:04:21 -050064/* requires cfg80211_mutex to be held! */
Johannes Berg55682962007-09-20 13:09:35 -040065static struct cfg80211_registered_device *
66__cfg80211_drv_from_info(struct genl_info *info)
67{
68 int ifindex;
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -050069 struct cfg80211_registered_device *bywiphyidx = NULL, *byifidx = NULL;
Johannes Berg55682962007-09-20 13:09:35 -040070 struct net_device *dev;
71 int err = -EINVAL;
72
Luis R. Rodriguez761cf7e2009-02-21 00:04:25 -050073 assert_cfg80211_lock();
74
Johannes Berg55682962007-09-20 13:09:35 -040075 if (info->attrs[NL80211_ATTR_WIPHY]) {
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -050076 bywiphyidx = cfg80211_drv_by_wiphy_idx(
Johannes Berg55682962007-09-20 13:09:35 -040077 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
78 err = -ENODEV;
79 }
80
81 if (info->attrs[NL80211_ATTR_IFINDEX]) {
82 ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
83 dev = dev_get_by_index(&init_net, ifindex);
84 if (dev) {
85 if (dev->ieee80211_ptr)
86 byifidx =
87 wiphy_to_dev(dev->ieee80211_ptr->wiphy);
88 dev_put(dev);
89 }
90 err = -ENODEV;
91 }
92
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -050093 if (bywiphyidx && byifidx) {
94 if (bywiphyidx != byifidx)
Johannes Berg55682962007-09-20 13:09:35 -040095 return ERR_PTR(-EINVAL);
96 else
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -050097 return bywiphyidx; /* == byifidx */
Johannes Berg55682962007-09-20 13:09:35 -040098 }
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -050099 if (bywiphyidx)
100 return bywiphyidx;
Johannes Berg55682962007-09-20 13:09:35 -0400101
102 if (byifidx)
103 return byifidx;
104
105 return ERR_PTR(err);
106}
107
108struct cfg80211_registered_device *
109cfg80211_get_dev_from_info(struct genl_info *info)
110{
111 struct cfg80211_registered_device *drv;
112
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500113 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400114 drv = __cfg80211_drv_from_info(info);
115
116 /* if it is not an error we grab the lock on
117 * it to assure it won't be going away while
118 * we operate on it */
119 if (!IS_ERR(drv))
120 mutex_lock(&drv->mtx);
121
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500122 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400123
124 return drv;
125}
126
127struct cfg80211_registered_device *
128cfg80211_get_dev_from_ifindex(int ifindex)
129{
130 struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV);
131 struct net_device *dev;
132
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500133 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400134 dev = dev_get_by_index(&init_net, ifindex);
135 if (!dev)
136 goto out;
137 if (dev->ieee80211_ptr) {
138 drv = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
139 mutex_lock(&drv->mtx);
140 } else
141 drv = ERR_PTR(-ENODEV);
142 dev_put(dev);
143 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500144 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400145 return drv;
146}
147
148void cfg80211_put_dev(struct cfg80211_registered_device *drv)
149{
150 BUG_ON(IS_ERR(drv));
151 mutex_unlock(&drv->mtx);
152}
153
154int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
155 char *newname)
156{
Eric W. Biederman2940bb62008-05-08 14:30:18 -0700157 struct cfg80211_registered_device *drv;
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500158 int wiphy_idx, taken = -1, result, digits;
Johannes Berg55682962007-09-20 13:09:35 -0400159
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500160 mutex_lock(&cfg80211_mutex);
Eric W. Biederman2940bb62008-05-08 14:30:18 -0700161
Johannes Berg55682962007-09-20 13:09:35 -0400162 /* prohibit calling the thing phy%d when %d is not its number */
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500163 sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
164 if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) {
165 /* count number of places needed to print wiphy_idx */
Johannes Berg55682962007-09-20 13:09:35 -0400166 digits = 1;
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500167 while (wiphy_idx /= 10)
Johannes Berg55682962007-09-20 13:09:35 -0400168 digits++;
169 /*
170 * deny the name if it is phy<idx> where <idx> is printed
171 * without leading zeroes. taken == strlen(newname) here
172 */
Eric W. Biederman2940bb62008-05-08 14:30:18 -0700173 result = -EINVAL;
Johannes Berg55682962007-09-20 13:09:35 -0400174 if (taken == strlen(PHY_NAME) + digits)
Eric W. Biederman2940bb62008-05-08 14:30:18 -0700175 goto out_unlock;
Johannes Berg55682962007-09-20 13:09:35 -0400176 }
177
Eric W. Biederman2940bb62008-05-08 14:30:18 -0700178
179 /* Ignore nop renames */
180 result = 0;
181 if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0)
182 goto out_unlock;
183
184 /* Ensure another device does not already have this name. */
185 list_for_each_entry(drv, &cfg80211_drv_list, list) {
186 result = -EINVAL;
187 if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0)
188 goto out_unlock;
189 }
190
191 /* this will only check for collisions in sysfs
192 * which is not even always compiled in.
193 */
Johannes Berg55682962007-09-20 13:09:35 -0400194 result = device_rename(&rdev->wiphy.dev, newname);
195 if (result)
Eric W. Biederman2940bb62008-05-08 14:30:18 -0700196 goto out_unlock;
Johannes Berg55682962007-09-20 13:09:35 -0400197
Johannes Berg33c03602008-10-08 10:23:48 +0200198 if (rdev->wiphy.debugfsdir &&
199 !debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
Johannes Berg55682962007-09-20 13:09:35 -0400200 rdev->wiphy.debugfsdir,
201 rdev->wiphy.debugfsdir->d_parent,
202 newname))
203 printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n",
204 newname);
205
Eric W. Biederman2940bb62008-05-08 14:30:18 -0700206 result = 0;
207out_unlock:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500208 mutex_unlock(&cfg80211_mutex);
Eric W. Biederman2940bb62008-05-08 14:30:18 -0700209 if (result == 0)
210 nl80211_notify_dev_rename(rdev);
Johannes Berg55682962007-09-20 13:09:35 -0400211
Eric W. Biederman2940bb62008-05-08 14:30:18 -0700212 return result;
Johannes Berg55682962007-09-20 13:09:35 -0400213}
214
Johannes Berg704232c2007-04-23 12:20:05 -0700215/* exported functions */
216
217struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
218{
Denis ChengRq638af072008-09-23 02:35:37 +0800219 static int wiphy_counter;
220
Johannes Berg704232c2007-04-23 12:20:05 -0700221 struct cfg80211_registered_device *drv;
222 int alloc_size;
223
Johannes Berg41ade002007-12-19 02:03:29 +0100224 WARN_ON(!ops->add_key && ops->del_key);
225 WARN_ON(ops->add_key && !ops->del_key);
226
Johannes Berg704232c2007-04-23 12:20:05 -0700227 alloc_size = sizeof(*drv) + sizeof_priv;
228
229 drv = kzalloc(alloc_size, GFP_KERNEL);
230 if (!drv)
231 return NULL;
232
233 drv->ops = ops;
234
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500235 mutex_lock(&cfg80211_mutex);
Johannes Berg704232c2007-04-23 12:20:05 -0700236
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500237 drv->wiphy_idx = wiphy_counter++;
Johannes Berga4d73ee2007-04-26 20:50:35 -0700238
Luis R. Rodriguez85fd1292009-02-21 00:04:20 -0500239 if (unlikely(!wiphy_idx_valid(drv->wiphy_idx))) {
Denis ChengRq638af072008-09-23 02:35:37 +0800240 wiphy_counter--;
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500241 mutex_unlock(&cfg80211_mutex);
Johannes Berg704232c2007-04-23 12:20:05 -0700242 /* ugh, wrapped! */
243 kfree(drv);
244 return NULL;
245 }
Johannes Berg704232c2007-04-23 12:20:05 -0700246
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500247 mutex_unlock(&cfg80211_mutex);
Denis ChengRq638af072008-09-23 02:35:37 +0800248
Johannes Berg704232c2007-04-23 12:20:05 -0700249 /* give it a proper name */
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500250 dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->wiphy_idx);
Johannes Berg704232c2007-04-23 12:20:05 -0700251
Johannes Berg704232c2007-04-23 12:20:05 -0700252 mutex_init(&drv->mtx);
253 mutex_init(&drv->devlist_mtx);
254 INIT_LIST_HEAD(&drv->netdev_list);
Johannes Berg2a519312009-02-10 21:25:55 +0100255 spin_lock_init(&drv->bss_lock);
256 INIT_LIST_HEAD(&drv->bss_list);
Johannes Berg704232c2007-04-23 12:20:05 -0700257
258 device_initialize(&drv->wiphy.dev);
259 drv->wiphy.dev.class = &ieee80211_class;
260 drv->wiphy.dev.platform_data = drv;
261
262 return &drv->wiphy;
263}
264EXPORT_SYMBOL(wiphy_new);
265
266int wiphy_register(struct wiphy *wiphy)
267{
268 struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
269 int res;
Johannes Berg8318d782008-01-24 19:38:38 +0100270 enum ieee80211_band band;
271 struct ieee80211_supported_band *sband;
272 bool have_band = false;
273 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700274 u16 ifmodes = wiphy->interface_modes;
275
Johannes Berg2a519312009-02-10 21:25:55 +0100276 if (WARN_ON(wiphy->max_scan_ssids < 1))
277 return -EINVAL;
278
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700279 /* sanity check ifmodes */
280 WARN_ON(!ifmodes);
281 ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1;
282 if (WARN_ON(ifmodes != wiphy->interface_modes))
283 wiphy->interface_modes = ifmodes;
Johannes Berg8318d782008-01-24 19:38:38 +0100284
285 /* sanity check supported bands/channels */
286 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
287 sband = wiphy->bands[band];
288 if (!sband)
289 continue;
290
291 sband->band = band;
292
Johannes Berg881d9482009-01-21 15:13:48 +0100293 if (WARN_ON(!sband->n_channels || !sband->n_bitrates))
Johannes Berg8318d782008-01-24 19:38:38 +0100294 return -EINVAL;
Johannes Berg881d9482009-01-21 15:13:48 +0100295
296 /*
297 * Since we use a u32 for rate bitmaps in
298 * ieee80211_get_response_rate, we cannot
299 * have more than 32 legacy rates.
300 */
301 if (WARN_ON(sband->n_bitrates > 32))
302 return -EINVAL;
Johannes Berg8318d782008-01-24 19:38:38 +0100303
304 for (i = 0; i < sband->n_channels; i++) {
305 sband->channels[i].orig_flags =
306 sband->channels[i].flags;
307 sband->channels[i].orig_mag =
308 sband->channels[i].max_antenna_gain;
309 sband->channels[i].orig_mpwr =
310 sband->channels[i].max_power;
311 sband->channels[i].band = band;
312 }
313
314 have_band = true;
315 }
316
317 if (!have_band) {
318 WARN_ON(1);
319 return -EINVAL;
320 }
321
322 /* check and set up bitrates */
323 ieee80211_set_bitrate_flags(wiphy);
324
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500325 mutex_lock(&cfg80211_mutex);
Johannes Berg704232c2007-04-23 12:20:05 -0700326
Johannes Bergf3b407f2008-10-21 09:57:41 +0200327 /* set up regulatory info */
328 wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE);
329
Johannes Berg704232c2007-04-23 12:20:05 -0700330 res = device_add(&drv->wiphy.dev);
331 if (res)
332 goto out_unlock;
333
334 list_add(&drv->list, &cfg80211_drv_list);
335
336 /* add to debugfs */
337 drv->wiphy.debugfsdir =
338 debugfs_create_dir(wiphy_name(&drv->wiphy),
339 ieee80211_debugfs_dir);
Johannes Berg33c03602008-10-08 10:23:48 +0200340 if (IS_ERR(drv->wiphy.debugfsdir))
341 drv->wiphy.debugfsdir = NULL;
Johannes Berg704232c2007-04-23 12:20:05 -0700342
343 res = 0;
344out_unlock:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500345 mutex_unlock(&cfg80211_mutex);
Johannes Berg704232c2007-04-23 12:20:05 -0700346 return res;
347}
348EXPORT_SYMBOL(wiphy_register);
349
350void wiphy_unregister(struct wiphy *wiphy)
351{
352 struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
353
Johannes Bergf16bfc12007-04-26 20:51:12 -0700354 /* protect the device list */
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500355 mutex_lock(&cfg80211_mutex);
Johannes Berg704232c2007-04-23 12:20:05 -0700356
Johannes Bergf16bfc12007-04-26 20:51:12 -0700357 BUG_ON(!list_empty(&drv->netdev_list));
358
359 /*
360 * Try to grab drv->mtx. If a command is still in progress,
361 * hopefully the driver will refuse it since it's tearing
362 * down the device already. We wait for this command to complete
363 * before unlinking the item from the list.
364 * Note: as codified by the BUG_ON above we cannot get here if
365 * a virtual interface is still associated. Hence, we can only
366 * get to lock contention here if userspace issues a command
367 * that identified the hardware by wiphy index.
368 */
Johannes Berg704232c2007-04-23 12:20:05 -0700369 mutex_lock(&drv->mtx);
Johannes Bergf16bfc12007-04-26 20:51:12 -0700370 /* unlock again before freeing */
Johannes Berg704232c2007-04-23 12:20:05 -0700371 mutex_unlock(&drv->mtx);
372
Luis R. Rodriguez3f2355c2008-11-12 14:22:02 -0800373 /* If this device got a regulatory hint tell core its
374 * free to listen now to a new shiny device regulatory hint */
375 reg_device_remove(wiphy);
376
Johannes Bergf16bfc12007-04-26 20:51:12 -0700377 list_del(&drv->list);
Johannes Berg704232c2007-04-23 12:20:05 -0700378 device_del(&drv->wiphy.dev);
379 debugfs_remove(drv->wiphy.debugfsdir);
380
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500381 mutex_unlock(&cfg80211_mutex);
Johannes Berg704232c2007-04-23 12:20:05 -0700382}
383EXPORT_SYMBOL(wiphy_unregister);
384
385void cfg80211_dev_free(struct cfg80211_registered_device *drv)
386{
Johannes Berg2a519312009-02-10 21:25:55 +0100387 struct cfg80211_internal_bss *scan, *tmp;
Johannes Berg704232c2007-04-23 12:20:05 -0700388 mutex_destroy(&drv->mtx);
389 mutex_destroy(&drv->devlist_mtx);
Johannes Berg2a519312009-02-10 21:25:55 +0100390 list_for_each_entry_safe(scan, tmp, &drv->bss_list, list)
Johannes Berg78c1c7e2009-02-10 21:25:57 +0100391 cfg80211_put_bss(&scan->pub);
Johannes Berg704232c2007-04-23 12:20:05 -0700392 kfree(drv);
393}
394
395void wiphy_free(struct wiphy *wiphy)
396{
397 put_device(&wiphy->dev);
398}
399EXPORT_SYMBOL(wiphy_free);
400
401static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
402 unsigned long state,
403 void *ndev)
404{
405 struct net_device *dev = ndev;
406 struct cfg80211_registered_device *rdev;
407
408 if (!dev->ieee80211_ptr)
409 return 0;
410
411 rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
412
Johannes Berg60719ff2008-09-16 14:55:09 +0200413 WARN_ON(dev->ieee80211_ptr->iftype == NL80211_IFTYPE_UNSPECIFIED);
414
Johannes Berg704232c2007-04-23 12:20:05 -0700415 switch (state) {
416 case NETDEV_REGISTER:
417 mutex_lock(&rdev->devlist_mtx);
418 list_add(&dev->ieee80211_ptr->list, &rdev->netdev_list);
419 if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
420 "phy80211")) {
421 printk(KERN_ERR "wireless: failed to add phy80211 "
422 "symlink to netdev!\n");
423 }
424 dev->ieee80211_ptr->netdev = dev;
425 mutex_unlock(&rdev->devlist_mtx);
426 break;
427 case NETDEV_UNREGISTER:
428 mutex_lock(&rdev->devlist_mtx);
429 if (!list_empty(&dev->ieee80211_ptr->list)) {
430 sysfs_remove_link(&dev->dev.kobj, "phy80211");
431 list_del_init(&dev->ieee80211_ptr->list);
432 }
433 mutex_unlock(&rdev->devlist_mtx);
434 break;
435 }
436
437 return 0;
438}
439
440static struct notifier_block cfg80211_netdev_notifier = {
441 .notifier_call = cfg80211_netdev_notifier_call,
442};
443
444static int cfg80211_init(void)
445{
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700446 int err;
447
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700448 err = wiphy_sysfs_init();
Johannes Berg704232c2007-04-23 12:20:05 -0700449 if (err)
450 goto out_fail_sysfs;
451
452 err = register_netdevice_notifier(&cfg80211_netdev_notifier);
453 if (err)
454 goto out_fail_notifier;
455
Johannes Berg55682962007-09-20 13:09:35 -0400456 err = nl80211_init();
457 if (err)
458 goto out_fail_nl80211;
459
Johannes Berg704232c2007-04-23 12:20:05 -0700460 ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
461
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700462 err = regulatory_init();
463 if (err)
464 goto out_fail_reg;
465
Johannes Berg704232c2007-04-23 12:20:05 -0700466 return 0;
467
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700468out_fail_reg:
469 debugfs_remove(ieee80211_debugfs_dir);
Johannes Berg55682962007-09-20 13:09:35 -0400470out_fail_nl80211:
471 unregister_netdevice_notifier(&cfg80211_netdev_notifier);
Johannes Berg704232c2007-04-23 12:20:05 -0700472out_fail_notifier:
473 wiphy_sysfs_exit();
474out_fail_sysfs:
475 return err;
476}
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700477
Johannes Berg3a462462007-09-10 13:44:45 +0200478subsys_initcall(cfg80211_init);
Johannes Berg704232c2007-04-23 12:20:05 -0700479
480static void cfg80211_exit(void)
481{
482 debugfs_remove(ieee80211_debugfs_dir);
Johannes Berg55682962007-09-20 13:09:35 -0400483 nl80211_exit();
Johannes Berg704232c2007-04-23 12:20:05 -0700484 unregister_netdevice_notifier(&cfg80211_netdev_notifier);
485 wiphy_sysfs_exit();
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700486 regulatory_exit();
Johannes Berg704232c2007-04-23 12:20:05 -0700487}
488module_exit(cfg80211_exit);