Merge branch 'x86-platform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / net / wireless / reg.c
index dd58b99..2f1bf91 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright      2017  Intel Deutschland GmbH
- * Copyright (C) 2018 Intel Corporation
+ * Copyright (C) 2018 - 2019 Intel Corporation
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -131,7 +131,8 @@ static spinlock_t reg_indoor_lock;
 /* Used to track the userspace process controlling the indoor setting */
 static u32 reg_is_indoor_portid;
 
-static void restore_regulatory_settings(bool reset_user);
+static void restore_regulatory_settings(bool reset_user, bool cached);
+static void print_regdomain(const struct ieee80211_regdomain *rd);
 
 static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
 {
@@ -263,6 +264,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
 
 static char *ieee80211_regdom = "00";
 static char user_alpha2[2];
+static const struct ieee80211_regdomain *cfg80211_user_regdom;
 
 module_param(ieee80211_regdom, charp, 0444);
 MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
@@ -445,6 +447,15 @@ reg_copy_regd(const struct ieee80211_regdomain *src_regd)
        return regd;
 }
 
+static void cfg80211_save_user_regdom(const struct ieee80211_regdomain *rd)
+{
+       ASSERT_RTNL();
+
+       if (!IS_ERR(cfg80211_user_regdom))
+               kfree(cfg80211_user_regdom);
+       cfg80211_user_regdom = reg_copy_regd(rd);
+}
+
 struct reg_regdb_apply_request {
        struct list_head list;
        const struct ieee80211_regdomain *regdom;
@@ -510,7 +521,7 @@ static void crda_timeout_work(struct work_struct *work)
        pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
        rtnl_lock();
        reg_crda_timeouts++;
-       restore_regulatory_settings(true);
+       restore_regulatory_settings(true, false);
        rtnl_unlock();
 }
 
@@ -1044,7 +1055,7 @@ static void regdb_fw_cb(const struct firmware *fw, void *context)
        }
 
        if (restore)
-               restore_regulatory_settings(true);
+               restore_regulatory_settings(true, false);
 
        rtnl_unlock();
 
@@ -2729,9 +2740,7 @@ static void notify_self_managed_wiphys(struct regulatory_request *request)
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                wiphy = &rdev->wiphy;
                if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
-                   request->initiator == NL80211_REGDOM_SET_BY_USER &&
-                   request->user_reg_hint_type ==
-                               NL80211_USER_REG_HINT_CELL_BASE)
+                   request->initiator == NL80211_REGDOM_SET_BY_USER)
                        reg_call_notifier(wiphy, request);
        }
 }
@@ -3119,7 +3128,7 @@ static void restore_custom_reg_settings(struct wiphy *wiphy)
  * 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)
+static void restore_regulatory_settings(bool reset_user, bool cached)
 {
        char alpha2[2];
        char world_alpha2[2];
@@ -3178,15 +3187,41 @@ static void restore_regulatory_settings(bool reset_user)
                        restore_custom_reg_settings(&rdev->wiphy);
        }
 
-       regulatory_hint_core(world_alpha2);
+       if (cached && (!is_an_alpha2(alpha2) ||
+                      !IS_ERR_OR_NULL(cfg80211_user_regdom))) {
+               reset_regdomains(false, cfg80211_world_regdom);
+               update_all_wiphy_regulatory(NL80211_REGDOM_SET_BY_CORE);
+               print_regdomain(get_cfg80211_regdom());
+               nl80211_send_reg_change_event(&core_request_world);
+               reg_set_request_processed();
 
-       /*
-        * 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(alpha2, NL80211_USER_REG_HINT_USER);
+               if (is_an_alpha2(alpha2) &&
+                   !regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER)) {
+                       struct regulatory_request *ureq;
+
+                       spin_lock(&reg_requests_lock);
+                       ureq = list_last_entry(&reg_requests_list,
+                                              struct regulatory_request,
+                                              list);
+                       list_del(&ureq->list);
+                       spin_unlock(&reg_requests_lock);
+
+                       notify_self_managed_wiphys(ureq);
+                       reg_update_last_request(ureq);
+                       set_regdom(reg_copy_regd(cfg80211_user_regdom),
+                                  REGD_SOURCE_CACHED);
+               }
+       } else {
+               regulatory_hint_core(world_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(alpha2, NL80211_USER_REG_HINT_USER);
+       }
 
        spin_lock(&reg_requests_lock);
        list_splice_tail_init(&tmp_reg_req_list, &reg_requests_list);
@@ -3246,7 +3281,7 @@ void regulatory_hint_disconnect(void)
        }
 
        pr_debug("All devices are disconnected, going to restore regulatory settings\n");
-       restore_regulatory_settings(false);
+       restore_regulatory_settings(false, true);
 }
 
 static bool freq_is_chan_12_13_14(u32 freq)
@@ -3563,6 +3598,9 @@ int set_regdom(const struct ieee80211_regdomain *rd,
        bool user_reset = false;
        int r;
 
+       if (IS_ERR_OR_NULL(rd))
+               return -ENODATA;
+
        if (!reg_is_valid_request(rd->alpha2)) {
                kfree(rd);
                return -EINVAL;
@@ -3579,6 +3617,7 @@ int set_regdom(const struct ieee80211_regdomain *rd,
                r = reg_set_rd_core(rd);
                break;
        case NL80211_REGDOM_SET_BY_USER:
+               cfg80211_save_user_regdom(rd);
                r = reg_set_rd_user(rd, lr);
                user_reset = true;
                break;
@@ -3601,7 +3640,7 @@ int set_regdom(const struct ieee80211_regdomain *rd,
                        break;
                default:
                        /* Back to world regulatory in case of errors */
-                       restore_regulatory_settings(user_reset);
+                       restore_regulatory_settings(user_reset, false);
                }
 
                kfree(rd);
@@ -3937,6 +3976,8 @@ void regulatory_exit(void)
 
        if (!IS_ERR_OR_NULL(regdb))
                kfree(regdb);
+       if (!IS_ERR_OR_NULL(cfg80211_user_regdom))
+               kfree(cfg80211_user_regdom);
 
        free_regdb_keyring();
 }