704232c271
This patch creates the core cfg80211 code along with some sysfs bits. This is a stripped down version to allow mac80211 to function, but doesn't include any configuration yet except for creating and removing virtual interfaces. This patch includes the nl80211 header file but it only contains the interface types which the cfg80211 interface for creating virtual interfaces relies on. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
210 lines
4.6 KiB
C
210 lines
4.6 KiB
C
/*
|
|
* This is the linux wireless configuration interface.
|
|
*
|
|
* Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
|
|
*/
|
|
|
|
#include <linux/if.h>
|
|
#include <linux/module.h>
|
|
#include <linux/err.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/list.h>
|
|
#include <linux/nl80211.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/device.h>
|
|
#include <net/genetlink.h>
|
|
#include <net/cfg80211.h>
|
|
#include <net/wireless.h>
|
|
#include "core.h"
|
|
#include "sysfs.h"
|
|
|
|
/* name for sysfs, %d is appended */
|
|
#define PHY_NAME "phy"
|
|
|
|
MODULE_AUTHOR("Johannes Berg");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("wireless configuration support");
|
|
|
|
/* RCU might be appropriate here since we usually
|
|
* only read the list, and that can happen quite
|
|
* often because we need to do it for each command */
|
|
LIST_HEAD(cfg80211_drv_list);
|
|
DEFINE_MUTEX(cfg80211_drv_mutex);
|
|
static int wiphy_counter;
|
|
|
|
/* for debugfs */
|
|
static struct dentry *ieee80211_debugfs_dir;
|
|
|
|
/* exported functions */
|
|
|
|
struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
|
|
{
|
|
struct cfg80211_registered_device *drv;
|
|
int alloc_size;
|
|
|
|
alloc_size = sizeof(*drv) + sizeof_priv;
|
|
|
|
drv = kzalloc(alloc_size, GFP_KERNEL);
|
|
if (!drv)
|
|
return NULL;
|
|
|
|
drv->ops = ops;
|
|
|
|
mutex_lock(&cfg80211_drv_mutex);
|
|
|
|
if (unlikely(wiphy_counter<0)) {
|
|
/* ugh, wrapped! */
|
|
kfree(drv);
|
|
return NULL;
|
|
}
|
|
drv->idx = wiphy_counter;
|
|
|
|
/* give it a proper name */
|
|
snprintf(drv->wiphy.dev.bus_id, BUS_ID_SIZE,
|
|
PHY_NAME "%d", drv->idx);
|
|
|
|
/* now increase counter for the next time */
|
|
wiphy_counter++;
|
|
mutex_unlock(&cfg80211_drv_mutex);
|
|
|
|
mutex_init(&drv->mtx);
|
|
mutex_init(&drv->devlist_mtx);
|
|
INIT_LIST_HEAD(&drv->netdev_list);
|
|
|
|
device_initialize(&drv->wiphy.dev);
|
|
drv->wiphy.dev.class = &ieee80211_class;
|
|
drv->wiphy.dev.platform_data = drv;
|
|
|
|
return &drv->wiphy;
|
|
}
|
|
EXPORT_SYMBOL(wiphy_new);
|
|
|
|
int wiphy_register(struct wiphy *wiphy)
|
|
{
|
|
struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
|
|
int res;
|
|
|
|
mutex_lock(&cfg80211_drv_mutex);
|
|
|
|
|
|
res = device_add(&drv->wiphy.dev);
|
|
if (res)
|
|
goto out_unlock;
|
|
|
|
list_add(&drv->list, &cfg80211_drv_list);
|
|
|
|
/* add to debugfs */
|
|
drv->wiphy.debugfsdir =
|
|
debugfs_create_dir(wiphy_name(&drv->wiphy),
|
|
ieee80211_debugfs_dir);
|
|
|
|
res = 0;
|
|
out_unlock:
|
|
mutex_unlock(&cfg80211_drv_mutex);
|
|
return res;
|
|
}
|
|
EXPORT_SYMBOL(wiphy_register);
|
|
|
|
void wiphy_unregister(struct wiphy *wiphy)
|
|
{
|
|
struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
|
|
|
|
mutex_lock(&cfg80211_drv_mutex);
|
|
|
|
/* hold registered driver mutex during list removal as well
|
|
* to make sure no commands are in progress at the moment */
|
|
mutex_lock(&drv->mtx);
|
|
list_del(&drv->list);
|
|
mutex_unlock(&drv->mtx);
|
|
|
|
device_del(&drv->wiphy.dev);
|
|
debugfs_remove(drv->wiphy.debugfsdir);
|
|
|
|
mutex_unlock(&cfg80211_drv_mutex);
|
|
}
|
|
EXPORT_SYMBOL(wiphy_unregister);
|
|
|
|
void cfg80211_dev_free(struct cfg80211_registered_device *drv)
|
|
{
|
|
mutex_destroy(&drv->mtx);
|
|
mutex_destroy(&drv->devlist_mtx);
|
|
kfree(drv);
|
|
}
|
|
|
|
void wiphy_free(struct wiphy *wiphy)
|
|
{
|
|
put_device(&wiphy->dev);
|
|
}
|
|
EXPORT_SYMBOL(wiphy_free);
|
|
|
|
static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
|
|
unsigned long state,
|
|
void *ndev)
|
|
{
|
|
struct net_device *dev = ndev;
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
if (!dev->ieee80211_ptr)
|
|
return 0;
|
|
|
|
rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
|
|
|
|
switch (state) {
|
|
case NETDEV_REGISTER:
|
|
mutex_lock(&rdev->devlist_mtx);
|
|
list_add(&dev->ieee80211_ptr->list, &rdev->netdev_list);
|
|
if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
|
|
"phy80211")) {
|
|
printk(KERN_ERR "wireless: failed to add phy80211 "
|
|
"symlink to netdev!\n");
|
|
}
|
|
dev->ieee80211_ptr->netdev = dev;
|
|
mutex_unlock(&rdev->devlist_mtx);
|
|
break;
|
|
case NETDEV_UNREGISTER:
|
|
mutex_lock(&rdev->devlist_mtx);
|
|
if (!list_empty(&dev->ieee80211_ptr->list)) {
|
|
sysfs_remove_link(&dev->dev.kobj, "phy80211");
|
|
list_del_init(&dev->ieee80211_ptr->list);
|
|
}
|
|
mutex_unlock(&rdev->devlist_mtx);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct notifier_block cfg80211_netdev_notifier = {
|
|
.notifier_call = cfg80211_netdev_notifier_call,
|
|
};
|
|
|
|
static int cfg80211_init(void)
|
|
{
|
|
int err = wiphy_sysfs_init();
|
|
if (err)
|
|
goto out_fail_sysfs;
|
|
|
|
err = register_netdevice_notifier(&cfg80211_netdev_notifier);
|
|
if (err)
|
|
goto out_fail_notifier;
|
|
|
|
ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
|
|
|
|
return 0;
|
|
|
|
out_fail_notifier:
|
|
wiphy_sysfs_exit();
|
|
out_fail_sysfs:
|
|
return err;
|
|
}
|
|
module_init(cfg80211_init);
|
|
|
|
static void cfg80211_exit(void)
|
|
{
|
|
debugfs_remove(ieee80211_debugfs_dir);
|
|
unregister_netdevice_notifier(&cfg80211_netdev_notifier);
|
|
wiphy_sysfs_exit();
|
|
}
|
|
module_exit(cfg80211_exit);
|