/**
  * freq_reg_info - get regulatory information for the given frequency
+ * @wiphy: the wiphy for which we want to process this rule for
  * @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
  * freq_in_rule_band() for our current definition of a band -- this is purely
  * subjective and right now its 802.11 specific.
  */
-static int freq_reg_info(u32 center_freq, u32 *bandwidth,
+static int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth,
                         const struct ieee80211_reg_rule **reg_rule)
 {
        int i;
        bool band_rule_found = false;
+       const struct ieee80211_regdomain *regd;
        u32 max_bandwidth = 0;
 
-       if (!cfg80211_regdomain)
+       regd = cfg80211_regdomain;
+
+       /* Follow the driver's regulatory domain, if present, unless a country
+        * IE has been processed */
+       if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE &&
+           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 = ®d->reg_rules[i];
                fr = &rr->freq_range;
                pr = &rr->power_rule;
 
 
        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, ®_rule);
 
        if (r) {
                wiphy->reg_notifier(wiphy, setby);
 }
 
+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(®d->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
  * it has been determined we should intersect two regulatory domains */
 #define REG_INTERSECT  1
                }
                return REG_INTERSECT;
        case REGDOM_SET_BY_DRIVER:
-               if (last_request->initiator == REGDOM_SET_BY_DRIVER)
-                       return -EALREADY;
-               return 0;
+               if (last_request->initiator == REGDOM_SET_BY_CORE)
+                       return 0;
+               return REG_INTERSECT;
        case REGDOM_SET_BY_USER:
                if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
                        return REG_INTERSECT;
 
        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)
 
        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
        }
 
        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;
                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();
 /* Caller must hold cfg80211_drv_mutex */
 void reg_device_remove(struct wiphy *wiphy)
 {
+       kfree(wiphy->regd);
        if (!last_request || !last_request->wiphy)
                return;
        if (last_request->wiphy != wiphy)