summaryrefslogtreecommitdiff
path: root/net/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/Kconfig3
-rw-r--r--net/wireless/Makefile4
-rw-r--r--net/wireless/core.c209
-rw-r--r--net/wireless/core.h49
-rw-r--r--net/wireless/sysfs.c80
-rw-r--r--net/wireless/sysfs.h9
6 files changed, 353 insertions, 1 deletions
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index ca2f05c29760..1863c0b07d45 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -1,3 +1,6 @@
+config CFG80211
+ tristate "Improved wireless configuration API"
+
config WIRELESS_EXT
bool "Wireless extensions"
default n
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index cf4e3d9726b8..3f082ffae387 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -1 +1,3 @@
-# dummy file for now
+obj-$(CONFIG_CFG80211) += cfg80211.o
+
+cfg80211-y += core.o sysfs.o
diff --git a/net/wireless/core.c b/net/wireless/core.c
new file mode 100644
index 000000000000..532e1e09e028
--- /dev/null
+++ b/net/wireless/core.c
@@ -0,0 +1,209 @@
+/*
+ * 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);
diff --git a/net/wireless/core.h b/net/wireless/core.h
new file mode 100644
index 000000000000..158db1edb92a
--- /dev/null
+++ b/net/wireless/core.h
@@ -0,0 +1,49 @@
+/*
+ * Wireless configuration interface internals.
+ *
+ * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
+ */
+#ifndef __NET_WIRELESS_CORE_H
+#define __NET_WIRELESS_CORE_H
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+#include <net/wireless.h>
+#include <net/cfg80211.h>
+
+struct cfg80211_registered_device {
+ struct cfg80211_ops *ops;
+ struct list_head list;
+ /* we hold this mutex during any call so that
+ * we cannot do multiple calls at once, and also
+ * to avoid the deregister call to proceed while
+ * any call is in progress */
+ struct mutex mtx;
+
+ /* wiphy index, internal only */
+ int idx;
+
+ /* associate netdev list */
+ struct mutex devlist_mtx;
+ struct list_head netdev_list;
+
+ /* must be last because of the way we do wiphy_priv(),
+ * and it should at least be aligned to NETDEV_ALIGN */
+ struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
+};
+
+static inline
+struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
+{
+ BUG_ON(!wiphy);
+ return container_of(wiphy, struct cfg80211_registered_device, wiphy);
+}
+
+extern struct mutex cfg80211_drv_mutex;
+extern struct list_head cfg80211_drv_list;
+
+/* free object */
+extern void cfg80211_dev_free(struct cfg80211_registered_device *drv);
+
+#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
new file mode 100644
index 000000000000..3ebae1442963
--- /dev/null
+++ b/net/wireless/sysfs.c
@@ -0,0 +1,80 @@
+/*
+ * This file provides /sys/class/ieee80211/<wiphy name>/
+ * and some default attributes.
+ *
+ * Copyright 2005-2006 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This file is GPLv2 as found in COPYING.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/nl80211.h>
+#include <linux/rtnetlink.h>
+#include <net/cfg80211.h>
+#include "sysfs.h"
+#include "core.h"
+
+static inline struct cfg80211_registered_device *dev_to_rdev(
+ struct device *dev)
+{
+ return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
+}
+
+static ssize_t _show_index(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", dev_to_rdev(dev)->idx);
+}
+
+static ssize_t _show_permaddr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ char *addr = dev_to_rdev(dev)->wiphy.perm_addr;
+
+ return sprintf(buf, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+
+static struct device_attribute ieee80211_dev_attrs[] = {
+ __ATTR(index, S_IRUGO, _show_index, NULL),
+ __ATTR(macaddress, S_IRUGO, _show_permaddr, NULL),
+ {}
+};
+
+static void wiphy_dev_release(struct device *dev)
+{
+ struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
+
+ cfg80211_dev_free(rdev);
+}
+
+static int wiphy_uevent(struct device *dev, char **envp,
+ int num_envp, char *buf, int size)
+{
+ /* TODO, we probably need stuff here */
+ return 0;
+}
+
+struct class ieee80211_class = {
+ .name = "ieee80211",
+ .owner = THIS_MODULE,
+ .dev_release = wiphy_dev_release,
+ .dev_attrs = ieee80211_dev_attrs,
+#ifdef CONFIG_HOTPLUG
+ .dev_uevent = wiphy_uevent,
+#endif
+};
+
+int wiphy_sysfs_init(void)
+{
+ return class_register(&ieee80211_class);
+}
+
+void wiphy_sysfs_exit(void)
+{
+ class_unregister(&ieee80211_class);
+}
diff --git a/net/wireless/sysfs.h b/net/wireless/sysfs.h
new file mode 100644
index 000000000000..65acbebd3711
--- /dev/null
+++ b/net/wireless/sysfs.h
@@ -0,0 +1,9 @@
+#ifndef __WIRELESS_SYSFS_H
+#define __WIRELESS_SYSFS_H
+
+extern int wiphy_sysfs_init(void);
+extern void wiphy_sysfs_exit(void);
+
+extern struct class ieee80211_class;
+
+#endif /* __WIRELESS_SYSFS_H */