cfg80211: avoid flushing the global workqueue for core reg hints
[safe/jmp/linux-2.6] / net / wireless / reg.c
index a5c2d3a..5dcda28 100644 (file)
@@ -485,6 +485,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 +560,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 +570,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 +585,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 +610,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 +628,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 +690,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,10 +765,16 @@ 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;
+                               triplet->chans.num_channels - 1;
                else
                        /*
                         * 5 GHz -- For example in country IEs if the first
@@ -761,7 +795,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 +804,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 +899,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;
+                               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,
@@ -1881,14 +1920,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 +2012,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 +2060,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;