summaryrefslogtreecommitdiff
path: root/net/wireless/reg.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r--net/wireless/reg.c72
1 files changed, 70 insertions, 2 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index ab801a1097b2..1613080a96b9 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -106,6 +106,9 @@ struct reg_beacon {
static void reg_todo(struct work_struct *work);
static DECLARE_WORK(reg_work, reg_todo);
+static void reg_timeout_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work);
+
/* We keep a static world regulatory domain in case of the absence of CRDA */
static const struct ieee80211_regdomain world_regdom = {
.n_reg_rules = 5,
@@ -1330,6 +1333,9 @@ static void reg_set_request_processed(void)
need_more_processing = true;
spin_unlock(&reg_requests_lock);
+ if (last_request->initiator == NL80211_REGDOM_SET_BY_USER)
+ cancel_delayed_work_sync(&reg_timeout);
+
if (need_more_processing)
schedule_work(&reg_work);
}
@@ -1440,8 +1446,18 @@ static void reg_process_hint(struct regulatory_request *reg_request)
r = __regulatory_hint(wiphy, reg_request);
/* This is required so that the orig_* parameters are saved */
if (r == -EALREADY && wiphy &&
- wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+ wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
wiphy_update_regulatory(wiphy, initiator);
+ return;
+ }
+
+ /*
+ * We only time out user hints, given that they should be the only
+ * source of bogus requests.
+ */
+ if (r != -EALREADY &&
+ reg_request->initiator == NL80211_REGDOM_SET_BY_USER)
+ schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
}
/*
@@ -1744,6 +1760,8 @@ static void restore_regulatory_settings(bool reset_user)
{
char alpha2[2];
struct reg_beacon *reg_beacon, *btmp;
+ struct regulatory_request *reg_request, *tmp;
+ LIST_HEAD(tmp_reg_req_list);
mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_mutex);
@@ -1751,6 +1769,25 @@ static void restore_regulatory_settings(bool reset_user)
reset_regdomains();
restore_alpha2(alpha2, reset_user);
+ /*
+ * If there's any pending requests we simply
+ * stash them to a temporary pending queue and
+ * add then after we've restored regulatory
+ * settings.
+ */
+ spin_lock(&reg_requests_lock);
+ if (!list_empty(&reg_requests_list)) {
+ list_for_each_entry_safe(reg_request, tmp,
+ &reg_requests_list, list) {
+ if (reg_request->initiator !=
+ NL80211_REGDOM_SET_BY_USER)
+ continue;
+ list_del(&reg_request->list);
+ list_add_tail(&reg_request->list, &tmp_reg_req_list);
+ }
+ }
+ spin_unlock(&reg_requests_lock);
+
/* Clear beacon hints */
spin_lock_bh(&reg_pending_beacons_lock);
if (!list_empty(&reg_pending_beacons)) {
@@ -1785,8 +1822,31 @@ static void restore_regulatory_settings(bool reset_user)
*/
if (is_an_alpha2(alpha2))
regulatory_hint_user(user_alpha2);
-}
+ if (list_empty(&tmp_reg_req_list))
+ return;
+
+ mutex_lock(&cfg80211_mutex);
+ mutex_lock(&reg_mutex);
+
+ spin_lock(&reg_requests_lock);
+ list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) {
+ REG_DBG_PRINT("Adding request for country %c%c back "
+ "into the queue\n",
+ reg_request->alpha2[0],
+ reg_request->alpha2[1]);
+ list_del(&reg_request->list);
+ list_add_tail(&reg_request->list, &reg_requests_list);
+ }
+ spin_unlock(&reg_requests_lock);
+
+ mutex_unlock(&reg_mutex);
+ mutex_unlock(&cfg80211_mutex);
+
+ REG_DBG_PRINT("Kicking the queue\n");
+
+ schedule_work(&reg_work);
+}
void regulatory_hint_disconnect(void)
{
@@ -2125,6 +2185,13 @@ out:
mutex_unlock(&reg_mutex);
}
+static void reg_timeout_work(struct work_struct *work)
+{
+ REG_DBG_PRINT("Timeout while waiting for CRDA to reply, "
+ "restoring regulatory settings");
+ restore_regulatory_settings(true);
+}
+
int __init regulatory_init(void)
{
int err = 0;
@@ -2178,6 +2245,7 @@ void /* __init_or_exit */ regulatory_exit(void)
struct reg_beacon *reg_beacon, *btmp;
cancel_work_sync(&reg_work);
+ cancel_delayed_work_sync(&reg_timeout);
mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_mutex);