179 lines
5.1 KiB
Diff
179 lines
5.1 KiB
Diff
--- a/include/net/regulatory.h
|
|
+++ b/include/net/regulatory.h
|
|
@@ -43,6 +43,12 @@ enum environment_cap {
|
|
* @intersect: indicates whether the wireless core should intersect
|
|
* the requested regulatory domain with the presently set regulatory
|
|
* domain.
|
|
+ * @processed: indicates whether or not this requests has already been
|
|
+ * processed. When the last request is processed it means that the
|
|
+ * currently regulatory domain set on cfg80211 is updated from
|
|
+ * CRDA and can be used by other regulatory requests. When a
|
|
+ * the last request is not yet processed we must yield until it
|
|
+ * is processed before processing any new requests.
|
|
* @country_ie_checksum: checksum of the last processed and accepted
|
|
* country IE
|
|
* @country_ie_env: lets us know if the AP is telling us we are outdoor,
|
|
@@ -54,6 +60,7 @@ struct regulatory_request {
|
|
enum nl80211_reg_initiator initiator;
|
|
char alpha2[2];
|
|
bool intersect;
|
|
+ bool processed;
|
|
enum environment_cap country_ie_env;
|
|
struct list_head list;
|
|
};
|
|
--- a/net/wireless/reg.c
|
|
+++ b/net/wireless/reg.c
|
|
@@ -96,6 +96,9 @@ struct reg_beacon {
|
|
struct ieee80211_channel chan;
|
|
};
|
|
|
|
+static void reg_todo(struct work_struct *work);
|
|
+static DECLARE_WORK(reg_work, reg_todo);
|
|
+
|
|
/* 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,
|
|
@@ -1317,6 +1320,21 @@ static int ignore_request(struct wiphy *
|
|
return -EINVAL;
|
|
}
|
|
|
|
+static void reg_set_request_processed(void)
|
|
+{
|
|
+ bool need_more_processing = false;
|
|
+
|
|
+ last_request->processed = true;
|
|
+
|
|
+ spin_lock(®_requests_lock);
|
|
+ if (!list_empty(®_requests_list))
|
|
+ need_more_processing = true;
|
|
+ spin_unlock(®_requests_lock);
|
|
+
|
|
+ if (need_more_processing)
|
|
+ schedule_work(®_work);
|
|
+}
|
|
+
|
|
/**
|
|
* __regulatory_hint - hint to the wireless core a regulatory domain
|
|
* @wiphy: if the hint comes from country information from an AP, this
|
|
@@ -1392,8 +1410,10 @@ new_request:
|
|
* have applied the requested regulatory domain before we just
|
|
* inform userspace we have processed the request
|
|
*/
|
|
- if (r == -EALREADY)
|
|
+ if (r == -EALREADY) {
|
|
nl80211_send_reg_change_event(last_request);
|
|
+ reg_set_request_processed();
|
|
+ }
|
|
return r;
|
|
}
|
|
|
|
@@ -1409,16 +1429,13 @@ static void reg_process_hint(struct regu
|
|
|
|
BUG_ON(!reg_request->alpha2);
|
|
|
|
- mutex_lock(&cfg80211_mutex);
|
|
- mutex_lock(®_mutex);
|
|
-
|
|
if (wiphy_idx_valid(reg_request->wiphy_idx))
|
|
wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
|
|
|
|
if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
|
|
!wiphy) {
|
|
kfree(reg_request);
|
|
- goto out;
|
|
+ return;
|
|
}
|
|
|
|
r = __regulatory_hint(wiphy, reg_request);
|
|
@@ -1426,28 +1443,46 @@ static void reg_process_hint(struct regu
|
|
if (r == -EALREADY && wiphy &&
|
|
wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
|
|
wiphy_update_regulatory(wiphy, initiator);
|
|
-out:
|
|
- mutex_unlock(®_mutex);
|
|
- mutex_unlock(&cfg80211_mutex);
|
|
}
|
|
|
|
-/* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */
|
|
+/*
|
|
+ * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_*
|
|
+ * Regulatory hints come on a first come first serve basis and we
|
|
+ * must process each one atomically.
|
|
+ */
|
|
static void reg_process_pending_hints(void)
|
|
- {
|
|
+{
|
|
struct regulatory_request *reg_request;
|
|
|
|
+ mutex_lock(&cfg80211_mutex);
|
|
+ mutex_lock(®_mutex);
|
|
+
|
|
+ /* When last_request->processed becomes true this will be rescheduled */
|
|
+ if (last_request && !last_request->processed) {
|
|
+ REG_DBG_PRINT("Pending regulatory request, waiting "
|
|
+ "for it to be processed...");
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
spin_lock(®_requests_lock);
|
|
- while (!list_empty(®_requests_list)) {
|
|
- reg_request = list_first_entry(®_requests_list,
|
|
- struct regulatory_request,
|
|
- list);
|
|
- list_del_init(®_request->list);
|
|
|
|
+ if (list_empty(®_requests_list)) {
|
|
spin_unlock(®_requests_lock);
|
|
- reg_process_hint(reg_request);
|
|
- spin_lock(®_requests_lock);
|
|
+ goto out;
|
|
}
|
|
+
|
|
+ reg_request = list_first_entry(®_requests_list,
|
|
+ struct regulatory_request,
|
|
+ list);
|
|
+ list_del_init(®_request->list);
|
|
+
|
|
spin_unlock(®_requests_lock);
|
|
+
|
|
+ reg_process_hint(reg_request);
|
|
+
|
|
+out:
|
|
+ mutex_unlock(®_mutex);
|
|
+ mutex_unlock(&cfg80211_mutex);
|
|
}
|
|
|
|
/* Processes beacon hints -- this has nothing to do with country IEs */
|
|
@@ -1494,8 +1529,6 @@ static void reg_todo(struct work_struct
|
|
reg_process_pending_beacon_hints();
|
|
}
|
|
|
|
-static DECLARE_WORK(reg_work, reg_todo);
|
|
-
|
|
static void queue_regulatory_request(struct regulatory_request *request)
|
|
{
|
|
if (isalpha(request->alpha2[0]))
|
|
@@ -1530,12 +1563,7 @@ static int regulatory_hint_core(const ch
|
|
request->alpha2[1] = alpha2[1];
|
|
request->initiator = NL80211_REGDOM_SET_BY_CORE;
|
|
|
|
- /*
|
|
- * This ensures last_request is populated once modules
|
|
- * come swinging in and calling regulatory hints and
|
|
- * wiphy_apply_custom_regulatory().
|
|
- */
|
|
- reg_process_hint(request);
|
|
+ queue_regulatory_request(request);
|
|
|
|
return 0;
|
|
}
|
|
@@ -2061,6 +2089,8 @@ int set_regdom(const struct ieee80211_re
|
|
|
|
nl80211_send_reg_change_event(last_request);
|
|
|
|
+ reg_set_request_processed();
|
|
+
|
|
mutex_unlock(®_mutex);
|
|
|
|
return r;
|