aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorLuis R. Rodriguez <mcgrof@qca.qualcomm.com>2012-07-12 14:49:18 -0400
committerJohannes Berg <johannes.berg@intel.com>2012-07-17 06:16:39 -0400
commit57b5ce072e7361218a8e2ea1d62960cbb71d9cff (patch)
treec00265b8f2123f860978d293fa122427ea2139f8 /net/wireless
parentb594bab9021f5225a24bcb69d7f7b7272419adb2 (diff)
cfg80211: add cellular base station regulatory hint support
Cellular base stations can provide hints to cfg80211 about where they think we are. This can be done for example on a cell phone. To enable these hints we simply allow them through as user regulatory hints but we allow userspace to clasify the hint as either coming directly from the user or coming from a cellular base station. This option is only available when you enable CONFIG_CFG80211_CERTIFICATION_ONUS. The base station hints themselves will not be processed by the core unless at least one device on the system supports this feature. Signed-off-by: Luis R. Rodriguez <mcgrof@qca.qualcomm.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-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
4 files changed, 134 insertions, 8 deletions
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