Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[pandora-kernel.git] / net / wireless / core.c
index 7eabd55..80afacd 100644 (file)
@@ -16,6 +16,7 @@
 #include <net/genetlink.h>
 #include <net/cfg80211.h>
 #include <net/wireless.h>
+#include "nl80211.h"
 #include "core.h"
 #include "sysfs.h"
 
@@ -36,6 +37,146 @@ static int wiphy_counter;
 /* for debugfs */
 static struct dentry *ieee80211_debugfs_dir;
 
+/* requires cfg80211_drv_mutex to be held! */
+static struct cfg80211_registered_device *cfg80211_drv_by_wiphy(int wiphy)
+{
+       struct cfg80211_registered_device *result = NULL, *drv;
+
+       list_for_each_entry(drv, &cfg80211_drv_list, list) {
+               if (drv->idx == wiphy) {
+                       result = drv;
+                       break;
+               }
+       }
+
+       return result;
+}
+
+/* requires cfg80211_drv_mutex to be held! */
+static struct cfg80211_registered_device *
+__cfg80211_drv_from_info(struct genl_info *info)
+{
+       int ifindex;
+       struct cfg80211_registered_device *bywiphy = NULL, *byifidx = NULL;
+       struct net_device *dev;
+       int err = -EINVAL;
+
+       if (info->attrs[NL80211_ATTR_WIPHY]) {
+               bywiphy = cfg80211_drv_by_wiphy(
+                               nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
+               err = -ENODEV;
+       }
+
+       if (info->attrs[NL80211_ATTR_IFINDEX]) {
+               ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
+               dev = dev_get_by_index(&init_net, ifindex);
+               if (dev) {
+                       if (dev->ieee80211_ptr)
+                               byifidx =
+                                       wiphy_to_dev(dev->ieee80211_ptr->wiphy);
+                       dev_put(dev);
+               }
+               err = -ENODEV;
+       }
+
+       if (bywiphy && byifidx) {
+               if (bywiphy != byifidx)
+                       return ERR_PTR(-EINVAL);
+               else
+                       return bywiphy; /* == byifidx */
+       }
+       if (bywiphy)
+               return bywiphy;
+
+       if (byifidx)
+               return byifidx;
+
+       return ERR_PTR(err);
+}
+
+struct cfg80211_registered_device *
+cfg80211_get_dev_from_info(struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+
+       mutex_lock(&cfg80211_drv_mutex);
+       drv = __cfg80211_drv_from_info(info);
+
+       /* if it is not an error we grab the lock on
+        * it to assure it won't be going away while
+        * we operate on it */
+       if (!IS_ERR(drv))
+               mutex_lock(&drv->mtx);
+
+       mutex_unlock(&cfg80211_drv_mutex);
+
+       return drv;
+}
+
+struct cfg80211_registered_device *
+cfg80211_get_dev_from_ifindex(int ifindex)
+{
+       struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV);
+       struct net_device *dev;
+
+       mutex_lock(&cfg80211_drv_mutex);
+       dev = dev_get_by_index(&init_net, ifindex);
+       if (!dev)
+               goto out;
+       if (dev->ieee80211_ptr) {
+               drv = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
+               mutex_lock(&drv->mtx);
+       } else
+               drv = ERR_PTR(-ENODEV);
+       dev_put(dev);
+ out:
+       mutex_unlock(&cfg80211_drv_mutex);
+       return drv;
+}
+
+void cfg80211_put_dev(struct cfg80211_registered_device *drv)
+{
+       BUG_ON(IS_ERR(drv));
+       mutex_unlock(&drv->mtx);
+}
+
+int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
+                       char *newname)
+{
+       int idx, taken = -1, result, digits;
+
+       /* prohibit calling the thing phy%d when %d is not its number */
+       sscanf(newname, PHY_NAME "%d%n", &idx, &taken);
+       if (taken == strlen(newname) && idx != rdev->idx) {
+               /* count number of places needed to print idx */
+               digits = 1;
+               while (idx /= 10)
+                       digits++;
+               /*
+                * deny the name if it is phy<idx> where <idx> is printed
+                * without leading zeroes. taken == strlen(newname) here
+                */
+               if (taken == strlen(PHY_NAME) + digits)
+                       return -EINVAL;
+       }
+
+       /* this will check for collisions */
+       result = device_rename(&rdev->wiphy.dev, newname);
+       if (result)
+               return result;
+
+       if (!debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
+                           rdev->wiphy.debugfsdir,
+                           rdev->wiphy.debugfsdir->d_parent,
+                           newname))
+               printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n",
+                      newname);
+
+       nl80211_notify_dev_rename(rdev);
+
+       return 0;
+}
+
 /* exported functions */
 
 struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
@@ -43,6 +184,9 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
        struct cfg80211_registered_device *drv;
        int alloc_size;
 
+       WARN_ON(!ops->add_key && ops->del_key);
+       WARN_ON(ops->add_key && !ops->del_key);
+
        alloc_size = sizeof(*drv) + sizeof_priv;
 
        drv = kzalloc(alloc_size, GFP_KERNEL);
@@ -88,6 +232,47 @@ int wiphy_register(struct wiphy *wiphy)
 {
        struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
        int res;
+       enum ieee80211_band band;
+       struct ieee80211_supported_band *sband;
+       bool have_band = false;
+       int i;
+
+       /* sanity check supported bands/channels */
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               sband = wiphy->bands[band];
+               if (!sband)
+                       continue;
+
+               sband->band = band;
+
+               if (!sband->n_channels || !sband->n_bitrates) {
+                       WARN_ON(1);
+                       return -EINVAL;
+               }
+
+               for (i = 0; i < sband->n_channels; i++) {
+                       sband->channels[i].orig_flags =
+                               sband->channels[i].flags;
+                       sband->channels[i].orig_mag =
+                               sband->channels[i].max_antenna_gain;
+                       sband->channels[i].orig_mpwr =
+                               sband->channels[i].max_power;
+                       sband->channels[i].band = band;
+               }
+
+               have_band = true;
+       }
+
+       if (!have_band) {
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       /* check and set up bitrates */
+       ieee80211_set_bitrate_flags(wiphy);
+
+       /* set up regulatory info */
+       wiphy_update_regulatory(wiphy);
 
        mutex_lock(&cfg80211_drv_mutex);
 
@@ -204,20 +389,27 @@ static int cfg80211_init(void)
        if (err)
                goto out_fail_notifier;
 
+       err = nl80211_init();
+       if (err)
+               goto out_fail_nl80211;
+
        ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
 
        return 0;
 
+out_fail_nl80211:
+       unregister_netdevice_notifier(&cfg80211_netdev_notifier);
 out_fail_notifier:
        wiphy_sysfs_exit();
 out_fail_sysfs:
        return err;
 }
-module_init(cfg80211_init);
+subsys_initcall(cfg80211_init);
 
 static void cfg80211_exit(void)
 {
        debugfs_remove(ieee80211_debugfs_dir);
+       nl80211_exit();
        unregister_netdevice_notifier(&cfg80211_netdev_notifier);
        wiphy_sysfs_exit();
 }