Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[sfrench/cifs-2.6.git] / net / wireless / reg.c
index 2f702adf2912105947560d07d79ff86119333bbf..148c229fe84f1949536116bd9901a6ab8349585f 100644 (file)
@@ -847,22 +847,36 @@ static bool valid_regdb(const u8 *data, unsigned int size)
        return true;
 }
 
-static void set_wmm_rule(struct ieee80211_reg_rule *rrule,
-                        struct fwdb_wmm_rule *wmm)
-{
-       struct ieee80211_wmm_rule *rule = &rrule->wmm_rule;
-       unsigned int i;
+static void set_wmm_rule(const struct fwdb_header *db,
+                        const struct fwdb_country *country,
+                        const struct fwdb_rule *rule,
+                        struct ieee80211_reg_rule *rrule)
+{
+       struct ieee80211_wmm_rule *wmm_rule = &rrule->wmm_rule;
+       struct fwdb_wmm_rule *wmm;
+       unsigned int i, wmm_ptr;
+
+       wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2;
+       wmm = (void *)((u8 *)db + wmm_ptr);
+
+       if (!valid_wmm(wmm)) {
+               pr_err("Invalid regulatory WMM rule %u-%u in domain %c%c\n",
+                      be32_to_cpu(rule->start), be32_to_cpu(rule->end),
+                      country->alpha2[0], country->alpha2[1]);
+               return;
+       }
 
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
-               rule->client[i].cw_min =
+               wmm_rule->client[i].cw_min =
                        ecw2cw((wmm->client[i].ecw & 0xf0) >> 4);
-               rule->client[i].cw_max = ecw2cw(wmm->client[i].ecw & 0x0f);
-               rule->client[i].aifsn =  wmm->client[i].aifsn;
-               rule->client[i].cot = 1000 * be16_to_cpu(wmm->client[i].cot);
-               rule->ap[i].cw_min = ecw2cw((wmm->ap[i].ecw & 0xf0) >> 4);
-               rule->ap[i].cw_max = ecw2cw(wmm->ap[i].ecw & 0x0f);
-               rule->ap[i].aifsn = wmm->ap[i].aifsn;
-               rule->ap[i].cot = 1000 * be16_to_cpu(wmm->ap[i].cot);
+               wmm_rule->client[i].cw_max = ecw2cw(wmm->client[i].ecw & 0x0f);
+               wmm_rule->client[i].aifsn =  wmm->client[i].aifsn;
+               wmm_rule->client[i].cot =
+                       1000 * be16_to_cpu(wmm->client[i].cot);
+               wmm_rule->ap[i].cw_min = ecw2cw((wmm->ap[i].ecw & 0xf0) >> 4);
+               wmm_rule->ap[i].cw_max = ecw2cw(wmm->ap[i].ecw & 0x0f);
+               wmm_rule->ap[i].aifsn = wmm->ap[i].aifsn;
+               wmm_rule->ap[i].cot = 1000 * be16_to_cpu(wmm->ap[i].cot);
        }
 
        rrule->has_wmm = true;
@@ -870,7 +884,7 @@ static void set_wmm_rule(struct ieee80211_reg_rule *rrule,
 
 static int __regdb_query_wmm(const struct fwdb_header *db,
                             const struct fwdb_country *country, int freq,
-                            struct ieee80211_reg_rule *rule)
+                            struct ieee80211_reg_rule *rrule)
 {
        unsigned int ptr = be16_to_cpu(country->coll_ptr) << 2;
        struct fwdb_collection *coll = (void *)((u8 *)db + ptr);
@@ -879,18 +893,14 @@ static int __regdb_query_wmm(const struct fwdb_header *db,
        for (i = 0; i < coll->n_rules; i++) {
                __be16 *rules_ptr = (void *)((u8 *)coll + ALIGN(coll->len, 2));
                unsigned int rule_ptr = be16_to_cpu(rules_ptr[i]) << 2;
-               struct fwdb_rule *rrule = (void *)((u8 *)db + rule_ptr);
-               struct fwdb_wmm_rule *wmm;
-               unsigned int wmm_ptr;
+               struct fwdb_rule *rule = (void *)((u8 *)db + rule_ptr);
 
-               if (rrule->len < offsetofend(struct fwdb_rule, wmm_ptr))
+               if (rule->len < offsetofend(struct fwdb_rule, wmm_ptr))
                        continue;
 
-               if (freq >= KHZ_TO_MHZ(be32_to_cpu(rrule->start)) &&
-                   freq <= KHZ_TO_MHZ(be32_to_cpu(rrule->end))) {
-                       wmm_ptr = be16_to_cpu(rrule->wmm_ptr) << 2;
-                       wmm = (void *)((u8 *)db + wmm_ptr);
-                       set_wmm_rule(rule, wmm);
+               if (freq >= KHZ_TO_MHZ(be32_to_cpu(rule->start)) &&
+                   freq <= KHZ_TO_MHZ(be32_to_cpu(rule->end))) {
+                       set_wmm_rule(db, country, rule, rrule);
                        return 0;
                }
        }
@@ -972,12 +982,8 @@ static int regdb_query_country(const struct fwdb_header *db,
                if (rule->len >= offsetofend(struct fwdb_rule, cac_timeout))
                        rrule->dfs_cac_ms =
                                1000 * be16_to_cpu(rule->cac_timeout);
-               if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr)) {
-                       u32 wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2;
-                       struct fwdb_wmm_rule *wmm = (void *)((u8 *)db + wmm_ptr);
-
-                       set_wmm_rule(rrule, wmm);
-               }
+               if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr))
+                       set_wmm_rule(db, country, rule, rrule);
        }
 
        return reg_schedule_apply(regdom);
@@ -2661,11 +2667,12 @@ static void reg_process_hint(struct regulatory_request *reg_request)
 {
        struct wiphy *wiphy = NULL;
        enum reg_request_treatment treatment;
+       enum nl80211_reg_initiator initiator = reg_request->initiator;
 
        if (reg_request->wiphy_idx != WIPHY_IDX_INVALID)
                wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
 
-       switch (reg_request->initiator) {
+       switch (initiator) {
        case NL80211_REGDOM_SET_BY_CORE:
                treatment = reg_process_hint_core(reg_request);
                break;
@@ -2683,7 +2690,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
                treatment = reg_process_hint_country_ie(wiphy, reg_request);
                break;
        default:
-               WARN(1, "invalid initiator %d\n", reg_request->initiator);
+               WARN(1, "invalid initiator %d\n", initiator);
                goto out_free;
        }
 
@@ -2698,7 +2705,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
         */
        if (treatment == REG_REQ_ALREADY_SET && wiphy &&
            wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
-               wiphy_update_regulatory(wiphy, reg_request->initiator);
+               wiphy_update_regulatory(wiphy, initiator);
                wiphy_all_share_dfs_chan_state(wiphy);
                reg_check_channels();
        }
@@ -2867,6 +2874,7 @@ static int regulatory_hint_core(const char *alpha2)
        request->alpha2[0] = alpha2[0];
        request->alpha2[1] = alpha2[1];
        request->initiator = NL80211_REGDOM_SET_BY_CORE;
+       request->wiphy_idx = WIPHY_IDX_INVALID;
 
        queue_regulatory_request(request);
 
@@ -3184,13 +3192,59 @@ static void restore_regulatory_settings(bool reset_user)
        schedule_work(&reg_work);
 }
 
+static bool is_wiphy_all_set_reg_flag(enum ieee80211_regulatory_flags flag)
+{
+       struct cfg80211_registered_device *rdev;
+       struct wireless_dev *wdev;
+
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+                       wdev_lock(wdev);
+                       if (!(wdev->wiphy->regulatory_flags & flag)) {
+                               wdev_unlock(wdev);
+                               return false;
+                       }
+                       wdev_unlock(wdev);
+               }
+       }
+
+       return true;
+}
+
 void regulatory_hint_disconnect(void)
 {
+       /* Restore of regulatory settings is not required when wiphy(s)
+        * ignore IE from connected access point but clearance of beacon hints
+        * is required when wiphy(s) supports beacon hints.
+        */
+       if (is_wiphy_all_set_reg_flag(REGULATORY_COUNTRY_IE_IGNORE)) {
+               struct reg_beacon *reg_beacon, *btmp;
+
+               if (is_wiphy_all_set_reg_flag(REGULATORY_DISABLE_BEACON_HINTS))
+                       return;
+
+               spin_lock_bh(&reg_pending_beacons_lock);
+               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);
+
+               list_for_each_entry_safe(reg_beacon, btmp,
+                                        &reg_beacon_list, list) {
+                       list_del(&reg_beacon->list);
+                       kfree(reg_beacon);
+               }
+
+               return;
+       }
+
        pr_debug("All devices are disconnected, going to restore regulatory settings\n");
        restore_regulatory_settings(false);
 }
 
-static bool freq_is_chan_12_13_14(u16 freq)
+static bool freq_is_chan_12_13_14(u32 freq)
 {
        if (freq == ieee80211_channel_to_frequency(12, NL80211_BAND_2GHZ) ||
            freq == ieee80211_channel_to_frequency(13, NL80211_BAND_2GHZ) ||