RxRPC: Error handling for rxrpc_alloc_connection()
[safe/jmp/linux-2.6] / net / wireless / core.c
index 5031db7..d1f5565 100644 (file)
@@ -7,7 +7,6 @@
 #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>
@@ -19,7 +18,6 @@
 #include "nl80211.h"
 #include "core.h"
 #include "sysfs.h"
-#include "reg.h"
 
 /* name for sysfs, %d is appended */
 #define PHY_NAME "phy"
@@ -32,18 +30,29 @@ MODULE_DESCRIPTION("wireless configuration support");
  * 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);
+
+/*
+ * This is used to protect the cfg80211_drv_list, cfg80211_regdomain,
+ * country_ie_regdomain, the reg_beacon_list and the the last regulatory
+ * request receipt (last_request).
+ */
+DEFINE_MUTEX(cfg80211_mutex);
 
 /* 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)
+/* requires cfg80211_mutex to be held! */
+struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx)
 {
        struct cfg80211_registered_device *result = NULL, *drv;
 
+       if (!wiphy_idx_valid(wiphy_idx))
+               return NULL;
+
+       assert_cfg80211_lock();
+
        list_for_each_entry(drv, &cfg80211_drv_list, list) {
-               if (drv->idx == wiphy) {
+               if (drv->wiphy_idx == wiphy_idx) {
                        result = drv;
                        break;
                }
@@ -52,17 +61,44 @@ static struct cfg80211_registered_device *cfg80211_drv_by_wiphy(int wiphy)
        return result;
 }
 
+int get_wiphy_idx(struct wiphy *wiphy)
+{
+       struct cfg80211_registered_device *drv;
+       if (!wiphy)
+               return WIPHY_IDX_STALE;
+       drv = wiphy_to_dev(wiphy);
+       return drv->wiphy_idx;
+}
+
 /* requires cfg80211_drv_mutex to be held! */
-static struct cfg80211_registered_device *
+struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
+{
+       struct cfg80211_registered_device *drv;
+
+       if (!wiphy_idx_valid(wiphy_idx))
+               return NULL;
+
+       assert_cfg80211_lock();
+
+       drv = cfg80211_drv_by_wiphy_idx(wiphy_idx);
+       if (!drv)
+               return NULL;
+       return &drv->wiphy;
+}
+
+/* requires cfg80211_mutex to be held! */
+struct cfg80211_registered_device *
 __cfg80211_drv_from_info(struct genl_info *info)
 {
        int ifindex;
-       struct cfg80211_registered_device *bywiphy = NULL, *byifidx = NULL;
+       struct cfg80211_registered_device *bywiphyidx = NULL, *byifidx = NULL;
        struct net_device *dev;
        int err = -EINVAL;
 
+       assert_cfg80211_lock();
+
        if (info->attrs[NL80211_ATTR_WIPHY]) {
-               bywiphy = cfg80211_drv_by_wiphy(
+               bywiphyidx = cfg80211_drv_by_wiphy_idx(
                                nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
                err = -ENODEV;
        }
@@ -79,14 +115,14 @@ __cfg80211_drv_from_info(struct genl_info *info)
                err = -ENODEV;
        }
 
-       if (bywiphy && byifidx) {
-               if (bywiphy != byifidx)
+       if (bywiphyidx && byifidx) {
+               if (bywiphyidx != byifidx)
                        return ERR_PTR(-EINVAL);
                else
-                       return bywiphy; /* == byifidx */
+                       return bywiphyidx; /* == byifidx */
        }
-       if (bywiphy)
-               return bywiphy;
+       if (bywiphyidx)
+               return bywiphyidx;
 
        if (byifidx)
                return byifidx;
@@ -99,7 +135,7 @@ cfg80211_get_dev_from_info(struct genl_info *info)
 {
        struct cfg80211_registered_device *drv;
 
-       mutex_lock(&cfg80211_drv_mutex);
+       mutex_lock(&cfg80211_mutex);
        drv = __cfg80211_drv_from_info(info);
 
        /* if it is not an error we grab the lock on
@@ -108,7 +144,7 @@ cfg80211_get_dev_from_info(struct genl_info *info)
        if (!IS_ERR(drv))
                mutex_lock(&drv->mtx);
 
-       mutex_unlock(&cfg80211_drv_mutex);
+       mutex_unlock(&cfg80211_mutex);
 
        return drv;
 }
@@ -119,7 +155,7 @@ cfg80211_get_dev_from_ifindex(int ifindex)
        struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV);
        struct net_device *dev;
 
-       mutex_lock(&cfg80211_drv_mutex);
+       mutex_lock(&cfg80211_mutex);
        dev = dev_get_by_index(&init_net, ifindex);
        if (!dev)
                goto out;
@@ -130,7 +166,7 @@ cfg80211_get_dev_from_ifindex(int ifindex)
                drv = ERR_PTR(-ENODEV);
        dev_put(dev);
  out:
-       mutex_unlock(&cfg80211_drv_mutex);
+       mutex_unlock(&cfg80211_mutex);
        return drv;
 }
 
@@ -140,49 +176,43 @@ void cfg80211_put_dev(struct cfg80211_registered_device *drv)
        mutex_unlock(&drv->mtx);
 }
 
+/* requires cfg80211_mutex to be held */
 int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                        char *newname)
 {
        struct cfg80211_registered_device *drv;
-       int idx, taken = -1, result, digits;
+       int wiphy_idx, taken = -1, result, digits;
 
-       mutex_lock(&cfg80211_drv_mutex);
+       assert_cfg80211_lock();
 
        /* 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 */
+       sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
+       if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) {
+               /* count number of places needed to print wiphy_idx */
                digits = 1;
-               while (idx /= 10)
+               while (wiphy_idx /= 10)
                        digits++;
                /*
                 * 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)
-                       goto out_unlock;
+                       return -EINVAL;
        }
 
 
        /* Ignore nop renames */
-       result = 0;
        if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0)
-               goto out_unlock;
+               return 0;
 
        /* Ensure another device does not already have this name. */
-       list_for_each_entry(drv, &cfg80211_drv_list, list) {
-               result = -EINVAL;
+       list_for_each_entry(drv, &cfg80211_drv_list, list)
                if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0)
-                       goto out_unlock;
-       }
+                       return -EINVAL;
 
-       /* this will only check for collisions in sysfs
-        * which is not even always compiled in.
-        */
        result = device_rename(&rdev->wiphy.dev, newname);
        if (result)
-               goto out_unlock;
+               return result;
 
        if (rdev->wiphy.debugfsdir &&
            !debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
@@ -192,13 +222,9 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n",
                       newname);
 
-       result = 0;
-out_unlock:
-       mutex_unlock(&cfg80211_drv_mutex);
-       if (result == 0)
-               nl80211_notify_dev_rename(rdev);
+       nl80211_notify_dev_rename(rdev);
 
-       return result;
+       return 0;
 }
 
 /* exported functions */
@@ -221,27 +247,28 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
 
        drv->ops = ops;
 
-       mutex_lock(&cfg80211_drv_mutex);
+       mutex_lock(&cfg80211_mutex);
 
-       drv->idx = wiphy_counter++;
+       drv->wiphy_idx = wiphy_counter++;
 
-       if (unlikely(drv->idx < 0)) {
+       if (unlikely(!wiphy_idx_valid(drv->wiphy_idx))) {
                wiphy_counter--;
-               mutex_unlock(&cfg80211_drv_mutex);
+               mutex_unlock(&cfg80211_mutex);
                /* ugh, wrapped! */
                kfree(drv);
                return NULL;
        }
 
-       mutex_unlock(&cfg80211_drv_mutex);
+       mutex_unlock(&cfg80211_mutex);
 
        /* give it a proper name */
-       snprintf(drv->wiphy.dev.bus_id, BUS_ID_SIZE,
-                PHY_NAME "%d", drv->idx);
+       dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->wiphy_idx);
 
        mutex_init(&drv->mtx);
        mutex_init(&drv->devlist_mtx);
        INIT_LIST_HEAD(&drv->netdev_list);
+       spin_lock_init(&drv->bss_lock);
+       INIT_LIST_HEAD(&drv->bss_list);
 
        device_initialize(&drv->wiphy.dev);
        drv->wiphy.dev.class = &ieee80211_class;
@@ -261,6 +288,9 @@ int wiphy_register(struct wiphy *wiphy)
        int i;
        u16 ifmodes = wiphy->interface_modes;
 
+       if (WARN_ON(wiphy->max_scan_ssids < 1))
+               return -EINVAL;
+
        /* sanity check ifmodes */
        WARN_ON(!ifmodes);
        ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1;
@@ -275,10 +305,16 @@ int wiphy_register(struct wiphy *wiphy)
 
                sband->band = band;
 
-               if (!sband->n_channels || !sband->n_bitrates) {
-                       WARN_ON(1);
+               if (WARN_ON(!sband->n_channels || !sband->n_bitrates))
+                       return -EINVAL;
+
+               /*
+                * Since we use a u32 for rate bitmaps in
+                * ieee80211_get_response_rate, we cannot
+                * have more than 32 legacy rates.
+                */
+               if (WARN_ON(sband->n_bitrates > 32))
                        return -EINVAL;
-               }
 
                for (i = 0; i < sband->n_channels; i++) {
                        sband->channels[i].orig_flags =
@@ -301,12 +337,10 @@ int wiphy_register(struct wiphy *wiphy)
        /* 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_mutex);
 
-       mutex_lock(&cfg80211_drv_mutex);
+       /* set up regulatory info */
+       wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE);
 
        res = device_add(&drv->wiphy.dev);
        if (res)
@@ -321,9 +355,20 @@ int wiphy_register(struct wiphy *wiphy)
        if (IS_ERR(drv->wiphy.debugfsdir))
                drv->wiphy.debugfsdir = NULL;
 
+       if (wiphy->custom_regulatory) {
+               struct regulatory_request request;
+
+               request.wiphy_idx = get_wiphy_idx(wiphy);
+               request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
+               request.alpha2[0] = '9';
+               request.alpha2[1] = '9';
+
+               nl80211_send_reg_change_event(&request);
+       }
+
        res = 0;
 out_unlock:
-       mutex_unlock(&cfg80211_drv_mutex);
+       mutex_unlock(&cfg80211_mutex);
        return res;
 }
 EXPORT_SYMBOL(wiphy_register);
@@ -333,7 +378,7 @@ void wiphy_unregister(struct wiphy *wiphy)
        struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
 
        /* protect the device list */
-       mutex_lock(&cfg80211_drv_mutex);
+       mutex_lock(&cfg80211_mutex);
 
        BUG_ON(!list_empty(&drv->netdev_list));
 
@@ -351,18 +396,25 @@ void wiphy_unregister(struct wiphy *wiphy)
        /* unlock again before freeing */
        mutex_unlock(&drv->mtx);
 
+       /* If this device got a regulatory hint tell core its
+        * free to listen now to a new shiny device regulatory hint */
+       reg_device_remove(wiphy);
+
        list_del(&drv->list);
        device_del(&drv->wiphy.dev);
        debugfs_remove(drv->wiphy.debugfsdir);
 
-       mutex_unlock(&cfg80211_drv_mutex);
+       mutex_unlock(&cfg80211_mutex);
 }
 EXPORT_SYMBOL(wiphy_unregister);
 
 void cfg80211_dev_free(struct cfg80211_registered_device *drv)
 {
+       struct cfg80211_internal_bss *scan, *tmp;
        mutex_destroy(&drv->mtx);
        mutex_destroy(&drv->devlist_mtx);
+       list_for_each_entry_safe(scan, tmp, &drv->bss_list, list)
+               cfg80211_put_bss(&scan->pub);
        kfree(drv);
 }