]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/wireless/reg.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/teigland...
[karo-tx-linux.git] / net / wireless / reg.c
index 3302c56f60d1511d292a2e9b4f71ad87b6d6f722..f65feaad155f42a328460d03ee0c47c770b81981 100644 (file)
@@ -2,13 +2,22 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright 2007      Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2008      Luis R. Rodriguez <lrodriguz@atheros.com>
+ * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+
 /**
  * DOC: Wireless regulatory infrastructure
  *
@@ -873,10 +882,22 @@ static void handle_channel(struct wiphy *wiphy,
        chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
        chan->max_antenna_gain = min(chan->orig_mag,
                (int) MBI_TO_DBI(power_rule->max_antenna_gain));
-       if (chan->orig_mpwr)
-               chan->max_power = min(chan->orig_mpwr,
-                       (int) MBM_TO_DBM(power_rule->max_eirp));
-       else
+       if (chan->orig_mpwr) {
+               /*
+                * Devices that have their own custom regulatory domain
+                * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the
+                * passed country IE power settings.
+                */
+               if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+                   wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
+                   wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
+                       chan->max_power =
+                               MBM_TO_DBM(power_rule->max_eirp);
+               } else {
+                       chan->max_power = min(chan->orig_mpwr,
+                               (int) MBM_TO_DBM(power_rule->max_eirp));
+               }
+       } else
                chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
 }
 
@@ -1139,6 +1160,8 @@ static void wiphy_update_regulatory(struct wiphy *wiphy,
        if (ignore_reg_update(wiphy, initiator))
                return;
 
+       last_request->dfs_region = cfg80211_regdomain->dfs_region;
+
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                if (wiphy->bands[band])
                        handle_band(wiphy, band, initiator);
@@ -1161,9 +1184,21 @@ void regulatory_update(struct wiphy *wiphy,
 static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
 {
        struct cfg80211_registered_device *rdev;
+       struct wiphy *wiphy;
 
-       list_for_each_entry(rdev, &cfg80211_rdev_list, list)
-               wiphy_update_regulatory(&rdev->wiphy, initiator);
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               wiphy = &rdev->wiphy;
+               wiphy_update_regulatory(wiphy, initiator);
+               /*
+                * Regulatory updates set by CORE are ignored for custom
+                * regulatory cards. Let us notify the changes to the driver,
+                * as some drivers used this to restore its orig_* reg domain.
+                */
+               if (initiator == NL80211_REGDOM_SET_BY_CORE &&
+                   wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
+                   wiphy->reg_notifier)
+                       wiphy->reg_notifier(wiphy, last_request);
+       }
 }
 
 static void handle_channel_custom(struct wiphy *wiphy,
@@ -1454,18 +1489,18 @@ new_request:
 }
 
 /* This processes *all* regulatory hints */
-static void reg_process_hint(struct regulatory_request *reg_request)
+static void reg_process_hint(struct regulatory_request *reg_request,
+                            enum nl80211_reg_initiator reg_initiator)
 {
        int r = 0;
        struct wiphy *wiphy = NULL;
-       enum nl80211_reg_initiator initiator = reg_request->initiator;
 
        BUG_ON(!reg_request->alpha2);
 
        if (wiphy_idx_valid(reg_request->wiphy_idx))
                wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
 
-       if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+       if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER &&
            !wiphy) {
                kfree(reg_request);
                return;
@@ -1475,7 +1510,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
        /* This is required so that the orig_* parameters are saved */
        if (r == -EALREADY && wiphy &&
            wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
-               wiphy_update_regulatory(wiphy, initiator);
+               wiphy_update_regulatory(wiphy, reg_initiator);
                return;
        }
 
@@ -1484,7 +1519,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
         * source of bogus requests.
         */
        if (r != -EALREADY &&
-           reg_request->initiator == NL80211_REGDOM_SET_BY_USER)
+           reg_initiator == NL80211_REGDOM_SET_BY_USER)
                schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
 }
 
@@ -1521,7 +1556,7 @@ static void reg_process_pending_hints(void)
 
        spin_unlock(&reg_requests_lock);
 
-       reg_process_hint(reg_request);
+       reg_process_hint(reg_request, reg_request->initiator);
 
 out:
        mutex_unlock(&reg_mutex);
@@ -1766,6 +1801,26 @@ static void restore_alpha2(char *alpha2, bool reset_user)
                REG_DBG_PRINT("Restoring regulatory settings\n");
 }
 
+static void restore_custom_reg_settings(struct wiphy *wiphy)
+{
+       struct ieee80211_supported_band *sband;
+       enum ieee80211_band band;
+       struct ieee80211_channel *chan;
+       int i;
+
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               sband = wiphy->bands[band];
+               if (!sband)
+                       continue;
+               for (i = 0; i < sband->n_channels; i++) {
+                       chan = &sband->channels[i];
+                       chan->flags = chan->orig_flags;
+                       chan->max_antenna_gain = chan->orig_mag;
+                       chan->max_power = chan->orig_mpwr;
+               }
+       }
+}
+
 /*
  * Restoring regulatory settings involves ingoring any
  * possibly stale country IE information and user regulatory
@@ -1784,9 +1839,11 @@ static void restore_alpha2(char *alpha2, bool reset_user)
 static void restore_regulatory_settings(bool reset_user)
 {
        char alpha2[2];
+       char world_alpha2[2];
        struct reg_beacon *reg_beacon, *btmp;
        struct regulatory_request *reg_request, *tmp;
        LIST_HEAD(tmp_reg_req_list);
+       struct cfg80211_registered_device *rdev;
 
        mutex_lock(&cfg80211_mutex);
        mutex_lock(&reg_mutex);
@@ -1834,11 +1891,18 @@ static void restore_regulatory_settings(bool reset_user)
 
        /* First restore to the basic regulatory settings */
        cfg80211_regdomain = cfg80211_world_regdom;
+       world_alpha2[0] = cfg80211_regdomain->alpha2[0];
+       world_alpha2[1] = cfg80211_regdomain->alpha2[1];
+
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY)
+                       restore_custom_reg_settings(&rdev->wiphy);
+       }
 
        mutex_unlock(&reg_mutex);
        mutex_unlock(&cfg80211_mutex);
 
-       regulatory_hint_core(cfg80211_regdomain->alpha2);
+       regulatory_hint_core(world_alpha2);
 
        /*
         * This restores the ieee80211_regdom module parameter
@@ -1935,7 +1999,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
        const struct ieee80211_freq_range *freq_range = NULL;
        const struct ieee80211_power_rule *power_rule = NULL;
 
-       pr_info("    (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n");
+       pr_info("  (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n");
 
        for (i = 0; i < rd->n_reg_rules; i++) {
                reg_rule = &rd->reg_rules[i];
@@ -1947,14 +2011,14 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
                 * in certain regions
                 */
                if (power_rule->max_antenna_gain)
-                       pr_info("    (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n",
+                       pr_info("  (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n",
                                freq_range->start_freq_khz,
                                freq_range->end_freq_khz,
                                freq_range->max_bandwidth_khz,
                                power_rule->max_antenna_gain,
                                power_rule->max_eirp);
                else
-                       pr_info("    (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n",
+                       pr_info("  (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n",
                                freq_range->start_freq_khz,
                                freq_range->end_freq_khz,
                                freq_range->max_bandwidth_khz,
@@ -1962,6 +2026,42 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
        }
 }
 
+bool reg_supported_dfs_region(u8 dfs_region)
+{
+       switch (dfs_region) {
+       case NL80211_DFS_UNSET:
+       case NL80211_DFS_FCC:
+       case NL80211_DFS_ETSI:
+       case NL80211_DFS_JP:
+               return true;
+       default:
+               REG_DBG_PRINT("Ignoring uknown DFS master region: %d\n",
+                             dfs_region);
+               return false;
+       }
+}
+
+static void print_dfs_region(u8 dfs_region)
+{
+       if (!dfs_region)
+               return;
+
+       switch (dfs_region) {
+       case NL80211_DFS_FCC:
+               pr_info(" DFS Master region FCC");
+               break;
+       case NL80211_DFS_ETSI:
+               pr_info(" DFS Master region ETSI");
+               break;
+       case NL80211_DFS_JP:
+               pr_info(" DFS Master region JP");
+               break;
+       default:
+               pr_info(" DFS Master region Uknown");
+               break;
+       }
+}
+
 static void print_regdomain(const struct ieee80211_regdomain *rd)
 {
 
@@ -1989,6 +2089,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
                        pr_info("Regulatory domain changed to country: %c%c\n",
                                rd->alpha2[0], rd->alpha2[1]);
        }
+       print_dfs_region(rd->dfs_region);
        print_rd_rules(rd);
 }