Merge branch 'batman-adv/maint' of git://git.open-mesh.org/linux-merge
[pandora-kernel.git] / net / wireless / reg.c
index 68a471b..3302c56 100644 (file)
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/kernel.h>
+#include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/list.h>
 #include <linux/random.h>
 #include <linux/ctype.h>
 #include <linux/nl80211.h>
 #include <linux/platform_device.h>
+#include <linux/moduleparam.h>
 #include <net/cfg80211.h>
 #include "core.h"
 #include "reg.h"
 #include "nl80211.h"
 
 #ifdef CONFIG_CFG80211_REG_DEBUG
-#define REG_DBG_PRINT(format, args...) \
-       do { \
-               printk(KERN_DEBUG pr_fmt(format), ##args);      \
-       } while (0)
+#define REG_DBG_PRINT(format, args...)                 \
+       printk(KERN_DEBUG pr_fmt(format), ##args)
 #else
 #define REG_DBG_PRINT(args...)
 #endif
 
+static struct regulatory_request core_request_world = {
+       .initiator = NL80211_REGDOM_SET_BY_CORE,
+       .alpha2[0] = '0',
+       .alpha2[1] = '0',
+       .intersect = false,
+       .processed = true,
+       .country_ie_env = ENVIRON_ANY,
+};
+
 /* Receipt of information from last regulatory request */
-static struct regulatory_request *last_request;
+static struct regulatory_request *last_request = &core_request_world;
 
 /* To trigger userspace events */
 static struct platform_device *reg_pdev;
@@ -150,7 +159,7 @@ static char user_alpha2[2];
 module_param(ieee80211_regdom, charp, 0444);
 MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
 
-static void reset_regdomains(void)
+static void reset_regdomains(bool full_reset)
 {
        /* avoid freeing static information or freeing something twice */
        if (cfg80211_regdomain == cfg80211_world_regdom)
@@ -165,6 +174,13 @@ static void reset_regdomains(void)
 
        cfg80211_world_regdom = &world_regdom;
        cfg80211_regdomain = NULL;
+
+       if (!full_reset)
+               return;
+
+       if (last_request != &core_request_world)
+               kfree(last_request);
+       last_request = &core_request_world;
 }
 
 /*
@@ -175,7 +191,7 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd)
 {
        BUG_ON(!last_request);
 
-       reset_regdomains();
+       reset_regdomains(false);
 
        cfg80211_world_regdom = rd;
        cfg80211_regdomain = rd;
@@ -753,9 +769,10 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
                      chan->center_freq,
                      KHZ_TO_MHZ(desired_bw_khz));
 
-       REG_DBG_PRINT("%d KHz - %d KHz @  KHz), (%s mBi, %d mBm)\n",
+       REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n",
                      freq_range->start_freq_khz,
                      freq_range->end_freq_khz,
+                     freq_range->max_bandwidth_khz,
                      max_antenna_gain,
                      power_rule->max_eirp);
 }
@@ -891,7 +908,7 @@ static bool ignore_reg_update(struct wiphy *wiphy,
            wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
                REG_DBG_PRINT("Ignoring regulatory request %s "
                              "since the driver uses its own custom "
-                             "regulatory domain ",
+                             "regulatory domain\n",
                              reg_initiator_name(initiator));
                return true;
        }
@@ -905,7 +922,7 @@ static bool ignore_reg_update(struct wiphy *wiphy,
            !is_world_regdom(last_request->alpha2)) {
                REG_DBG_PRINT("Ignoring regulatory request %s "
                              "since the driver requires its own regulatory "
-                             "domain to be set first",
+                             "domain to be set first\n",
                              reg_initiator_name(initiator));
                return true;
        }
@@ -913,14 +930,6 @@ static bool ignore_reg_update(struct wiphy *wiphy,
        return false;
 }
 
-static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
-{
-       struct cfg80211_registered_device *rdev;
-
-       list_for_each_entry(rdev, &cfg80211_rdev_list, list)
-               wiphy_update_regulatory(&rdev->wiphy, initiator);
-}
-
 static void handle_reg_beacon(struct wiphy *wiphy,
                              unsigned int chan_idx,
                              struct reg_beacon *reg_beacon)
@@ -1120,11 +1129,13 @@ static void reg_process_ht_flags(struct wiphy *wiphy)
 
 }
 
-void wiphy_update_regulatory(struct wiphy *wiphy,
-                            enum nl80211_reg_initiator initiator)
+static void wiphy_update_regulatory(struct wiphy *wiphy,
+                                   enum nl80211_reg_initiator initiator)
 {
        enum ieee80211_band band;
 
+       assert_reg_lock();
+
        if (ignore_reg_update(wiphy, initiator))
                return;
 
@@ -1139,6 +1150,22 @@ void wiphy_update_regulatory(struct wiphy *wiphy,
                wiphy->reg_notifier(wiphy, last_request);
 }
 
+void regulatory_update(struct wiphy *wiphy,
+                      enum nl80211_reg_initiator setby)
+{
+       mutex_lock(&reg_mutex);
+       wiphy_update_regulatory(wiphy, setby);
+       mutex_unlock(&reg_mutex);
+}
+
+static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
+{
+       struct cfg80211_registered_device *rdev;
+
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list)
+               wiphy_update_regulatory(&rdev->wiphy, initiator);
+}
+
 static void handle_channel_custom(struct wiphy *wiphy,
                                  enum ieee80211_band band,
                                  unsigned int chan_idx,
@@ -1396,7 +1423,8 @@ static int __regulatory_hint(struct wiphy *wiphy,
        }
 
 new_request:
-       kfree(last_request);
+       if (last_request != &core_request_world)
+               kfree(last_request);
 
        last_request = pending_request;
        last_request->intersect = intersect;
@@ -1475,7 +1503,7 @@ static void reg_process_pending_hints(void)
        /* When last_request->processed becomes true this will be rescheduled */
        if (last_request && !last_request->processed) {
                REG_DBG_PRINT("Pending regulatory request, waiting "
-                             "for it to be processed...");
+                             "for it to be processed...\n");
                goto out;
        }
 
@@ -1566,9 +1594,6 @@ static int regulatory_hint_core(const char *alpha2)
 {
        struct regulatory_request *request;
 
-       kfree(last_request);
-       last_request = NULL;
-
        request = kzalloc(sizeof(struct regulatory_request),
                          GFP_KERNEL);
        if (!request)
@@ -1766,7 +1791,7 @@ static void restore_regulatory_settings(bool reset_user)
        mutex_lock(&cfg80211_mutex);
        mutex_lock(&reg_mutex);
 
-       reset_regdomains();
+       reset_regdomains(true);
        restore_alpha2(alpha2, reset_user);
 
        /*
@@ -2026,12 +2051,18 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
        }
 
        request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+       if (!request_wiphy &&
+           (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
+            last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
+               schedule_delayed_work(&reg_timeout, 0);
+               return -ENODEV;
+       }
 
        if (!last_request->intersect) {
                int r;
 
                if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
-                       reset_regdomains();
+                       reset_regdomains(false);
                        cfg80211_regdomain = rd;
                        return 0;
                }
@@ -2052,7 +2083,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
                if (r)
                        return r;
 
-               reset_regdomains();
+               reset_regdomains(false);
                cfg80211_regdomain = rd;
                return 0;
        }
@@ -2077,7 +2108,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
 
                rd = NULL;
 
-               reset_regdomains();
+               reset_regdomains(false);
                cfg80211_regdomain = intersected_rd;
 
                return 0;
@@ -2097,7 +2128,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
        kfree(rd);
        rd = NULL;
 
-       reset_regdomains();
+       reset_regdomains(false);
        cfg80211_regdomain = intersected_rd;
 
        return 0;
@@ -2188,7 +2219,7 @@ out:
 static void reg_timeout_work(struct work_struct *work)
 {
        REG_DBG_PRINT("Timeout while waiting for CRDA to reply, "
-                     "restoring regulatory settings");
+                     "restoring regulatory settings\n");
        restore_regulatory_settings(true);
 }
 
@@ -2250,9 +2281,9 @@ void /* __init_or_exit */ regulatory_exit(void)
        mutex_lock(&cfg80211_mutex);
        mutex_lock(&reg_mutex);
 
-       reset_regdomains();
+       reset_regdomains(true);
 
-       kfree(last_request);
+       dev_set_uevent_suppress(&reg_pdev->dev, true);
 
        platform_device_unregister(reg_pdev);