aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/nl80211.h32
-rw-r--r--include/net/regulatory.h5
-rw-r--r--net/wireless/core.c1
-rw-r--r--net/wireless/nl80211.c23
-rw-r--r--net/wireless/reg.c113
-rw-r--r--net/wireless/reg.h5
6 files changed, 171 insertions, 8 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index d6cfacc3ce4d..2f3878806403 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -1245,6 +1245,12 @@ enum nl80211_commands {
1245 * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds 1245 * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
1246 * or 0 to disable background scan. 1246 * or 0 to disable background scan.
1247 * 1247 *
1248 * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from
1249 * userspace. If unset it is assumed the hint comes directly from
1250 * a user. If set code could specify exactly what type of source
1251 * was used to provide the hint. For the different types of
1252 * allowed user regulatory hints see nl80211_user_reg_hint_type.
1253 *
1248 * @NL80211_ATTR_MAX: highest attribute number currently defined 1254 * @NL80211_ATTR_MAX: highest attribute number currently defined
1249 * @__NL80211_ATTR_AFTER_LAST: internal use 1255 * @__NL80211_ATTR_AFTER_LAST: internal use
1250 */ 1256 */
@@ -1498,6 +1504,8 @@ enum nl80211_attrs {
1498 1504
1499 NL80211_ATTR_WDEV, 1505 NL80211_ATTR_WDEV,
1500 1506
1507 NL80211_ATTR_USER_REG_HINT_TYPE,
1508
1501 /* add attributes here, update the policy in nl80211.c */ 1509 /* add attributes here, update the policy in nl80211.c */
1502 1510
1503 __NL80211_ATTR_AFTER_LAST, 1511 __NL80211_ATTR_AFTER_LAST,
@@ -2061,6 +2069,26 @@ enum nl80211_dfs_regions {
2061}; 2069};
2062 2070
2063/** 2071/**
2072 * enum nl80211_user_reg_hint_type - type of user regulatory hint
2073 *
2074 * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always
2075 * assumed if the attribute is not set.
2076 * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular
2077 * base station. Device drivers that have been tested to work
2078 * properly to support this type of hint can enable these hints
2079 * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature
2080 * capability on the struct wiphy. The wireless core will
2081 * ignore all cell base station hints until at least one device
2082 * present has been registered with the wireless core that
2083 * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
2084 * supported feature.
2085 */
2086enum nl80211_user_reg_hint_type {
2087 NL80211_USER_REG_HINT_USER = 0,
2088 NL80211_USER_REG_HINT_CELL_BASE = 1,
2089};
2090
2091/**
2064 * enum nl80211_survey_info - survey information 2092 * enum nl80211_survey_info - survey information
2065 * 2093 *
2066 * These attribute types are used with %NL80211_ATTR_SURVEY_INFO 2094 * These attribute types are used with %NL80211_ATTR_SURVEY_INFO
@@ -2963,11 +2991,15 @@ enum nl80211_ap_sme_features {
2963 * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. 2991 * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
2964 * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up 2992 * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
2965 * the connected inactive stations in AP mode. 2993 * the connected inactive stations in AP mode.
2994 * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
2995 * to work properly to suppport receiving regulatory hints from
2996 * cellular base stations.
2966 */ 2997 */
2967enum nl80211_feature_flags { 2998enum nl80211_feature_flags {
2968 NL80211_FEATURE_SK_TX_STATUS = 1 << 0, 2999 NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
2969 NL80211_FEATURE_HT_IBSS = 1 << 1, 3000 NL80211_FEATURE_HT_IBSS = 1 << 1,
2970 NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, 3001 NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2,
3002 NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3,
2971}; 3003};
2972 3004
2973/** 3005/**
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index a5f79933e211..7dcaa2794fde 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -52,6 +52,10 @@ enum environment_cap {
52 * DFS master operation on a known DFS region (NL80211_DFS_*), 52 * DFS master operation on a known DFS region (NL80211_DFS_*),
53 * dfs_region represents that region. Drivers can use this and the 53 * dfs_region represents that region. Drivers can use this and the
54 * @alpha2 to adjust their device's DFS parameters as required. 54 * @alpha2 to adjust their device's DFS parameters as required.
55 * @user_reg_hint_type: if the @initiator was of type
56 * %NL80211_REGDOM_SET_BY_USER, this classifies the type
57 * of hint passed. This could be any of the %NL80211_USER_REG_HINT_*
58 * types.
55 * @intersect: indicates whether the wireless core should intersect 59 * @intersect: indicates whether the wireless core should intersect
56 * the requested regulatory domain with the presently set regulatory 60 * the requested regulatory domain with the presently set regulatory
57 * domain. 61 * domain.
@@ -70,6 +74,7 @@ enum environment_cap {
70struct regulatory_request { 74struct regulatory_request {
71 int wiphy_idx; 75 int wiphy_idx;
72 enum nl80211_reg_initiator initiator; 76 enum nl80211_reg_initiator initiator;
77 enum nl80211_user_reg_hint_type user_reg_hint_type;
73 char alpha2[2]; 78 char alpha2[2];
74 u8 dfs_region; 79 u8 dfs_region;
75 bool intersect; 80 bool intersect;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 71b684b5a675..c0307b05986c 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -542,6 +542,7 @@ int wiphy_register(struct wiphy *wiphy)
542 } 542 }
543 543
544 /* set up regulatory info */ 544 /* set up regulatory info */
545 wiphy_regulatory_register(wiphy);
545 regulatory_update(wiphy, NL80211_REGDOM_SET_BY_CORE); 546 regulatory_update(wiphy, NL80211_REGDOM_SET_BY_CORE);
546 547
547 list_add_rcu(&rdev->list, &cfg80211_rdev_list); 548 list_add_rcu(&rdev->list, &cfg80211_rdev_list);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 9216e45e53a0..50b1a0e84f1a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -354,6 +354,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
354 [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, 354 [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
355 [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, 355 [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
356 [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, 356 [NL80211_ATTR_WDEV] = { .type = NLA_U64 },
357 [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
357}; 358};
358 359
359/* policy for the key attributes */ 360/* policy for the key attributes */
@@ -3582,6 +3583,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
3582{ 3583{
3583 int r; 3584 int r;
3584 char *data = NULL; 3585 char *data = NULL;
3586 enum nl80211_user_reg_hint_type user_reg_hint_type;
3585 3587
3586 /* 3588 /*
3587 * You should only get this when cfg80211 hasn't yet initialized 3589 * You should only get this when cfg80211 hasn't yet initialized
@@ -3601,7 +3603,21 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
3601 3603
3602 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); 3604 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3603 3605
3604 r = regulatory_hint_user(data); 3606 if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE])
3607 user_reg_hint_type =
3608 nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]);
3609 else
3610 user_reg_hint_type = NL80211_USER_REG_HINT_USER;
3611
3612 switch (user_reg_hint_type) {
3613 case NL80211_USER_REG_HINT_USER:
3614 case NL80211_USER_REG_HINT_CELL_BASE:
3615 break;
3616 default:
3617 return -EINVAL;
3618 }
3619
3620 r = regulatory_hint_user(data, user_reg_hint_type);
3605 3621
3606 return r; 3622 return r;
3607} 3623}
@@ -3971,6 +3987,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
3971 cfg80211_regdomain->dfs_region))) 3987 cfg80211_regdomain->dfs_region)))
3972 goto nla_put_failure; 3988 goto nla_put_failure;
3973 3989
3990 if (reg_last_request_cell_base() &&
3991 nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
3992 NL80211_USER_REG_HINT_CELL_BASE))
3993 goto nla_put_failure;
3994
3974 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); 3995 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
3975 if (!nl_reg_rules) 3996 if (!nl_reg_rules)
3976 goto nla_put_failure; 3997 goto nla_put_failure;
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index ad6f9029c564..83583a9c15d9 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -97,9 +97,16 @@ const struct ieee80211_regdomain *cfg80211_regdomain;
97 * - cfg80211_world_regdom 97 * - cfg80211_world_regdom
98 * - cfg80211_regdom 98 * - cfg80211_regdom
99 * - last_request 99 * - last_request
100 * - reg_num_devs_support_basehint
100 */ 101 */
101static DEFINE_MUTEX(reg_mutex); 102static DEFINE_MUTEX(reg_mutex);
102 103
104/*
105 * Number of devices that registered to the core
106 * that support cellular base station regulatory hints
107 */
108static int reg_num_devs_support_basehint;
109
103static inline void assert_reg_lock(void) 110static inline void assert_reg_lock(void)
104{ 111{
105 lockdep_assert_held(&reg_mutex); 112 lockdep_assert_held(&reg_mutex);
@@ -911,6 +918,59 @@ static void handle_band(struct wiphy *wiphy,
911 handle_channel(wiphy, initiator, band, i); 918 handle_channel(wiphy, initiator, band, i);
912} 919}
913 920
921static bool reg_request_cell_base(struct regulatory_request *request)
922{
923 if (request->initiator != NL80211_REGDOM_SET_BY_USER)
924 return false;
925 if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE)
926 return false;
927 return true;
928}
929
930bool reg_last_request_cell_base(void)
931{
932 assert_cfg80211_lock();
933
934 mutex_lock(&reg_mutex);
935 return reg_request_cell_base(last_request);
936 mutex_unlock(&reg_mutex);
937}
938
939#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
940
941/* Core specific check */
942static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
943{
944 if (!reg_num_devs_support_basehint)
945 return -EOPNOTSUPP;
946
947 if (reg_request_cell_base(last_request)) {
948 if (!regdom_changes(pending_request->alpha2))
949 return -EALREADY;
950 return 0;
951 }
952 return 0;
953}
954
955/* Device specific check */
956static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
957{
958 if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS))
959 return true;
960 return false;
961}
962#else
963static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
964{
965 return -EOPNOTSUPP;
966}
967static int reg_dev_ignore_cell_hint(struct wiphy *wiphy)
968{
969 return true;
970}
971#endif
972
973
914static bool ignore_reg_update(struct wiphy *wiphy, 974static bool ignore_reg_update(struct wiphy *wiphy,
915 enum nl80211_reg_initiator initiator) 975 enum nl80211_reg_initiator initiator)
916{ 976{
@@ -944,6 +1004,9 @@ static bool ignore_reg_update(struct wiphy *wiphy,
944 return true; 1004 return true;
945 } 1005 }
946 1006
1007 if (reg_request_cell_base(last_request))
1008 return reg_dev_ignore_cell_hint(wiphy);
1009
947 return false; 1010 return false;
948} 1011}
949 1012
@@ -1307,6 +1370,13 @@ static int ignore_request(struct wiphy *wiphy,
1307 return 0; 1370 return 0;
1308 case NL80211_REGDOM_SET_BY_COUNTRY_IE: 1371 case NL80211_REGDOM_SET_BY_COUNTRY_IE:
1309 1372
1373 if (reg_request_cell_base(last_request)) {
1374 /* Trust a Cell base station over the AP's country IE */
1375 if (regdom_changes(pending_request->alpha2))
1376 return -EOPNOTSUPP;
1377 return -EALREADY;
1378 }
1379
1310 last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); 1380 last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
1311 1381
1312 if (unlikely(!is_an_alpha2(pending_request->alpha2))) 1382 if (unlikely(!is_an_alpha2(pending_request->alpha2)))
@@ -1351,6 +1421,12 @@ static int ignore_request(struct wiphy *wiphy,
1351 1421
1352 return REG_INTERSECT; 1422 return REG_INTERSECT;
1353 case NL80211_REGDOM_SET_BY_USER: 1423 case NL80211_REGDOM_SET_BY_USER:
1424 if (reg_request_cell_base(pending_request))
1425 return reg_ignore_cell_hint(pending_request);
1426
1427 if (reg_request_cell_base(last_request))
1428 return -EOPNOTSUPP;
1429
1354 if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) 1430 if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
1355 return REG_INTERSECT; 1431 return REG_INTERSECT;
1356 /* 1432 /*
@@ -1640,7 +1716,8 @@ static int regulatory_hint_core(const char *alpha2)
1640} 1716}
1641 1717
1642/* User hints */ 1718/* User hints */
1643int regulatory_hint_user(const char *alpha2) 1719int regulatory_hint_user(const char *alpha2,
1720 enum nl80211_user_reg_hint_type user_reg_hint_type)
1644{ 1721{
1645 struct regulatory_request *request; 1722 struct regulatory_request *request;
1646 1723
@@ -1654,6 +1731,7 @@ int regulatory_hint_user(const char *alpha2)
1654 request->alpha2[0] = alpha2[0]; 1731 request->alpha2[0] = alpha2[0];
1655 request->alpha2[1] = alpha2[1]; 1732 request->alpha2[1] = alpha2[1];
1656 request->initiator = NL80211_REGDOM_SET_BY_USER; 1733 request->initiator = NL80211_REGDOM_SET_BY_USER;
1734 request->user_reg_hint_type = user_reg_hint_type;
1657 1735
1658 queue_regulatory_request(request); 1736 queue_regulatory_request(request);
1659 1737
@@ -1906,7 +1984,7 @@ static void restore_regulatory_settings(bool reset_user)
1906 * settings, user regulatory settings takes precedence. 1984 * settings, user regulatory settings takes precedence.
1907 */ 1985 */
1908 if (is_an_alpha2(alpha2)) 1986 if (is_an_alpha2(alpha2))
1909 regulatory_hint_user(user_alpha2); 1987 regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER);
1910 1988
1911 if (list_empty(&tmp_reg_req_list)) 1989 if (list_empty(&tmp_reg_req_list))
1912 return; 1990 return;
@@ -2081,9 +2159,16 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
2081 else { 2159 else {
2082 if (is_unknown_alpha2(rd->alpha2)) 2160 if (is_unknown_alpha2(rd->alpha2))
2083 pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); 2161 pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n");
2084 else 2162 else {
2085 pr_info("Regulatory domain changed to country: %c%c\n", 2163 if (reg_request_cell_base(last_request))
2086 rd->alpha2[0], rd->alpha2[1]); 2164 pr_info("Regulatory domain changed "
2165 "to country: %c%c by Cell Station\n",
2166 rd->alpha2[0], rd->alpha2[1]);
2167 else
2168 pr_info("Regulatory domain changed "
2169 "to country: %c%c\n",
2170 rd->alpha2[0], rd->alpha2[1]);
2171 }
2087 } 2172 }
2088 print_dfs_region(rd->dfs_region); 2173 print_dfs_region(rd->dfs_region);
2089 print_rd_rules(rd); 2174 print_rd_rules(rd);
@@ -2293,6 +2378,18 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
2293} 2378}
2294#endif /* CONFIG_HOTPLUG */ 2379#endif /* CONFIG_HOTPLUG */
2295 2380
2381void wiphy_regulatory_register(struct wiphy *wiphy)
2382{
2383 assert_cfg80211_lock();
2384
2385 mutex_lock(&reg_mutex);
2386
2387 if (!reg_dev_ignore_cell_hint(wiphy))
2388 reg_num_devs_support_basehint++;
2389
2390 mutex_unlock(&reg_mutex);
2391}
2392
2296/* Caller must hold cfg80211_mutex */ 2393/* Caller must hold cfg80211_mutex */
2297void reg_device_remove(struct wiphy *wiphy) 2394void reg_device_remove(struct wiphy *wiphy)
2298{ 2395{
@@ -2302,6 +2399,9 @@ void reg_device_remove(struct wiphy *wiphy)
2302 2399
2303 mutex_lock(&reg_mutex); 2400 mutex_lock(&reg_mutex);
2304 2401
2402 if (!reg_dev_ignore_cell_hint(wiphy))
2403 reg_num_devs_support_basehint--;
2404
2305 kfree(wiphy->regd); 2405 kfree(wiphy->regd);
2306 2406
2307 if (last_request) 2407 if (last_request)
@@ -2367,7 +2467,8 @@ int __init regulatory_init(void)
2367 * as a user hint. 2467 * as a user hint.
2368 */ 2468 */
2369 if (!is_world_regdom(ieee80211_regdom)) 2469 if (!is_world_regdom(ieee80211_regdom))
2370 regulatory_hint_user(ieee80211_regdom); 2470 regulatory_hint_user(ieee80211_regdom,
2471 NL80211_USER_REG_HINT_USER);
2371 2472
2372 return 0; 2473 return 0;
2373} 2474}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index e2aaaf525a22..519492fdda3c 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -22,9 +22,11 @@ bool is_world_regdom(const char *alpha2);
22bool reg_is_valid_request(const char *alpha2); 22bool reg_is_valid_request(const char *alpha2);
23bool reg_supported_dfs_region(u8 dfs_region); 23bool reg_supported_dfs_region(u8 dfs_region);
24 24
25int regulatory_hint_user(const char *alpha2); 25int regulatory_hint_user(const char *alpha2,
26 enum nl80211_user_reg_hint_type user_reg_hint_type);
26 27
27int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env); 28int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env);
29void wiphy_regulatory_register(struct wiphy *wiphy);
28void reg_device_remove(struct wiphy *wiphy); 30void reg_device_remove(struct wiphy *wiphy);
29 31
30int __init regulatory_init(void); 32int __init regulatory_init(void);
@@ -33,6 +35,7 @@ void regulatory_exit(void);
33int set_regdom(const struct ieee80211_regdomain *rd); 35int set_regdom(const struct ieee80211_regdomain *rd);
34 36
35void regulatory_update(struct wiphy *wiphy, enum nl80211_reg_initiator setby); 37void regulatory_update(struct wiphy *wiphy, enum nl80211_reg_initiator setby);
38bool reg_last_request_cell_base(void);
36 39
37/** 40/**
38 * regulatory_hint_found_beacon - hints a beacon was found on a channel 41 * regulatory_hint_found_beacon - hints a beacon was found on a channel