Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[safe/jmp/linux-2.6] / net / wireless / reg.c
index 4dab993..f643d39 100644 (file)
 #include "core.h"
 #include "reg.h"
 
-/**
- * struct regulatory_request - receipt of last regulatory request
- *
- * @wiphy: this is set if this request's initiator is
- *     %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This
- *     can be used by the wireless core to deal with conflicts
- *     and potentially inform users of which devices specifically
- *     cased the conflicts.
- * @initiator: indicates who sent this request, could be any of
- *     of those set in reg_set_by, %REGDOM_SET_BY_*
- * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested
- *     regulatory domain. We have a few special codes:
- *     00 - World regulatory domain
- *     99 - built by driver but a specific alpha2 cannot be determined
- *     98 - result of an intersection between two regulatory domains
- * @intersect: indicates whether the wireless core should intersect
- *     the requested regulatory domain with the presently set regulatory
- *     domain.
- * @country_ie_checksum: checksum of the last processed and accepted
- *     country IE
- * @country_ie_env: lets us know if the AP is telling us we are outdoor,
- *     indoor, or if it doesn't matter
- */
-struct regulatory_request {
-       struct wiphy *wiphy;
-       enum reg_set_by initiator;
-       char alpha2[2];
-       bool intersect;
-       u32 country_ie_checksum;
-       enum environment_cap country_ie_env;
-};
-
 /* Receipt of information from last regulatory request */
 static struct regulatory_request *last_request;
 
@@ -421,6 +389,31 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range,
        return 0;
 }
 
+/**
+ * freq_in_rule_band - tells us if a frequency is in a frequency band
+ * @freq_range: frequency rule we want to query
+ * @freq_khz: frequency we are inquiring about
+ *
+ * This lets us know if a specific frequency rule is or is not relevant to
+ * a specific frequency's band. Bands are device specific and artificial
+ * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is
+ * safe for now to assume that a frequency rule should not be part of a
+ * frequency's band if the start freq or end freq are off by more than 2 GHz.
+ * This resolution can be lowered and should be considered as we add
+ * regulatory rule support for other "bands".
+ **/
+static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
+       u32 freq_khz)
+{
+#define ONE_GHZ_IN_KHZ 1000000
+       if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+               return true;
+       if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+               return true;
+       return false;
+#undef ONE_GHZ_IN_KHZ
+}
+
 /* Converts a country IE to a regulatory domain. A regulatory domain
  * structure has a lot of information which the IE doesn't yet have,
  * so for the other values we use upper max values as we will intersect
@@ -473,6 +466,7 @@ static struct ieee80211_regdomain *country_ie_2_rd(
         * calculate the number of reg rules we will need. We will need one
         * for each channel subband */
        while (country_ie_len >= 3) {
+               int end_channel = 0;
                struct ieee80211_country_ie_triplet *triplet =
                        (struct ieee80211_country_ie_triplet *) country_ie;
                int cur_sub_max_channel = 0, cur_channel = 0;
@@ -484,9 +478,25 @@ static struct ieee80211_regdomain *country_ie_2_rd(
                        continue;
                }
 
+               /* 2 GHz */
+               if (triplet->chans.first_channel <= 14)
+                       end_channel = triplet->chans.first_channel +
+                               triplet->chans.num_channels;
+               else
+                       /*
+                        * 5 GHz -- For example in country IEs if the first
+                        * channel given is 36 and the number of channels is 4
+                        * then the individual channel numbers defined for the
+                        * 5 GHz PHY by these parameters are: 36, 40, 44, and 48
+                        * and not 36, 37, 38, 39.
+                        *
+                        * See: http://tinyurl.com/11d-clarification
+                        */
+                       end_channel =  triplet->chans.first_channel +
+                               (4 * (triplet->chans.num_channels - 1));
+
                cur_channel = triplet->chans.first_channel;
-               cur_sub_max_channel = ieee80211_channel_to_frequency(
-                       cur_channel + triplet->chans.num_channels);
+               cur_sub_max_channel = end_channel;
 
                /* Basic sanity check */
                if (cur_sub_max_channel < cur_channel)
@@ -538,6 +548,7 @@ static struct ieee80211_regdomain *country_ie_2_rd(
 
        /* This time around we fill in the rd */
        while (country_ie_len >= 3) {
+               int end_channel = 0;
                struct ieee80211_country_ie_triplet *triplet =
                        (struct ieee80211_country_ie_triplet *) country_ie;
                struct ieee80211_reg_rule *reg_rule = NULL;
@@ -559,6 +570,14 @@ static struct ieee80211_regdomain *country_ie_2_rd(
 
                reg_rule->flags = flags;
 
+               /* 2 GHz */
+               if (triplet->chans.first_channel <= 14)
+                       end_channel = triplet->chans.first_channel +
+                               triplet->chans.num_channels;
+               else
+                       end_channel =  triplet->chans.first_channel +
+                               (4 * (triplet->chans.num_channels - 1));
+
                /* The +10 is since the regulatory domain expects
                 * the actual band edge, not the center of freq for
                 * its start and end freqs, assuming 20 MHz bandwidth on
@@ -568,8 +587,7 @@ static struct ieee80211_regdomain *country_ie_2_rd(
                                triplet->chans.first_channel) - 10);
                freq_range->end_freq_khz =
                        MHZ_TO_KHZ(ieee80211_channel_to_frequency(
-                               triplet->chans.first_channel +
-                                       triplet->chans.num_channels) + 10);
+                               end_channel) + 10);
 
                /* Large arbitrary values, we intersect later */
                /* Increment this if we ever support >= 40 MHz channels
@@ -740,34 +758,46 @@ static u32 map_regdom_flags(u32 rd_flags)
        return channel_flags;
 }
 
-/**
- * freq_reg_info - get regulatory information for the given frequency
- * @center_freq: Frequency in KHz for which we want regulatory information for
- * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one
- *     you can set this to 0. If this frequency is allowed we then set
- *     this value to the maximum allowed bandwidth.
- * @reg_rule: the regulatory rule which we have for this frequency
- *
- * Use this function to get the regulatory rule for a specific frequency.
- */
-static int freq_reg_info(u32 center_freq, u32 *bandwidth,
-                        const struct ieee80211_reg_rule **reg_rule)
+static int freq_reg_info_regd(struct wiphy *wiphy,
+                             u32 center_freq,
+                             u32 *bandwidth,
+                             const struct ieee80211_reg_rule **reg_rule,
+                             const struct ieee80211_regdomain *custom_regd)
 {
        int i;
+       bool band_rule_found = false;
+       const struct ieee80211_regdomain *regd;
        u32 max_bandwidth = 0;
 
-       if (!cfg80211_regdomain)
+       regd = custom_regd ? custom_regd : cfg80211_regdomain;
+
+       /* Follow the driver's regulatory domain, if present, unless a country
+        * IE has been processed or a user wants to help complaince further */
+       if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE &&
+           last_request->initiator != REGDOM_SET_BY_USER &&
+           wiphy->regd)
+               regd = wiphy->regd;
+
+       if (!regd)
                return -EINVAL;
 
-       for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
+       for (i = 0; i < regd->n_reg_rules; i++) {
                const struct ieee80211_reg_rule *rr;
                const struct ieee80211_freq_range *fr = NULL;
                const struct ieee80211_power_rule *pr = NULL;
 
-               rr = &cfg80211_regdomain->reg_rules[i];
+               rr = &regd->reg_rules[i];
                fr = &rr->freq_range;
                pr = &rr->power_rule;
+
+               /* We only need to know if one frequency rule was
+                * was in center_freq's band, that's enough, so lets
+                * not overwrite it once found */
+               if (!band_rule_found)
+                       band_rule_found = freq_in_rule_band(fr, center_freq);
+
                max_bandwidth = freq_max_bandwidth(fr, center_freq);
+
                if (max_bandwidth && *bandwidth <= max_bandwidth) {
                        *reg_rule = rr;
                        *bandwidth = max_bandwidth;
@@ -775,28 +805,93 @@ static int freq_reg_info(u32 center_freq, u32 *bandwidth,
                }
        }
 
+       if (!band_rule_found)
+               return -ERANGE;
+
        return !max_bandwidth;
 }
+EXPORT_SYMBOL(freq_reg_info);
+
+int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth,
+                        const struct ieee80211_reg_rule **reg_rule)
+{
+       return freq_reg_info_regd(wiphy, center_freq,
+               bandwidth, reg_rule, NULL);
+}
 
-static void handle_channel(struct ieee80211_channel *chan)
+static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
+                          unsigned int chan_idx)
 {
        int r;
-       u32 flags = chan->orig_flags;
+       u32 flags;
        u32 max_bandwidth = 0;
        const struct ieee80211_reg_rule *reg_rule = NULL;
        const struct ieee80211_power_rule *power_rule = NULL;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_channel *chan;
+
+       sband = wiphy->bands[band];
+       BUG_ON(chan_idx >= sband->n_channels);
+       chan = &sband->channels[chan_idx];
+
+       flags = chan->orig_flags;
 
-       r = freq_reg_info(MHZ_TO_KHZ(chan->center_freq),
+       r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq),
                &max_bandwidth, &reg_rule);
 
        if (r) {
-               flags |= IEEE80211_CHAN_DISABLED;
-               chan->flags = flags;
+               /* This means no regulatory rule was found in the country IE
+                * with a frequency range on the center_freq's band, since
+                * IEEE-802.11 allows for a country IE to have a subset of the
+                * regulatory information provided in a country we ignore
+                * disabling the channel unless at least one reg rule was
+                * found on the center_freq's band. For details see this
+                * clarification:
+                *
+                * http://tinyurl.com/11d-clarification
+                */
+               if (r == -ERANGE &&
+                   last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
+#ifdef CONFIG_CFG80211_REG_DEBUG
+                       printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz "
+                               "intact on %s - no rule found in band on "
+                               "Country IE\n",
+                               chan->center_freq, wiphy_name(wiphy));
+#endif
+               } else {
+               /* In this case we know the country IE has at least one reg rule
+                * for the band so we respect its band definitions */
+#ifdef CONFIG_CFG80211_REG_DEBUG
+                       if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
+                               printk(KERN_DEBUG "cfg80211: Disabling "
+                                       "channel %d MHz on %s due to "
+                                       "Country IE\n",
+                                       chan->center_freq, wiphy_name(wiphy));
+#endif
+                       flags |= IEEE80211_CHAN_DISABLED;
+                       chan->flags = flags;
+               }
                return;
        }
 
        power_rule = &reg_rule->power_rule;
 
+       if (last_request->initiator == REGDOM_SET_BY_DRIVER &&
+           last_request->wiphy && last_request->wiphy == wiphy &&
+           last_request->wiphy->strict_regulatory) {
+               /* This gaurantees the driver's requested regulatory domain
+                * will always be used as a base for further regulatory
+                * settings */
+               chan->flags = chan->orig_flags =
+                       map_regdom_flags(reg_rule->flags);
+               chan->max_antenna_gain = chan->orig_mag =
+                       (int) MBI_TO_DBI(power_rule->max_antenna_gain);
+               chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
+               chan->max_power = chan->orig_mpwr =
+                       (int) MBM_TO_DBM(power_rule->max_eirp);
+               return;
+       }
+
        chan->flags = flags | map_regdom_flags(reg_rule->flags);
        chan->max_antenna_gain = min(chan->orig_mag,
                (int) MBI_TO_DBI(power_rule->max_antenna_gain));
@@ -808,12 +903,31 @@ static void handle_channel(struct ieee80211_channel *chan)
                chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
 }
 
-static void handle_band(struct ieee80211_supported_band *sband)
+static void handle_band(struct wiphy *wiphy, enum ieee80211_band band)
 {
-       int i;
+       unsigned int i;
+       struct ieee80211_supported_band *sband;
+
+       BUG_ON(!wiphy->bands[band]);
+       sband = wiphy->bands[band];
 
        for (i = 0; i < sband->n_channels; i++)
-               handle_channel(&sband->channels[i]);
+               handle_channel(wiphy, band, i);
+}
+
+static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby)
+{
+       if (!last_request)
+               return true;
+       if (setby == REGDOM_SET_BY_CORE &&
+                 wiphy->custom_regulatory)
+               return true;
+       /* wiphy->regd will be set once the device has its own
+        * desired regulatory domain set */
+       if (wiphy->strict_regulatory && !wiphy->regd &&
+           !is_world_regdom(last_request->alpha2))
+               return true;
+       return false;
 }
 
 static void update_all_wiphy_regulatory(enum reg_set_by setby)
@@ -827,12 +941,96 @@ static void update_all_wiphy_regulatory(enum reg_set_by setby)
 void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby)
 {
        enum ieee80211_band band;
+
+       if (ignore_reg_update(wiphy, setby))
+               return;
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                if (wiphy->bands[band])
-                       handle_band(wiphy->bands[band]);
-               if (wiphy->reg_notifier)
-                       wiphy->reg_notifier(wiphy, setby);
+                       handle_band(wiphy, band);
        }
+       if (wiphy->reg_notifier)
+               wiphy->reg_notifier(wiphy, last_request);
+}
+
+static void handle_channel_custom(struct wiphy *wiphy,
+                                 enum ieee80211_band band,
+                                 unsigned int chan_idx,
+                                 const struct ieee80211_regdomain *regd)
+{
+       int r;
+       u32 max_bandwidth = 0;
+       const struct ieee80211_reg_rule *reg_rule = NULL;
+       const struct ieee80211_power_rule *power_rule = NULL;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_channel *chan;
+
+       sband = wiphy->bands[band];
+       BUG_ON(chan_idx >= sband->n_channels);
+       chan = &sband->channels[chan_idx];
+
+       r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
+               &max_bandwidth, &reg_rule, regd);
+
+       if (r) {
+               chan->flags = IEEE80211_CHAN_DISABLED;
+               return;
+       }
+
+       power_rule = &reg_rule->power_rule;
+
+       chan->flags |= map_regdom_flags(reg_rule->flags);
+       chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
+       chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
+       chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
+}
+
+static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band,
+                              const struct ieee80211_regdomain *regd)
+{
+       unsigned int i;
+       struct ieee80211_supported_band *sband;
+
+       BUG_ON(!wiphy->bands[band]);
+       sband = wiphy->bands[band];
+
+       for (i = 0; i < sband->n_channels; i++)
+               handle_channel_custom(wiphy, band, i, regd);
+}
+
+/* Used by drivers prior to wiphy registration */
+void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
+                                  const struct ieee80211_regdomain *regd)
+{
+       enum ieee80211_band band;
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               if (wiphy->bands[band])
+                       handle_band_custom(wiphy, band, regd);
+       }
+}
+EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
+
+static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
+                        const struct ieee80211_regdomain *src_regd)
+{
+       struct ieee80211_regdomain *regd;
+       int size_of_regd = 0;
+       unsigned int i;
+
+       size_of_regd = sizeof(struct ieee80211_regdomain) +
+         ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule));
+
+       regd = kzalloc(size_of_regd, GFP_KERNEL);
+       if (!regd)
+               return -ENOMEM;
+
+       memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
+
+       for (i = 0; i < src_regd->n_reg_rules; i++)
+               memcpy(&regd->reg_rules[i], &src_regd->reg_rules[i],
+                       sizeof(struct ieee80211_reg_rule));
+
+       *dst_regd = regd;
+       return 0;
 }
 
 /* Return value which can be used by ignore_request() to indicate
@@ -882,9 +1080,14 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
                }
                return REG_INTERSECT;
        case REGDOM_SET_BY_DRIVER:
-               if (last_request->initiator == REGDOM_SET_BY_DRIVER)
+               if (last_request->initiator == REGDOM_SET_BY_CORE) {
+                       if (is_old_static_regdom(cfg80211_regdomain))
+                               return 0;
+                       if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
+                               return 0;
                        return -EALREADY;
-               return 0;
+               }
+               return REG_INTERSECT;
        case REGDOM_SET_BY_USER:
                if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
                        return REG_INTERSECT;
@@ -893,6 +1096,20 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
                if (last_request->initiator == REGDOM_SET_BY_USER &&
                          last_request->intersect)
                        return -EOPNOTSUPP;
+               /* Process user requests only after previous user/driver/core
+                * requests have been processed */
+               if (last_request->initiator == REGDOM_SET_BY_CORE ||
+                   last_request->initiator == REGDOM_SET_BY_DRIVER ||
+                   last_request->initiator == REGDOM_SET_BY_USER) {
+                       if (!alpha2_equal(last_request->alpha2,
+                           cfg80211_regdomain->alpha2))
+                               return -EAGAIN;
+               }
+
+               if (!is_old_static_regdom(cfg80211_regdomain) &&
+                   alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
+                       return -EALREADY;
+
                return 0;
        }
 
@@ -911,11 +1128,28 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
 
        r = ignore_request(wiphy, set_by, alpha2);
 
-       if (r == REG_INTERSECT)
+       if (r == REG_INTERSECT) {
+               if (set_by == REGDOM_SET_BY_DRIVER) {
+                       r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain);
+                       if (r)
+                               return r;
+               }
                intersect = true;
-       else if (r)
+       } else if (r) {
+               /* If the regulatory domain being requested by the
+                * driver has already been set just copy it to the
+                * wiphy */
+               if (r == -EALREADY && set_by == REGDOM_SET_BY_DRIVER) {
+                       r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain);
+                       if (r)
+                               return r;
+                       r = -EALREADY;
+                       goto new_request;
+               }
                return r;
+       }
 
+new_request:
        request = kzalloc(sizeof(struct regulatory_request),
                          GFP_KERNEL);
        if (!request)
@@ -931,6 +1165,11 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
 
        kfree(last_request);
        last_request = request;
+
+       /* When r == REG_INTERSECT we do need to call CRDA */
+       if (r < 0)
+               return r;
+
        /*
         * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled
         * AND if CRDA is NOT present nothing will happen, if someone
@@ -946,10 +1185,15 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
 
 void regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 {
+       int r;
        BUG_ON(!alpha2);
 
        mutex_lock(&cfg80211_drv_mutex);
-       __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY);
+       r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER,
+               alpha2, 0, ENVIRON_ANY);
+       /* This is required so that the orig_* parameters are saved */
+       if (r == -EALREADY && wiphy->strict_regulatory)
+               wiphy_update_regulatory(wiphy, REGDOM_SET_BY_DRIVER);
        mutex_unlock(&cfg80211_drv_mutex);
 }
 EXPORT_SYMBOL(regulatory_hint);
@@ -978,6 +1222,9 @@ void regulatory_hint_11d(struct wiphy *wiphy,
        u32 checksum = 0;
        enum environment_cap env = ENVIRON_ANY;
 
+       if (!last_request)
+               return;
+
        mutex_lock(&cfg80211_drv_mutex);
 
        /* IE len must be evenly divisible by 2 */
@@ -1119,7 +1366,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
                                        "domain intersected: \n");
                } else
                                printk(KERN_INFO "cfg80211: Current regulatory "
-                                       "intersected: \n");
+                                       "domain intersected: \n");
        } else if (is_world_regdom(rd->alpha2))
                printk(KERN_INFO "cfg80211: World regulatory "
                        "domain updated:\n");
@@ -1156,7 +1403,7 @@ static void reg_country_ie_process_debug(
        if (intersected_rd) {
                printk(KERN_DEBUG "cfg80211: We intersect both of these "
                        "and get:\n");
-               print_regdomain_info(rd);
+               print_regdomain_info(intersected_rd);
                return;
        }
        printk(KERN_DEBUG "cfg80211: Intersection between both failed\n");
@@ -1221,6 +1468,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
        }
 
        if (!last_request->intersect) {
+               int r;
+
+               if (last_request->initiator != REGDOM_SET_BY_DRIVER) {
+                       reset_regdomains();
+                       cfg80211_regdomain = rd;
+                       return 0;
+               }
+
+               /* For a driver hint, lets copy the regulatory domain the
+                * driver wanted to the wiphy to deal with conflicts */
+
+               BUG_ON(last_request->wiphy->regd);
+
+               r = reg_copy_regd(&last_request->wiphy->regd, rd);
+               if (r)
+                       return r;
+
                reset_regdomains();
                cfg80211_regdomain = rd;
                return 0;
@@ -1234,8 +1498,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
                if (!intersected_rd)
                        return -EINVAL;
 
-               /* We can trash what CRDA provided now */
-               kfree(rd);
+               /* We can trash what CRDA provided now.
+                * However if a driver requested this specific regulatory
+                * domain we keep it for its private use */
+               if (last_request->initiator == REGDOM_SET_BY_DRIVER)
+                       last_request->wiphy->regd = rd;
+               else
+                       kfree(rd);
+
                rd = NULL;
 
                reset_regdomains();
@@ -1319,7 +1589,8 @@ int set_regdom(const struct ieee80211_regdomain *rd)
 /* Caller must hold cfg80211_drv_mutex */
 void reg_device_remove(struct wiphy *wiphy)
 {
-       if (!last_request->wiphy)
+       kfree(wiphy->regd);
+       if (!last_request || !last_request->wiphy)
                return;
        if (last_request->wiphy != wiphy)
                return;