nfsd4: remove probe task's reference on client
[safe/jmp/linux-2.6] / net / wireless / reg.c
index f0859ea..ed89c59 100644 (file)
@@ -134,6 +134,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
        &world_regdom;
 
 static char *ieee80211_regdom = "00";
+static char user_alpha2[2];
 
 module_param(ieee80211_regdom, charp, 0444);
 MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
@@ -252,6 +253,27 @@ static bool regdom_changes(const char *alpha2)
        return true;
 }
 
+/*
+ * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets
+ * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER
+ * has ever been issued.
+ */
+static bool is_user_regdom_saved(void)
+{
+       if (user_alpha2[0] == '9' && user_alpha2[1] == '7')
+               return false;
+
+       /* This would indicate a mistake on the design */
+       if (WARN((!is_world_regdom(user_alpha2) &&
+                 !is_an_alpha2(user_alpha2)),
+                "Unexpected user alpha2: %c%c\n",
+                user_alpha2[0],
+                user_alpha2[1]))
+               return false;
+
+       return true;
+}
+
 /**
  * country_ie_integrity_changes - tells us if the country IE has changed
  * @checksum: checksum of country IE of fields we are interested in
@@ -485,6 +507,34 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
 }
 
 /*
+ * This is a work around for sanity checking ieee80211_channel_to_frequency()'s
+ * work. ieee80211_channel_to_frequency() can for example currently provide a
+ * 2 GHz channel when in fact a 5 GHz channel was desired. An example would be
+ * an AP providing channel 8 on a country IE triplet when it sent this on the
+ * 5 GHz band, that channel is designed to be channel 8 on 5 GHz, not a 2 GHz
+ * channel.
+ *
+ * This can be removed once ieee80211_channel_to_frequency() takes in a band.
+ */
+static bool chan_in_band(int chan, enum ieee80211_band band)
+{
+       int center_freq = ieee80211_channel_to_frequency(chan);
+
+       switch (band) {
+       case IEEE80211_BAND_2GHZ:
+               if (center_freq <= 2484)
+                       return true;
+               return false;
+       case IEEE80211_BAND_5GHZ:
+               if (center_freq >= 5005)
+                       return true;
+               return false;
+       default:
+               return false;
+       }
+}
+
+/*
  * Some APs may send a country IE triplet for each channel they
  * support and while this is completely overkill and silly we still
  * need to support it. We avoid making a single rule for each channel
@@ -532,7 +582,8 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
  * Returns 0 if the IE has been found to be invalid in the middle
  * somewhere.
  */
-static int max_subband_chan(int orig_cur_chan,
+static int max_subband_chan(enum ieee80211_band band,
+                           int orig_cur_chan,
                            int orig_end_channel,
                            s8 orig_max_power,
                            u8 **country_ie,
@@ -541,7 +592,6 @@ static int max_subband_chan(int orig_cur_chan,
        u8 *triplets_start = *country_ie;
        u8 len_at_triplet = *country_ie_len;
        int end_subband_chan = orig_end_channel;
-       enum ieee80211_band band;
 
        /*
         * We'll deal with padding for the caller unless
@@ -557,17 +607,14 @@ static int max_subband_chan(int orig_cur_chan,
        *country_ie += 3;
        *country_ie_len -= 3;
 
-       if (orig_cur_chan <= 14)
-               band = IEEE80211_BAND_2GHZ;
-       else
-               band = IEEE80211_BAND_5GHZ;
+       if (!chan_in_band(orig_cur_chan, band))
+               return 0;
 
        while (*country_ie_len >= 3) {
                int end_channel = 0;
                struct ieee80211_country_ie_triplet *triplet =
                        (struct ieee80211_country_ie_triplet *) *country_ie;
                int cur_channel = 0, next_expected_chan;
-               enum ieee80211_band next_band = IEEE80211_BAND_2GHZ;
 
                /* means last triplet is completely unrelated to this one */
                if (triplet->ext.reg_extension_id >=
@@ -585,10 +632,16 @@ static int max_subband_chan(int orig_cur_chan,
                        break;
                }
 
+               if (triplet->chans.num_channels == 0)
+                       return 0;
+
                /* Monitonically increasing channel order */
                if (triplet->chans.first_channel <= end_subband_chan)
                        return 0;
 
+               if (!chan_in_band(triplet->chans.first_channel, band))
+                       return 0;
+
                /* 2 GHz */
                if (triplet->chans.first_channel <= 14) {
                        end_channel = triplet->chans.first_channel +
@@ -597,14 +650,10 @@ static int max_subband_chan(int orig_cur_chan,
                else {
                        end_channel =  triplet->chans.first_channel +
                                (4 * (triplet->chans.num_channels - 1));
-                       next_band = IEEE80211_BAND_5GHZ;
                }
 
-               if (band != next_band) {
-                       *country_ie -= 3;
-                       *country_ie_len += 3;
-                       break;
-               }
+               if (!chan_in_band(end_channel, band))
+                       return 0;
 
                if (orig_max_power != triplet->chans.max_power) {
                        *country_ie -= 3;
@@ -663,6 +712,7 @@ static int max_subband_chan(int orig_cur_chan,
  * with our userspace regulatory agent to get lower bounds.
  */
 static struct ieee80211_regdomain *country_ie_2_rd(
+                               enum ieee80211_band band,
                                u8 *country_ie,
                                u8 country_ie_len,
                                u32 *checksum)
@@ -737,8 +787,14 @@ static struct ieee80211_regdomain *country_ie_2_rd(
                        break;
                }
 
+               if (triplet->chans.num_channels == 0)
+                       return NULL;
+
+               if (!chan_in_band(triplet->chans.first_channel, band))
+                       return NULL;
+
                /* 2 GHz */
-               if (triplet->chans.first_channel <= 14)
+               if (band == IEEE80211_BAND_2GHZ)
                        end_channel = triplet->chans.first_channel +
                                triplet->chans.num_channels - 1;
                else
@@ -761,7 +817,8 @@ static struct ieee80211_regdomain *country_ie_2_rd(
                 * or for whatever reason sends triplets with multiple channels
                 * separated when in fact they should be together.
                 */
-               end_channel = max_subband_chan(cur_channel,
+               end_channel = max_subband_chan(band,
+                                              cur_channel,
                                               end_channel,
                                               triplet->chans.max_power,
                                               &country_ie,
@@ -769,6 +826,9 @@ static struct ieee80211_regdomain *country_ie_2_rd(
                if (!end_channel)
                        return NULL;
 
+               if (!chan_in_band(end_channel, band))
+                       return NULL;
+
                cur_sub_max_channel = end_channel;
 
                /* Basic sanity check */
@@ -861,14 +921,15 @@ static struct ieee80211_regdomain *country_ie_2_rd(
                reg_rule->flags = flags;
 
                /* 2 GHz */
-               if (triplet->chans.first_channel <= 14)
+               if (band == IEEE80211_BAND_2GHZ)
                        end_channel = triplet->chans.first_channel +
                                triplet->chans.num_channels -1;
                else
                        end_channel =  triplet->chans.first_channel +
                                (4 * (triplet->chans.num_channels - 1));
 
-               end_channel = max_subband_chan(triplet->chans.first_channel,
+               end_channel = max_subband_chan(band,
+                                              triplet->chans.first_channel,
                                               end_channel,
                                               triplet->chans.max_power,
                                               &country_ie,
@@ -1607,7 +1668,7 @@ static int ignore_request(struct wiphy *wiphy,
 
        switch (pending_request->initiator) {
        case NL80211_REGDOM_SET_BY_CORE:
-               return -EINVAL;
+               return 0;
        case NL80211_REGDOM_SET_BY_COUNTRY_IE:
 
                last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
@@ -1746,6 +1807,11 @@ new_request:
 
        pending_request = NULL;
 
+       if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) {
+               user_alpha2[0] = last_request->alpha2[0];
+               user_alpha2[1] = last_request->alpha2[1];
+       }
+
        /* When r == REG_INTERSECT we do need to call CRDA */
        if (r < 0) {
                /*
@@ -1865,12 +1931,16 @@ static void queue_regulatory_request(struct regulatory_request *request)
        schedule_work(&reg_work);
 }
 
-/* Core regulatory hint -- happens once during cfg80211_init() */
+/*
+ * Core regulatory hint -- happens during cfg80211_init()
+ * and when we restore regulatory settings.
+ */
 static int regulatory_hint_core(const char *alpha2)
 {
        struct regulatory_request *request;
 
-       BUG_ON(last_request);
+       kfree(last_request);
+       last_request = NULL;
 
        request = kzalloc(sizeof(struct regulatory_request),
                          GFP_KERNEL);
@@ -1881,14 +1951,12 @@ static int regulatory_hint_core(const char *alpha2)
        request->alpha2[1] = alpha2[1];
        request->initiator = NL80211_REGDOM_SET_BY_CORE;
 
-       queue_regulatory_request(request);
-
        /*
         * This ensures last_request is populated once modules
         * come swinging in and calling regulatory hints and
         * wiphy_apply_custom_regulatory().
         */
-       flush_scheduled_work();
+       reg_process_hint(request);
 
        return 0;
 }
@@ -1975,8 +2043,9 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy,
  * therefore cannot iterate over the rdev list here.
  */
 void regulatory_hint_11d(struct wiphy *wiphy,
-                       u8 *country_ie,
-                       u8 country_ie_len)
+                        enum ieee80211_band band,
+                        u8 *country_ie,
+                        u8 country_ie_len)
 {
        struct ieee80211_regdomain *rd = NULL;
        char alpha2[2];
@@ -2022,7 +2091,7 @@ void regulatory_hint_11d(struct wiphy *wiphy,
            wiphy_idx_valid(last_request->wiphy_idx)))
                goto out;
 
-       rd = country_ie_2_rd(country_ie, country_ie_len, &checksum);
+       rd = country_ie_2_rd(band, country_ie, country_ie_len, &checksum);
        if (!rd) {
                REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n");
                goto out;
@@ -2069,6 +2138,123 @@ out:
        mutex_unlock(&reg_mutex);
 }
 
+static void restore_alpha2(char *alpha2, bool reset_user)
+{
+       /* indicates there is no alpha2 to consider for restoration */
+       alpha2[0] = '9';
+       alpha2[1] = '7';
+
+       /* The user setting has precedence over the module parameter */
+       if (is_user_regdom_saved()) {
+               /* Unless we're asked to ignore it and reset it */
+               if (reset_user) {
+                       REG_DBG_PRINT("cfg80211: Restoring regulatory settings "
+                              "including user preference\n");
+                       user_alpha2[0] = '9';
+                       user_alpha2[1] = '7';
+
+                       /*
+                        * If we're ignoring user settings, we still need to
+                        * check the module parameter to ensure we put things
+                        * back as they were for a full restore.
+                        */
+                       if (!is_world_regdom(ieee80211_regdom)) {
+                               REG_DBG_PRINT("cfg80211: Keeping preference on "
+                                      "module parameter ieee80211_regdom: %c%c\n",
+                                      ieee80211_regdom[0],
+                                      ieee80211_regdom[1]);
+                               alpha2[0] = ieee80211_regdom[0];
+                               alpha2[1] = ieee80211_regdom[1];
+                       }
+               } else {
+                       REG_DBG_PRINT("cfg80211: Restoring regulatory settings "
+                              "while preserving user preference for: %c%c\n",
+                              user_alpha2[0],
+                              user_alpha2[1]);
+                       alpha2[0] = user_alpha2[0];
+                       alpha2[1] = user_alpha2[1];
+               }
+       } else if (!is_world_regdom(ieee80211_regdom)) {
+               REG_DBG_PRINT("cfg80211: Keeping preference on "
+                      "module parameter ieee80211_regdom: %c%c\n",
+                      ieee80211_regdom[0],
+                      ieee80211_regdom[1]);
+               alpha2[0] = ieee80211_regdom[0];
+               alpha2[1] = ieee80211_regdom[1];
+       } else
+               REG_DBG_PRINT("cfg80211: Restoring regulatory settings\n");
+}
+
+/*
+ * Restoring regulatory settings involves ingoring any
+ * possibly stale country IE information and user regulatory
+ * settings if so desired, this includes any beacon hints
+ * learned as we could have traveled outside to another country
+ * after disconnection. To restore regulatory settings we do
+ * exactly what we did at bootup:
+ *
+ *   - send a core regulatory hint
+ *   - send a user regulatory hint if applicable
+ *
+ * Device drivers that send a regulatory hint for a specific country
+ * keep their own regulatory domain on wiphy->regd so that does does
+ * not need to be remembered.
+ */
+static void restore_regulatory_settings(bool reset_user)
+{
+       char alpha2[2];
+       struct reg_beacon *reg_beacon, *btmp;
+
+       mutex_lock(&cfg80211_mutex);
+       mutex_lock(&reg_mutex);
+
+       reset_regdomains();
+       restore_alpha2(alpha2, reset_user);
+
+       /* Clear beacon hints */
+       spin_lock_bh(&reg_pending_beacons_lock);
+       if (!list_empty(&reg_pending_beacons)) {
+               list_for_each_entry_safe(reg_beacon, btmp,
+                                        &reg_pending_beacons, list) {
+                       list_del(&reg_beacon->list);
+                       kfree(reg_beacon);
+               }
+       }
+       spin_unlock_bh(&reg_pending_beacons_lock);
+
+       if (!list_empty(&reg_beacon_list)) {
+               list_for_each_entry_safe(reg_beacon, btmp,
+                                        &reg_beacon_list, list) {
+                       list_del(&reg_beacon->list);
+                       kfree(reg_beacon);
+               }
+       }
+
+       /* First restore to the basic regulatory settings */
+       cfg80211_regdomain = cfg80211_world_regdom;
+
+       mutex_unlock(&reg_mutex);
+       mutex_unlock(&cfg80211_mutex);
+
+       regulatory_hint_core(cfg80211_regdomain->alpha2);
+
+       /*
+        * This restores the ieee80211_regdom module parameter
+        * preference or the last user requested regulatory
+        * settings, user regulatory settings takes precedence.
+        */
+       if (is_an_alpha2(alpha2))
+               regulatory_hint_user(user_alpha2);
+}
+
+
+void regulatory_hint_disconnect(void)
+{
+       REG_DBG_PRINT("cfg80211: All devices are disconnected, going to "
+                     "restore regulatory settings\n");
+       restore_regulatory_settings(false);
+}
+
 static bool freq_is_chan_12_13_14(u16 freq)
 {
        if (freq == ieee80211_channel_to_frequency(12) ||
@@ -2458,6 +2644,9 @@ int regulatory_init(void)
 
        cfg80211_regdomain = cfg80211_world_regdom;
 
+       user_alpha2[0] = '9';
+       user_alpha2[1] = '7';
+
        /* We always try to get an update for the static regdomain */
        err = regulatory_hint_core(cfg80211_regdomain->alpha2);
        if (err) {