2015-01-06 10:50:12

by Peer, Ilan

[permalink] [raw]
Subject: [PATCH v2 1/2] cfg80211: Add API to change the indoor regulatory setting

As the operating environment of a device can change, add
API for user space to indicate a change of indoor settings.
In addition modify the handling of the indoor processing as
follows:

1. Directly update the indoor setting without wrapping it as a
regulatory request.
2. Track the socket on which the indoor hint is issued, and reset
indoor setting if the socket was released.
3. Do not reset the indoor setting when restoring the regulatory
settings as it has nothing to do with CRDA or interface
disconnection.

Signed-off-by: Ilan Peer <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
include/uapi/linux/nl80211.h | 5 +++
net/wireless/nl80211.c | 10 +++++-
net/wireless/reg.c | 80 +++++++++++++++++++++++++-------------------
net/wireless/reg.h | 15 ++++++++-
4 files changed, 73 insertions(+), 37 deletions(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 54f3911..6ef0a5a 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1713,6 +1713,9 @@ enum nl80211_commands {
* obtained from it is coming from the device's wiphy and not the global
* cfg80211 regdomain.
*
+ * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+ * is operating in an indoor environment.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2072,6 +2075,8 @@ enum nl80211_attrs {

NL80211_ATTR_WIPHY_SELF_MANAGED_REG,

+ NL80211_ATTR_REG_INDOOR,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 7029201..ca12a31 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -397,6 +397,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
[NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
[NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
+ [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
};

/* policy for the key attributes */
@@ -4923,6 +4924,7 @@ static int parse_reg_rule(struct nlattr *tb[],
static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
{
char *data = NULL;
+ bool is_indoor;
enum nl80211_user_reg_hint_type user_reg_hint_type;

/*
@@ -4949,7 +4951,8 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
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();
+ is_indoor = !!info->attrs[NL80211_ATTR_REG_INDOOR];
+ return regulatory_hint_indoor(is_indoor, info->snd_portid);
default:
return -EINVAL;
}
@@ -12669,6 +12672,11 @@ static int nl80211_netlink_notify(struct notifier_block * nb,

rcu_read_unlock();

+ /*
+ * It is possible that the user space process that is controlling the
+ * indoor setting disappeared, so notify the regulatory core.
+ */
+ regulatory_netlink_notify(notify->portid);
return NOTIFY_OK;
}

diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 9a5411c..6b0da81 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -82,17 +82,12 @@
* 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 {
REG_REQ_OK,
REG_REQ_IGNORE,
REG_REQ_INTERSECT,
REG_REQ_ALREADY_SET,
- REG_REQ_USER_HINT_HANDLED,
};

static struct regulatory_request core_request_world = {
@@ -133,9 +128,12 @@ 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 spinlock_t reg_indoor_lock;
+
+/* Used to track the userspace process controlling the indoor setting */
+static u32 reg_is_indoor_portid;

static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
{
@@ -1248,13 +1246,6 @@ static bool reg_request_cell_base(struct regulatory_request *request)
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)
{
return reg_request_cell_base(get_last_request());
@@ -1815,11 +1806,6 @@ __reg_process_hint_user(struct regulatory_request *user_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))
return reg_ignore_cell_hint(user_request);

@@ -1867,8 +1853,7 @@ reg_process_hint_user(struct regulatory_request *user_request)

treatment = __reg_process_hint_user(user_request);
if (treatment == REG_REQ_IGNORE ||
- treatment == REG_REQ_ALREADY_SET ||
- treatment == REG_REQ_USER_HINT_HANDLED) {
+ treatment == REG_REQ_ALREADY_SET) {
reg_free_request(user_request);
return treatment;
}
@@ -1929,7 +1914,6 @@ reg_process_hint_driver(struct wiphy *wiphy,
case REG_REQ_OK:
break;
case REG_REQ_IGNORE:
- case REG_REQ_USER_HINT_HANDLED:
reg_free_request(driver_request);
return treatment;
case REG_REQ_INTERSECT:
@@ -2029,7 +2013,6 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
case REG_REQ_OK:
break;
case REG_REQ_IGNORE:
- case REG_REQ_USER_HINT_HANDLED:
/* fall through */
case REG_REQ_ALREADY_SET:
reg_free_request(country_ie_request);
@@ -2068,8 +2051,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
case NL80211_REGDOM_SET_BY_USER:
treatment = reg_process_hint_user(reg_request);
if (treatment == REG_REQ_IGNORE ||
- treatment == REG_REQ_ALREADY_SET ||
- treatment == REG_REQ_USER_HINT_HANDLED)
+ treatment == REG_REQ_ALREADY_SET)
return;
queue_delayed_work(system_power_efficient_wq,
&reg_timeout, msecs_to_jiffies(3142));
@@ -2266,22 +2248,51 @@ int regulatory_hint_user(const char *alpha2,
return 0;
}

-int regulatory_hint_indoor_user(void)
+int regulatory_hint_indoor(bool is_indoor, u32 portid)
{
- struct regulatory_request *request;
+ spin_lock(&reg_indoor_lock);

- request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
- if (!request)
- return -ENOMEM;
+ /*
+ * Process only if there is a real change, so the original port ID is
+ * saved (to handle cases that several processes try to change the
+ * indoor setting).
+ */
+ if (reg_is_indoor == is_indoor) {
+ spin_unlock(&reg_indoor_lock);
+ return 0;
+ }

- 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);
+ reg_is_indoor = is_indoor;
+ if (reg_is_indoor)
+ reg_is_indoor_portid = portid;
+ else
+ reg_is_indoor_portid = 0;
+
+ spin_unlock(&reg_indoor_lock);
+
+ if (!is_indoor)
+ reg_check_channels();

return 0;
}

+void regulatory_netlink_notify(u32 portid)
+{
+ spin_lock(&reg_indoor_lock);
+
+ if (reg_is_indoor_portid != portid) {
+ spin_unlock(&reg_indoor_lock);
+ return;
+ }
+
+ reg_is_indoor = false;
+ reg_is_indoor_portid = 0;
+
+ spin_unlock(&reg_indoor_lock);
+
+ reg_check_channels();
+}
+
/* Driver hints */
int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
{
@@ -2449,8 +2460,6 @@ static void restore_regulatory_settings(bool reset_user)

ASSERT_RTNL();

- reg_is_indoor = false;
-
reset_regdomains(true, &world_regdom);
restore_alpha2(alpha2, reset_user);

@@ -3019,6 +3028,7 @@ int __init regulatory_init(void)

spin_lock_init(&reg_requests_lock);
spin_lock_init(&reg_pending_beacons_lock);
+ spin_lock_init(&reg_indoor_lock);

reg_regdb_size_check();

diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 4b45d6e..a2c4e16 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -25,7 +25,20 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);

int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type);
-int regulatory_hint_indoor_user(void);
+
+/**
+ * regulatory_hint_indoor - hint operation in indoor env. or not
+ * @is_indoor: if true indicates that user space thinks that the
+ * device is operating in an indoor environment.
+ * @portid: the netlink port ID on which the hint was given.
+ */
+int regulatory_hint_indoor(bool is_indoor, u32 portid);
+
+/**
+ * regulatory_netlink_notify - notify on released netlink socket
+ * @portid: the netlink socket port ID
+ */
+void regulatory_netlink_notify(u32 portid);

void wiphy_regulatory_register(struct wiphy *wiphy);
void wiphy_regulatory_deregister(struct wiphy *wiphy);
--
1.8.3.2



2015-01-06 11:28:09

by Peer, Ilan

[permalink] [raw]
Subject: RE: [PATCH v2 2/2] cfg80211: Schedule timeout for all CRDA calls

PiAtLS0tLU9yaWdpbmFsIE1lc3NhZ2UtLS0tLQ0KPiBGcm9tOiBKb2hhbm5lcyBCZXJnIFttYWls
dG86am9oYW5uZXNAc2lwc29sdXRpb25zLm5ldF0NCj4gU2VudDogVHVlc2RheSwgSmFudWFyeSAw
NiwgMjAxNSAxMjo1OQ0KPiBUbzogUGVlciwgSWxhbg0KPiBDYzogbGludXgtd2lyZWxlc3NAdmdl
ci5rZXJuZWwub3JnOyBtY2dyb2ZAc3VzZS5jb20NCj4gU3ViamVjdDogUmU6IFtQQVRDSCB2MiAy
LzJdIGNmZzgwMjExOiBTY2hlZHVsZSB0aW1lb3V0IGZvciBhbGwgQ1JEQSBjYWxscw0KPiANCj4g
T24gTW9uLCAyMDE1LTAxLTA1IGF0IDIzOjUyIC0wNTAwLCBJbGFuIFBlZXIgd3JvdGU6DQo+ID4g
VGltZW91dCB3YXMgc2NoZWR1bGVkIG9ubHkgaW4gY2FzZSBDUkRBIHdhcyBjYWxsZWQgZHVlIHRv
IHVzZXIgaGludHMsDQo+ID4gYnV0IHdhcyBub3Qgc2NoZWR1bGVkIGZvciBvdGhlciBjYXNlcy4g
VGhpcyBjYW4gcmVzdWx0IGluIHJlZ3VsYXRvcnkNCj4gPiBoaW50IHByb2Nlc3NpbmcgZ2V0dGlu
ZyBzdHVjayBpbiBjYXNlIHRoYXQgdGhlcmUgaXMgbm8gQ1JEQSBjb25maWd1cmVkLg0KPiANCj4g
SSBjYW4gdW5kZXJzdGFuZCB0aGlzIHBhcnQuDQo+IA0KPiA+IENoYW5nZSB0aGlzIGJ5IHNjaGVk
dWxpbmcgYSB0aW1lb3V0IGV2ZXJ5IHRpbWUgQ1JEQSBpcyBjYWxsZWQuIEluDQo+ID4gYWRkaXRp
b24sIGluIHJlc3RvcmVfcmVndWxhdG9yeV9zZXR0aW5ncygpIGFsbCBwZW5kaW5nIHJlcXVlc3Rz
IGFyZQ0KPiA+IHJlc3RvcmVkIChhbmQgbm90IG9ubHkgdGhlIHVzZXIgb25lcykuDQo+IA0KPiBD
YXJlIHRvIGV4cGxhaW4gd2h5LCBhbmQgd2h5IHRoaXMgc2hvdWxkIGJlIGluIHRoZSBzYW1lIHBh
dGNoPw0KDQpQcmV2aW91c2x5IG9ubHkgdGhlIHBlbmRpbmcgdXNlciBoaW50cyB3ZXJlIHJlc3Rv
cmVkIChub3Qgc3VyZSB3aHksIGJ1dCBtYXliZSB0aGVyZSB3YXMgYW4gYXNzdW1wdGlvbiB0aGF0
IHRoZSB0aW1lb3V0IHdhcyBvbmx5IHNjaGVkdWxlZCBmb3IgdXNlciBoaW50cz8pLiBXYXMgbm90
IHN1cmUgd2h5IGFsbCBvdGhlciBoaW50IHR5cGVzIHdlcmUgbm90IHJlc3RvcmVkLiBJIGNhbiBt
b3ZlIHRoaXMgdG8gYW5vdGhlciBwYXRjaCBpdCB0aGlzIGlzIHN0aWxsIHJlYXNvbmFibGUuDQoN
CklsYW4uDQo=

2015-01-06 10:50:13

by Peer, Ilan

[permalink] [raw]
Subject: [PATCH v2 2/2] cfg80211: Schedule timeout for all CRDA calls

Timeout was scheduled only in case CRDA was called due to user hints,
but was not scheduled for other cases. This can result in regulatory
hint processing getting stuck in case that there is no CRDA configured.

Change this by scheduling a timeout every time CRDA is called. In
addition, in restore_regulatory_settings() all pending requests are
restored (and not only the user ones).

Signed-off-by: Ilan Peer <[email protected]>
---
net/wireless/reg.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 6b0da81..fa71c34 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -552,6 +552,9 @@ reg_call_crda(struct regulatory_request *request)
{
if (call_crda(request->alpha2))
return REG_REQ_IGNORE;
+
+ queue_delayed_work(system_power_efficient_wq,
+ &reg_timeout, msecs_to_jiffies(3142));
return REG_REQ_OK;
}

@@ -1773,8 +1776,7 @@ static void reg_set_request_processed(void)
need_more_processing = true;
spin_unlock(&reg_requests_lock);

- if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
- cancel_delayed_work(&reg_timeout);
+ cancel_delayed_work(&reg_timeout);

if (need_more_processing)
schedule_work(&reg_work);
@@ -2053,8 +2055,6 @@ static void reg_process_hint(struct regulatory_request *reg_request)
if (treatment == REG_REQ_IGNORE ||
treatment == REG_REQ_ALREADY_SET)
return;
- queue_delayed_work(system_power_efficient_wq,
- &reg_timeout, msecs_to_jiffies(3142));
return;
case NL80211_REGDOM_SET_BY_DRIVER:
if (!wiphy)
@@ -2454,7 +2454,6 @@ static void restore_regulatory_settings(bool reset_user)
char alpha2[2];
char world_alpha2[2];
struct reg_beacon *reg_beacon, *btmp;
- struct regulatory_request *reg_request, *tmp;
LIST_HEAD(tmp_reg_req_list);
struct cfg80211_registered_device *rdev;

@@ -2470,11 +2469,7 @@ static void restore_regulatory_settings(bool reset_user)
* settings.
*/
spin_lock(&reg_requests_lock);
- list_for_each_entry_safe(reg_request, tmp, &reg_requests_list, list) {
- if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER)
- continue;
- list_move_tail(&reg_request->list, &tmp_reg_req_list);
- }
+ list_splice_tail_init(&reg_requests_list, &tmp_reg_req_list);
spin_unlock(&reg_requests_lock);

/* Clear beacon hints */
--
1.8.3.2


2015-01-06 10:59:24

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] cfg80211: Schedule timeout for all CRDA calls

On Mon, 2015-01-05 at 23:52 -0500, Ilan Peer wrote:
> Timeout was scheduled only in case CRDA was called due to user hints,
> but was not scheduled for other cases. This can result in regulatory
> hint processing getting stuck in case that there is no CRDA configured.

I can understand this part.

> Change this by scheduling a timeout every time CRDA is called. In
> addition, in restore_regulatory_settings() all pending requests are
> restored (and not only the user ones).

Care to explain why, and why this should be in the same patch?

johannes


2015-01-06 13:08:17

by Peer, Ilan

[permalink] [raw]
Subject: [PATCH v3 1/2] cfg80211: Add API to change the indoor regulatory setting

As the operating environment of a device can change, add
API for user space to indicate a change of indoor settings.
In addition modify the handling of the indoor processing as
follows:

1. Directly update the indoor setting without wrapping it as a
regulatory request.
2. Track the socket on which the indoor hint is issued, and reset
indoor setting if the socket was released. The motivation here is to
force a user space process that sets the indoor setting to constantly
monitor this setting, and be responsible to correctly toggling it,
so indoor operation will not be enabled uncontrolled.
3. Do not reset the indoor setting when restoring the regulatory
settings as it has nothing to do with CRDA or interface
disconnection.

Signed-off-by: Ilan Peer <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
include/uapi/linux/nl80211.h | 5 +++
net/wireless/nl80211.c | 10 +++++-
net/wireless/reg.c | 80 +++++++++++++++++++++++++-------------------
net/wireless/reg.h | 15 ++++++++-
4 files changed, 73 insertions(+), 37 deletions(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 54f3911..6ef0a5a 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1713,6 +1713,9 @@ enum nl80211_commands {
* obtained from it is coming from the device's wiphy and not the global
* cfg80211 regdomain.
*
+ * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+ * is operating in an indoor environment.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2072,6 +2075,8 @@ enum nl80211_attrs {

NL80211_ATTR_WIPHY_SELF_MANAGED_REG,

+ NL80211_ATTR_REG_INDOOR,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 7029201..ca12a31 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -397,6 +397,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
[NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
[NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
+ [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
};

/* policy for the key attributes */
@@ -4923,6 +4924,7 @@ static int parse_reg_rule(struct nlattr *tb[],
static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
{
char *data = NULL;
+ bool is_indoor;
enum nl80211_user_reg_hint_type user_reg_hint_type;

/*
@@ -4949,7 +4951,8 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
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();
+ is_indoor = !!info->attrs[NL80211_ATTR_REG_INDOOR];
+ return regulatory_hint_indoor(is_indoor, info->snd_portid);
default:
return -EINVAL;
}
@@ -12669,6 +12672,11 @@ static int nl80211_netlink_notify(struct notifier_block * nb,

rcu_read_unlock();

+ /*
+ * It is possible that the user space process that is controlling the
+ * indoor setting disappeared, so notify the regulatory core.
+ */
+ regulatory_netlink_notify(notify->portid);
return NOTIFY_OK;
}

diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 9a5411c..6b0da81 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -82,17 +82,12 @@
* 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 {
REG_REQ_OK,
REG_REQ_IGNORE,
REG_REQ_INTERSECT,
REG_REQ_ALREADY_SET,
- REG_REQ_USER_HINT_HANDLED,
};

static struct regulatory_request core_request_world = {
@@ -133,9 +128,12 @@ 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 spinlock_t reg_indoor_lock;
+
+/* Used to track the userspace process controlling the indoor setting */
+static u32 reg_is_indoor_portid;

static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
{
@@ -1248,13 +1246,6 @@ static bool reg_request_cell_base(struct regulatory_request *request)
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)
{
return reg_request_cell_base(get_last_request());
@@ -1815,11 +1806,6 @@ __reg_process_hint_user(struct regulatory_request *user_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))
return reg_ignore_cell_hint(user_request);

@@ -1867,8 +1853,7 @@ reg_process_hint_user(struct regulatory_request *user_request)

treatment = __reg_process_hint_user(user_request);
if (treatment == REG_REQ_IGNORE ||
- treatment == REG_REQ_ALREADY_SET ||
- treatment == REG_REQ_USER_HINT_HANDLED) {
+ treatment == REG_REQ_ALREADY_SET) {
reg_free_request(user_request);
return treatment;
}
@@ -1929,7 +1914,6 @@ reg_process_hint_driver(struct wiphy *wiphy,
case REG_REQ_OK:
break;
case REG_REQ_IGNORE:
- case REG_REQ_USER_HINT_HANDLED:
reg_free_request(driver_request);
return treatment;
case REG_REQ_INTERSECT:
@@ -2029,7 +2013,6 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
case REG_REQ_OK:
break;
case REG_REQ_IGNORE:
- case REG_REQ_USER_HINT_HANDLED:
/* fall through */
case REG_REQ_ALREADY_SET:
reg_free_request(country_ie_request);
@@ -2068,8 +2051,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
case NL80211_REGDOM_SET_BY_USER:
treatment = reg_process_hint_user(reg_request);
if (treatment == REG_REQ_IGNORE ||
- treatment == REG_REQ_ALREADY_SET ||
- treatment == REG_REQ_USER_HINT_HANDLED)
+ treatment == REG_REQ_ALREADY_SET)
return;
queue_delayed_work(system_power_efficient_wq,
&reg_timeout, msecs_to_jiffies(3142));
@@ -2266,22 +2248,51 @@ int regulatory_hint_user(const char *alpha2,
return 0;
}

-int regulatory_hint_indoor_user(void)
+int regulatory_hint_indoor(bool is_indoor, u32 portid)
{
- struct regulatory_request *request;
+ spin_lock(&reg_indoor_lock);

- request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
- if (!request)
- return -ENOMEM;
+ /*
+ * Process only if there is a real change, so the original port ID is
+ * saved (to handle cases that several processes try to change the
+ * indoor setting).
+ */
+ if (reg_is_indoor == is_indoor) {
+ spin_unlock(&reg_indoor_lock);
+ return 0;
+ }

- 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);
+ reg_is_indoor = is_indoor;
+ if (reg_is_indoor)
+ reg_is_indoor_portid = portid;
+ else
+ reg_is_indoor_portid = 0;
+
+ spin_unlock(&reg_indoor_lock);
+
+ if (!is_indoor)
+ reg_check_channels();

return 0;
}

+void regulatory_netlink_notify(u32 portid)
+{
+ spin_lock(&reg_indoor_lock);
+
+ if (reg_is_indoor_portid != portid) {
+ spin_unlock(&reg_indoor_lock);
+ return;
+ }
+
+ reg_is_indoor = false;
+ reg_is_indoor_portid = 0;
+
+ spin_unlock(&reg_indoor_lock);
+
+ reg_check_channels();
+}
+
/* Driver hints */
int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
{
@@ -2449,8 +2460,6 @@ static void restore_regulatory_settings(bool reset_user)

ASSERT_RTNL();

- reg_is_indoor = false;
-
reset_regdomains(true, &world_regdom);
restore_alpha2(alpha2, reset_user);

@@ -3019,6 +3028,7 @@ int __init regulatory_init(void)

spin_lock_init(&reg_requests_lock);
spin_lock_init(&reg_pending_beacons_lock);
+ spin_lock_init(&reg_indoor_lock);

reg_regdb_size_check();

diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 4b45d6e..a2c4e16 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -25,7 +25,20 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);

int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type);
-int regulatory_hint_indoor_user(void);
+
+/**
+ * regulatory_hint_indoor - hint operation in indoor env. or not
+ * @is_indoor: if true indicates that user space thinks that the
+ * device is operating in an indoor environment.
+ * @portid: the netlink port ID on which the hint was given.
+ */
+int regulatory_hint_indoor(bool is_indoor, u32 portid);
+
+/**
+ * regulatory_netlink_notify - notify on released netlink socket
+ * @portid: the netlink socket port ID
+ */
+void regulatory_netlink_notify(u32 portid);

void wiphy_regulatory_register(struct wiphy *wiphy);
void wiphy_regulatory_deregister(struct wiphy *wiphy);
--
1.8.3.2


2015-01-06 11:08:43

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 1/2] cfg80211: Add API to change the indoor regulatory setting

On Mon, 2015-01-05 at 23:52 -0500, Ilan Peer wrote:
> As the operating environment of a device can change, add
> API for user space to indicate a change of indoor settings.
> In addition modify the handling of the indoor processing as
> follows:
>
> 1. Directly update the indoor setting without wrapping it as a
> regulatory request.
> 2. Track the socket on which the indoor hint is issued, and reset
> indoor setting if the socket was released.

This probably needs to be wrapped with a request flag from userspace to
have the new behaviour - but if not then at least explain why not?

johannes