* @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,
enum nl80211_reg_initiator initiator;
char alpha2[2];
bool intersect;
+ bool processed;
enum environment_cap country_ie_env;
struct list_head list;
};
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
* 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;
}
wiphy_update_regulatory(wiphy, initiator);
}
-/* 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);
}
nl80211_send_reg_change_event(last_request);
+ reg_set_request_processed();
+
mutex_unlock(®_mutex);
return r;