diff options
| author | Ilan Peer <ilan.peer@intel.com> | 2014-02-25 09:26:00 -0500 |
|---|---|---|
| committer | Johannes Berg <johannes.berg@intel.com> | 2014-04-09 04:55:35 -0400 |
| commit | 52616f2b446eaad8eb2cd78bbd052f0066069757 (patch) | |
| tree | e410fdc5238dc4d1aec48035ecef7ff51f579730 | |
| parent | 174e0cd28af0fe3c6c634c3e4d9e042c683bd7f7 (diff) | |
cfg80211: Add an option to hint indoor operation
Add the option to hint the wireless core that it is operating in an indoor
environment.
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
| -rw-r--r-- | include/uapi/linux/nl80211.h | 3 | ||||
| -rw-r--r-- | net/wireless/nl80211.c | 18 | ||||
| -rw-r--r-- | net/wireless/reg.c | 58 | ||||
| -rw-r--r-- | net/wireless/reg.h | 1 |
4 files changed, 68 insertions, 12 deletions
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index ac5b2d25f0fc..513bfd7b2e5f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h | |||
| @@ -2602,10 +2602,13 @@ enum nl80211_dfs_regions { | |||
| 2602 | * present has been registered with the wireless core that | 2602 | * present has been registered with the wireless core that |
| 2603 | * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a | 2603 | * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a |
| 2604 | * supported feature. | 2604 | * supported feature. |
| 2605 | * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the | ||
| 2606 | * platform is operating in an indoor environment. | ||
| 2605 | */ | 2607 | */ |
| 2606 | enum nl80211_user_reg_hint_type { | 2608 | enum nl80211_user_reg_hint_type { |
| 2607 | NL80211_USER_REG_HINT_USER = 0, | 2609 | NL80211_USER_REG_HINT_USER = 0, |
| 2608 | NL80211_USER_REG_HINT_CELL_BASE = 1, | 2610 | NL80211_USER_REG_HINT_CELL_BASE = 1, |
| 2611 | NL80211_USER_REG_HINT_INDOOR = 2, | ||
| 2609 | }; | 2612 | }; |
| 2610 | 2613 | ||
| 2611 | /** | 2614 | /** |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b8d81e41b0f7..85bc830fd7e3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
| @@ -4677,7 +4677,6 @@ static int parse_reg_rule(struct nlattr *tb[], | |||
| 4677 | 4677 | ||
| 4678 | static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | 4678 | static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) |
| 4679 | { | 4679 | { |
| 4680 | int r; | ||
| 4681 | char *data = NULL; | 4680 | char *data = NULL; |
| 4682 | enum nl80211_user_reg_hint_type user_reg_hint_type; | 4681 | enum nl80211_user_reg_hint_type user_reg_hint_type; |
| 4683 | 4682 | ||
| @@ -4690,11 +4689,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
| 4690 | if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) | 4689 | if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) |
| 4691 | return -EINPROGRESS; | 4690 | return -EINPROGRESS; |
| 4692 | 4691 | ||
| 4693 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | ||
| 4694 | return -EINVAL; | ||
| 4695 | |||
| 4696 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | ||
| 4697 | |||
| 4698 | if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]) | 4692 | if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]) |
| 4699 | user_reg_hint_type = | 4693 | user_reg_hint_type = |
| 4700 | nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]); | 4694 | nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]); |
| @@ -4704,14 +4698,16 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
| 4704 | switch (user_reg_hint_type) { | 4698 | switch (user_reg_hint_type) { |
| 4705 | case NL80211_USER_REG_HINT_USER: | 4699 | case NL80211_USER_REG_HINT_USER: |
| 4706 | case NL80211_USER_REG_HINT_CELL_BASE: | 4700 | case NL80211_USER_REG_HINT_CELL_BASE: |
| 4707 | break; | 4701 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) |
| 4702 | return -EINVAL; | ||
| 4703 | |||
| 4704 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | ||
| 4705 | return regulatory_hint_user(data, user_reg_hint_type); | ||
| 4706 | case NL80211_USER_REG_HINT_INDOOR: | ||
| 4707 | return regulatory_hint_indoor_user(); | ||
| 4708 | default: | 4708 | default: |
| 4709 | return -EINVAL; | 4709 | return -EINVAL; |
| 4710 | } | 4710 | } |
| 4711 | |||
| 4712 | r = regulatory_hint_user(data, user_reg_hint_type); | ||
| 4713 | |||
| 4714 | return r; | ||
| 4715 | } | 4711 | } |
| 4716 | 4712 | ||
| 4717 | static int nl80211_get_mesh_config(struct sk_buff *skb, | 4713 | static int nl80211_get_mesh_config(struct sk_buff *skb, |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 58f48b8f42ae..55d68c31ad72 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
| @@ -65,11 +65,26 @@ | |||
| 65 | #define REG_DBG_PRINT(args...) | 65 | #define REG_DBG_PRINT(args...) |
| 66 | #endif | 66 | #endif |
| 67 | 67 | ||
| 68 | /** | ||
| 69 | * enum reg_request_treatment - regulatory request treatment | ||
| 70 | * | ||
| 71 | * @REG_REQ_OK: continue processing the regulatory request | ||
| 72 | * @REG_REQ_IGNORE: ignore the regulatory request | ||
| 73 | * @REG_REQ_INTERSECT: the regulatory domain resulting from this request should | ||
| 74 | * be intersected with the current one. | ||
| 75 | * @REG_REQ_ALREADY_SET: the regulatory request will not change the current | ||
| 76 | * regulatory settings, and no further processing is required. | ||
| 77 | * @REG_REQ_USER_HINT_HANDLED: a non alpha2 user hint was handled and no | ||
| 78 | * further processing is required, i.e., not need to update last_request | ||
| 79 | * etc. This should be used for user hints that do not provide an alpha2 | ||
| 80 | * but some other type of regulatory hint, i.e., indoor operation. | ||
| 81 | */ | ||
| 68 | enum reg_request_treatment { | 82 | enum reg_request_treatment { |
| 69 | REG_REQ_OK, | 83 | REG_REQ_OK, |
| 70 | REG_REQ_IGNORE, | 84 | REG_REQ_IGNORE, |
| 71 | REG_REQ_INTERSECT, | 85 | REG_REQ_INTERSECT, |
| 72 | REG_REQ_ALREADY_SET, | 86 | REG_REQ_ALREADY_SET, |
| 87 | REG_REQ_USER_HINT_HANDLED, | ||
| 73 | }; | 88 | }; |
| 74 | 89 | ||
| 75 | static struct regulatory_request core_request_world = { | 90 | static struct regulatory_request core_request_world = { |
| @@ -106,6 +121,14 @@ const struct ieee80211_regdomain __rcu *cfg80211_regdomain; | |||
| 106 | */ | 121 | */ |
| 107 | static int reg_num_devs_support_basehint; | 122 | static int reg_num_devs_support_basehint; |
| 108 | 123 | ||
| 124 | /* | ||
| 125 | * State variable indicating if the platform on which the devices | ||
| 126 | * are attached is operating in an indoor environment. The state variable | ||
| 127 | * is relevant for all registered devices. | ||
| 128 | * (protected by RTNL) | ||
| 129 | */ | ||
| 130 | static bool reg_is_indoor; | ||
| 131 | |||
| 109 | static const struct ieee80211_regdomain *get_cfg80211_regdom(void) | 132 | static const struct ieee80211_regdomain *get_cfg80211_regdom(void) |
| 110 | { | 133 | { |
| 111 | return rtnl_dereference(cfg80211_regdomain); | 134 | return rtnl_dereference(cfg80211_regdomain); |
| @@ -1128,6 +1151,13 @@ static bool reg_request_cell_base(struct regulatory_request *request) | |||
| 1128 | return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; | 1151 | return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; |
| 1129 | } | 1152 | } |
| 1130 | 1153 | ||
| 1154 | static bool reg_request_indoor(struct regulatory_request *request) | ||
| 1155 | { | ||
| 1156 | if (request->initiator != NL80211_REGDOM_SET_BY_USER) | ||
| 1157 | return false; | ||
| 1158 | return request->user_reg_hint_type == NL80211_USER_REG_HINT_INDOOR; | ||
| 1159 | } | ||
| 1160 | |||
| 1131 | bool reg_last_request_cell_base(void) | 1161 | bool reg_last_request_cell_base(void) |
| 1132 | { | 1162 | { |
| 1133 | return reg_request_cell_base(get_last_request()); | 1163 | return reg_request_cell_base(get_last_request()); |
| @@ -1570,6 +1600,11 @@ __reg_process_hint_user(struct regulatory_request *user_request) | |||
| 1570 | { | 1600 | { |
| 1571 | struct regulatory_request *lr = get_last_request(); | 1601 | struct regulatory_request *lr = get_last_request(); |
| 1572 | 1602 | ||
| 1603 | if (reg_request_indoor(user_request)) { | ||
| 1604 | reg_is_indoor = true; | ||
| 1605 | return REG_REQ_USER_HINT_HANDLED; | ||
| 1606 | } | ||
| 1607 | |||
| 1573 | if (reg_request_cell_base(user_request)) | 1608 | if (reg_request_cell_base(user_request)) |
| 1574 | return reg_ignore_cell_hint(user_request); | 1609 | return reg_ignore_cell_hint(user_request); |
| 1575 | 1610 | ||
| @@ -1617,7 +1652,8 @@ reg_process_hint_user(struct regulatory_request *user_request) | |||
| 1617 | 1652 | ||
| 1618 | treatment = __reg_process_hint_user(user_request); | 1653 | treatment = __reg_process_hint_user(user_request); |
| 1619 | if (treatment == REG_REQ_IGNORE || | 1654 | if (treatment == REG_REQ_IGNORE || |
| 1620 | treatment == REG_REQ_ALREADY_SET) { | 1655 | treatment == REG_REQ_ALREADY_SET || |
| 1656 | treatment == REG_REQ_USER_HINT_HANDLED) { | ||
| 1621 | kfree(user_request); | 1657 | kfree(user_request); |
| 1622 | return treatment; | 1658 | return treatment; |
| 1623 | } | 1659 | } |
| @@ -1678,6 +1714,7 @@ reg_process_hint_driver(struct wiphy *wiphy, | |||
| 1678 | case REG_REQ_OK: | 1714 | case REG_REQ_OK: |
| 1679 | break; | 1715 | break; |
| 1680 | case REG_REQ_IGNORE: | 1716 | case REG_REQ_IGNORE: |
| 1717 | case REG_REQ_USER_HINT_HANDLED: | ||
| 1681 | kfree(driver_request); | 1718 | kfree(driver_request); |
| 1682 | return treatment; | 1719 | return treatment; |
| 1683 | case REG_REQ_INTERSECT: | 1720 | case REG_REQ_INTERSECT: |
| @@ -1777,6 +1814,7 @@ reg_process_hint_country_ie(struct wiphy *wiphy, | |||
| 1777 | case REG_REQ_OK: | 1814 | case REG_REQ_OK: |
| 1778 | break; | 1815 | break; |
| 1779 | case REG_REQ_IGNORE: | 1816 | case REG_REQ_IGNORE: |
| 1817 | case REG_REQ_USER_HINT_HANDLED: | ||
| 1780 | /* fall through */ | 1818 | /* fall through */ |
| 1781 | case REG_REQ_ALREADY_SET: | 1819 | case REG_REQ_ALREADY_SET: |
| 1782 | kfree(country_ie_request); | 1820 | kfree(country_ie_request); |
| @@ -1969,6 +2007,22 @@ int regulatory_hint_user(const char *alpha2, | |||
| 1969 | return 0; | 2007 | return 0; |
| 1970 | } | 2008 | } |
| 1971 | 2009 | ||
| 2010 | int regulatory_hint_indoor_user(void) | ||
| 2011 | { | ||
| 2012 | struct regulatory_request *request; | ||
| 2013 | |||
| 2014 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); | ||
| 2015 | if (!request) | ||
| 2016 | return -ENOMEM; | ||
| 2017 | |||
| 2018 | request->wiphy_idx = WIPHY_IDX_INVALID; | ||
| 2019 | request->initiator = NL80211_REGDOM_SET_BY_USER; | ||
| 2020 | request->user_reg_hint_type = NL80211_USER_REG_HINT_INDOOR; | ||
| 2021 | queue_regulatory_request(request); | ||
| 2022 | |||
| 2023 | return 0; | ||
| 2024 | } | ||
| 2025 | |||
| 1972 | /* Driver hints */ | 2026 | /* Driver hints */ |
| 1973 | int regulatory_hint(struct wiphy *wiphy, const char *alpha2) | 2027 | int regulatory_hint(struct wiphy *wiphy, const char *alpha2) |
| 1974 | { | 2028 | { |
| @@ -2136,6 +2190,8 @@ static void restore_regulatory_settings(bool reset_user) | |||
| 2136 | 2190 | ||
| 2137 | ASSERT_RTNL(); | 2191 | ASSERT_RTNL(); |
| 2138 | 2192 | ||
| 2193 | reg_is_indoor = false; | ||
| 2194 | |||
| 2139 | reset_regdomains(true, &world_regdom); | 2195 | reset_regdomains(true, &world_regdom); |
| 2140 | restore_alpha2(alpha2, reset_user); | 2196 | restore_alpha2(alpha2, reset_user); |
| 2141 | 2197 | ||
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 334a53af0fc8..2a3842828f6d 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
| @@ -25,6 +25,7 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy); | |||
| 25 | 25 | ||
| 26 | int regulatory_hint_user(const char *alpha2, | 26 | int regulatory_hint_user(const char *alpha2, |
| 27 | enum nl80211_user_reg_hint_type user_reg_hint_type); | 27 | enum nl80211_user_reg_hint_type user_reg_hint_type); |
| 28 | int regulatory_hint_indoor_user(void); | ||
| 28 | 29 | ||
| 29 | void wiphy_regulatory_register(struct wiphy *wiphy); | 30 | void wiphy_regulatory_register(struct wiphy *wiphy); |
| 30 | void wiphy_regulatory_deregister(struct wiphy *wiphy); | 31 | void wiphy_regulatory_deregister(struct wiphy *wiphy); |
