Commit 52616f2b authored by Ilan Peer's avatar Ilan Peer Committed by Johannes Berg

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: default avatarIlan Peer <ilan.peer@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 174e0cd2
...@@ -2602,10 +2602,13 @@ enum nl80211_dfs_regions { ...@@ -2602,10 +2602,13 @@ enum nl80211_dfs_regions {
* present has been registered with the wireless core that * present has been registered with the wireless core that
* has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
* supported feature. * supported feature.
* @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the
* platform is operating in an indoor environment.
*/ */
enum nl80211_user_reg_hint_type { enum nl80211_user_reg_hint_type {
NL80211_USER_REG_HINT_USER = 0, NL80211_USER_REG_HINT_USER = 0,
NL80211_USER_REG_HINT_CELL_BASE = 1, NL80211_USER_REG_HINT_CELL_BASE = 1,
NL80211_USER_REG_HINT_INDOOR = 2,
}; };
/** /**
......
...@@ -4677,7 +4677,6 @@ static int parse_reg_rule(struct nlattr *tb[], ...@@ -4677,7 +4677,6 @@ static int parse_reg_rule(struct nlattr *tb[],
static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
{ {
int r;
char *data = NULL; char *data = NULL;
enum nl80211_user_reg_hint_type user_reg_hint_type; enum nl80211_user_reg_hint_type user_reg_hint_type;
...@@ -4690,11 +4689,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) ...@@ -4690,11 +4689,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))
return -EINPROGRESS; return -EINPROGRESS;
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
return -EINVAL;
data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]) if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE])
user_reg_hint_type = user_reg_hint_type =
nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]); 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,14 +4698,16 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
switch (user_reg_hint_type) { switch (user_reg_hint_type) {
case NL80211_USER_REG_HINT_USER: case NL80211_USER_REG_HINT_USER:
case NL80211_USER_REG_HINT_CELL_BASE: case NL80211_USER_REG_HINT_CELL_BASE:
break; if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
return -EINVAL;
data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
return regulatory_hint_user(data, user_reg_hint_type);
case NL80211_USER_REG_HINT_INDOOR:
return regulatory_hint_indoor_user();
default: default:
return -EINVAL; return -EINVAL;
} }
r = regulatory_hint_user(data, user_reg_hint_type);
return r;
} }
static int nl80211_get_mesh_config(struct sk_buff *skb, static int nl80211_get_mesh_config(struct sk_buff *skb,
......
...@@ -65,11 +65,26 @@ ...@@ -65,11 +65,26 @@
#define REG_DBG_PRINT(args...) #define REG_DBG_PRINT(args...)
#endif #endif
/**
* enum reg_request_treatment - regulatory request treatment
*
* @REG_REQ_OK: continue processing the regulatory request
* @REG_REQ_IGNORE: ignore the regulatory request
* @REG_REQ_INTERSECT: the regulatory domain resulting from this request should
* be intersected with the current one.
* @REG_REQ_ALREADY_SET: the regulatory request will not change the current
* regulatory settings, and no further processing is required.
* @REG_REQ_USER_HINT_HANDLED: a non alpha2 user hint was handled and no
* further processing is required, i.e., not need to update last_request
* etc. This should be used for user hints that do not provide an alpha2
* but some other type of regulatory hint, i.e., indoor operation.
*/
enum reg_request_treatment { enum reg_request_treatment {
REG_REQ_OK, REG_REQ_OK,
REG_REQ_IGNORE, REG_REQ_IGNORE,
REG_REQ_INTERSECT, REG_REQ_INTERSECT,
REG_REQ_ALREADY_SET, REG_REQ_ALREADY_SET,
REG_REQ_USER_HINT_HANDLED,
}; };
static struct regulatory_request core_request_world = { static struct regulatory_request core_request_world = {
...@@ -106,6 +121,14 @@ const struct ieee80211_regdomain __rcu *cfg80211_regdomain; ...@@ -106,6 +121,14 @@ const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
*/ */
static int reg_num_devs_support_basehint; static int reg_num_devs_support_basehint;
/*
* State variable indicating if the platform on which the devices
* are attached is operating in an indoor environment. The state variable
* is relevant for all registered devices.
* (protected by RTNL)
*/
static bool reg_is_indoor;
static const struct ieee80211_regdomain *get_cfg80211_regdom(void) static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
{ {
return rtnl_dereference(cfg80211_regdomain); return rtnl_dereference(cfg80211_regdomain);
...@@ -1128,6 +1151,13 @@ static bool reg_request_cell_base(struct regulatory_request *request) ...@@ -1128,6 +1151,13 @@ static bool reg_request_cell_base(struct regulatory_request *request)
return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE;
} }
static bool reg_request_indoor(struct regulatory_request *request)
{
if (request->initiator != NL80211_REGDOM_SET_BY_USER)
return false;
return request->user_reg_hint_type == NL80211_USER_REG_HINT_INDOOR;
}
bool reg_last_request_cell_base(void) bool reg_last_request_cell_base(void)
{ {
return reg_request_cell_base(get_last_request()); return reg_request_cell_base(get_last_request());
...@@ -1570,6 +1600,11 @@ __reg_process_hint_user(struct regulatory_request *user_request) ...@@ -1570,6 +1600,11 @@ __reg_process_hint_user(struct regulatory_request *user_request)
{ {
struct regulatory_request *lr = get_last_request(); struct regulatory_request *lr = get_last_request();
if (reg_request_indoor(user_request)) {
reg_is_indoor = true;
return REG_REQ_USER_HINT_HANDLED;
}
if (reg_request_cell_base(user_request)) if (reg_request_cell_base(user_request))
return reg_ignore_cell_hint(user_request); return reg_ignore_cell_hint(user_request);
...@@ -1617,7 +1652,8 @@ reg_process_hint_user(struct regulatory_request *user_request) ...@@ -1617,7 +1652,8 @@ reg_process_hint_user(struct regulatory_request *user_request)
treatment = __reg_process_hint_user(user_request); treatment = __reg_process_hint_user(user_request);
if (treatment == REG_REQ_IGNORE || if (treatment == REG_REQ_IGNORE ||
treatment == REG_REQ_ALREADY_SET) { treatment == REG_REQ_ALREADY_SET ||
treatment == REG_REQ_USER_HINT_HANDLED) {
kfree(user_request); kfree(user_request);
return treatment; return treatment;
} }
...@@ -1678,6 +1714,7 @@ reg_process_hint_driver(struct wiphy *wiphy, ...@@ -1678,6 +1714,7 @@ reg_process_hint_driver(struct wiphy *wiphy,
case REG_REQ_OK: case REG_REQ_OK:
break; break;
case REG_REQ_IGNORE: case REG_REQ_IGNORE:
case REG_REQ_USER_HINT_HANDLED:
kfree(driver_request); kfree(driver_request);
return treatment; return treatment;
case REG_REQ_INTERSECT: case REG_REQ_INTERSECT:
...@@ -1777,6 +1814,7 @@ reg_process_hint_country_ie(struct wiphy *wiphy, ...@@ -1777,6 +1814,7 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
case REG_REQ_OK: case REG_REQ_OK:
break; break;
case REG_REQ_IGNORE: case REG_REQ_IGNORE:
case REG_REQ_USER_HINT_HANDLED:
/* fall through */ /* fall through */
case REG_REQ_ALREADY_SET: case REG_REQ_ALREADY_SET:
kfree(country_ie_request); kfree(country_ie_request);
...@@ -1969,6 +2007,22 @@ int regulatory_hint_user(const char *alpha2, ...@@ -1969,6 +2007,22 @@ int regulatory_hint_user(const char *alpha2,
return 0; return 0;
} }
int regulatory_hint_indoor_user(void)
{
struct regulatory_request *request;
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
if (!request)
return -ENOMEM;
request->wiphy_idx = WIPHY_IDX_INVALID;
request->initiator = NL80211_REGDOM_SET_BY_USER;
request->user_reg_hint_type = NL80211_USER_REG_HINT_INDOOR;
queue_regulatory_request(request);
return 0;
}
/* Driver hints */ /* Driver hints */
int regulatory_hint(struct wiphy *wiphy, const char *alpha2) int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
{ {
...@@ -2136,6 +2190,8 @@ static void restore_regulatory_settings(bool reset_user) ...@@ -2136,6 +2190,8 @@ static void restore_regulatory_settings(bool reset_user)
ASSERT_RTNL(); ASSERT_RTNL();
reg_is_indoor = false;
reset_regdomains(true, &world_regdom); reset_regdomains(true, &world_regdom);
restore_alpha2(alpha2, reset_user); restore_alpha2(alpha2, reset_user);
......
...@@ -25,6 +25,7 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy); ...@@ -25,6 +25,7 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);
int regulatory_hint_user(const char *alpha2, int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type); enum nl80211_user_reg_hint_type user_reg_hint_type);
int regulatory_hint_indoor_user(void);
void wiphy_regulatory_register(struct wiphy *wiphy); void wiphy_regulatory_register(struct wiphy *wiphy);
void wiphy_regulatory_deregister(struct wiphy *wiphy); void wiphy_regulatory_deregister(struct wiphy *wiphy);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment