cfg80211: clean up regulatory mess
[safe/jmp/linux-2.6] / net / wireless / core.c
index febc33b..88cb733 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This is the linux wireless configuration interface.
  *
- * Copyright 2006, 2007                Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006-2008         Johannes Berg <johannes@sipsolutions.net>
  */
 
 #include <linux/if.h>
 #include <linux/debugfs.h>
 #include <linux/notifier.h>
 #include <linux/device.h>
+#include <linux/list.h>
 #include <net/genetlink.h>
 #include <net/cfg80211.h>
 #include <net/wireless.h>
 #include "nl80211.h"
 #include "core.h"
 #include "sysfs.h"
+#include "reg.h"
 
 /* name for sysfs, %d is appended */
 #define PHY_NAME "phy"
@@ -143,8 +145,11 @@ void cfg80211_put_dev(struct cfg80211_registered_device *drv)
 int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                        char *newname)
 {
+       struct cfg80211_registered_device *drv;
        int idx, taken = -1, result, digits;
 
+       mutex_lock(&cfg80211_drv_mutex);
+
        /* 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) {
@@ -156,14 +161,30 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                 * deny the name if it is phy<idx> where <idx> is printed
                 * without leading zeroes. taken == strlen(newname) here
                 */
+               result = -EINVAL;
                if (taken == strlen(PHY_NAME) + digits)
-                       return -EINVAL;
+                       goto out_unlock;
        }
 
-       /* this will check for collisions */
+
+       /* Ignore nop renames */
+       result = 0;
+       if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0)
+               goto out_unlock;
+
+       /* Ensure another device does not already have this name. */
+       list_for_each_entry(drv, &cfg80211_drv_list, list) {
+               result = -EINVAL;
+               if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0)
+                       goto out_unlock;
+       }
+
+       /* this will only check for collisions in sysfs
+        * which is not even always compiled in.
+        */
        result = device_rename(&rdev->wiphy.dev, newname);
        if (result)
-               return result;
+               goto out_unlock;
 
        if (!debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
                            rdev->wiphy.debugfsdir,
@@ -172,9 +193,13 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n",
                       newname);
 
-       nl80211_notify_dev_rename(rdev);
+       result = 0;
+out_unlock:
+       mutex_unlock(&cfg80211_drv_mutex);
+       if (result == 0)
+               nl80211_notify_dev_rename(rdev);
 
-       return 0;
+       return result;
 }
 
 /* exported functions */
@@ -184,6 +209,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);
@@ -229,6 +257,56 @@ 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;
+       u16 ifmodes = wiphy->interface_modes;
+
+       /* sanity check ifmodes */
+       WARN_ON(!ifmodes);
+       ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1;
+       if (WARN_ON(ifmodes != wiphy->interface_modes))
+               wiphy->interface_modes = ifmodes;
+
+       /* 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 */
+       mutex_lock(&cfg80211_reg_mutex);
+       wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE);
+       mutex_unlock(&cfg80211_reg_mutex);
 
        mutex_lock(&cfg80211_drv_mutex);
 
@@ -337,7 +415,9 @@ static struct notifier_block cfg80211_netdev_notifier = {
 
 static int cfg80211_init(void)
 {
-       int err = wiphy_sysfs_init();
+       int err;
+
+       err = wiphy_sysfs_init();
        if (err)
                goto out_fail_sysfs;
 
@@ -351,8 +431,14 @@ static int cfg80211_init(void)
 
        ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
 
+       err = regulatory_init();
+       if (err)
+               goto out_fail_reg;
+
        return 0;
 
+out_fail_reg:
+       debugfs_remove(ieee80211_debugfs_dir);
 out_fail_nl80211:
        unregister_netdevice_notifier(&cfg80211_netdev_notifier);
 out_fail_notifier:
@@ -360,6 +446,7 @@ out_fail_notifier:
 out_fail_sysfs:
        return err;
 }
+
 subsys_initcall(cfg80211_init);
 
 static void cfg80211_exit(void)
@@ -368,5 +455,6 @@ static void cfg80211_exit(void)
        nl80211_exit();
        unregister_netdevice_notifier(&cfg80211_netdev_notifier);
        wiphy_sysfs_exit();
+       regulatory_exit();
 }
 module_exit(cfg80211_exit);