cfg80211: process regulatory DFS region for countries
authorLuis R. Rodriguez <mcgrof@qca.qualcomm.com>
Tue, 11 Oct 2011 17:59:02 +0000 (10:59 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 21 Nov 2011 21:20:41 +0000 (16:20 -0500)
The wireless-regdb now has support for mapping a country to
one DFS region. CRDA sends this to us now so process it
so we can provide that hint to drivers. This will later be
used by code for processing DFS in a way that meets the
criteria for the DFS region the country belongs to.

Signed-off-by: Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/linux/nl80211.h
include/net/regulatory.h
net/wireless/nl80211.c
net/wireless/reg.c
net/wireless/reg.h

index f9261c253735b362f22769f38aa4519f984118c3..6396819a7e41d0df138e05101c1f76e4947183ae 100644 (file)
@@ -1170,6 +1170,10 @@ enum nl80211_commands {
  *     probe-response frame. The DA field in the 802.11 header is zero-ed out,
  *     to be filled by the FW.
  *
+ * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
+ *    abides to when initiating radiation on DFS channels. A country maps
+ *    to one DFS region.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1408,6 +1412,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_PROBE_RESP,
 
+       NL80211_ATTR_DFS_REGION,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -1916,6 +1922,21 @@ enum nl80211_reg_rule_flags {
        NL80211_RRF_NO_IBSS             = 1<<8,
 };
 
+/**
+ * enum nl80211_dfs_regions - regulatory DFS regions
+ *
+ * @NL80211_DFS_UNSET: Country has no DFS master region specified
+ * @NL80211_DFS_FCC_: Country follows DFS master rules from FCC
+ * @NL80211_DFS_FCC_: Country follows DFS master rules from ETSI
+ * @NL80211_DFS_JP_: Country follows DFS master rules from JP/MKK/Telec
+ */
+enum nl80211_dfs_regions {
+       NL80211_DFS_UNSET       = 0,
+       NL80211_DFS_FCC         = 1,
+       NL80211_DFS_ETSI        = 2,
+       NL80211_DFS_JP          = 3,
+};
+
 /**
  * enum nl80211_survey_info - survey information
  *
index eb7d3c2d427480d8c9bb77723386681cdc6c1b35..7399c93cb4bc9ee61cd7b667a8c6579d5b17cd67 100644 (file)
@@ -93,6 +93,7 @@ struct ieee80211_reg_rule {
 struct ieee80211_regdomain {
        u32 n_reg_rules;
        char alpha2[2];
+       u8 dfs_region;
        struct ieee80211_reg_rule reg_rules[];
 };
 
index 6bc7c4b32fa5786196dd39e5755d2dd3282e25a0..50482e129263f746891211d2d4ff5540d7e8979c 100644 (file)
@@ -199,6 +199,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
        [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
                                      .len = IEEE80211_MAX_DATA_LEN },
+       [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
 };
 
 /* policy for the key attributes */
@@ -3382,6 +3383,9 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
 
        NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
                cfg80211_regdomain->alpha2);
+       if (cfg80211_regdomain->dfs_region)
+               NLA_PUT_U8(msg, NL80211_ATTR_DFS_REGION,
+                          cfg80211_regdomain->dfs_region);
 
        nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
        if (!nl_reg_rules)
@@ -3440,6 +3444,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        char *alpha2 = NULL;
        int rem_reg_rules = 0, r = 0;
        u32 num_rules = 0, rule_idx = 0, size_of_regd;
+       u8 dfs_region = 0;
        struct ieee80211_regdomain *rd = NULL;
 
        if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
@@ -3450,6 +3455,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 
        alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
 
+       if (info->attrs[NL80211_ATTR_DFS_REGION])
+               dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
+
        nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
                        rem_reg_rules) {
                num_rules++;
@@ -3477,6 +3485,13 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        rd->alpha2[0] = alpha2[0];
        rd->alpha2[1] = alpha2[1];
 
+       /*
+        * Disable DFS master mode if the DFS region was
+        * not supported or known on this kernel.
+        */
+       if (reg_supported_dfs_region(dfs_region))
+               rd->dfs_region = dfs_region;
+
        nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
                        rem_reg_rules) {
                nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
index 2520a1b7e7db7947754dafe3c814405ea26798e7..69141ed1f6dfea2b89cad572ddabf564bfdb20c8 100644 (file)
@@ -1946,6 +1946,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)
 {
 
@@ -1973,6 +2009,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);
 }
 
index 4a56799d868d5fa7efc48ab8e21cbb67d3a8a473..786e414afd914805c7a90f16ee738106903ab9c9 100644 (file)
@@ -5,6 +5,7 @@ extern const struct ieee80211_regdomain *cfg80211_regdomain;
 
 bool is_world_regdom(const char *alpha2);
 bool reg_is_valid_request(const char *alpha2);
+bool reg_supported_dfs_region(u8 dfs_region);
 
 int regulatory_hint_user(const char *alpha2);