+static void handle_reg_beacon(struct wiphy *wiphy,
+ unsigned int chan_idx,
+ struct reg_beacon *reg_beacon)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+ bool channel_changed = false;
+ struct ieee80211_channel chan_before;
+
+ assert_cfg80211_lock();
+
+ sband = wiphy->bands[reg_beacon->chan.band];
+ chan = &sband->channels[chan_idx];
+
+ if (likely(chan->center_freq != reg_beacon->chan.center_freq))
+ return;
+
+ if (chan->beacon_found)
+ return;
+
+ chan->beacon_found = true;
+
+ chan_before.center_freq = chan->center_freq;
+ chan_before.flags = chan->flags;
+
+ if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
+ !(chan->orig_flags & IEEE80211_CHAN_PASSIVE_SCAN)) {
+ chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
+ channel_changed = true;
+ }
+
+ if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
+ !(chan->orig_flags & IEEE80211_CHAN_NO_IBSS)) {
+ chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
+ channel_changed = true;
+ }
+
+ if (channel_changed)
+ nl80211_send_beacon_hint_event(wiphy, &chan_before, chan);
+}
+
+/*
+ * Called when a scan on a wiphy finds a beacon on
+ * new channel
+ */
+static void wiphy_update_new_beacon(struct wiphy *wiphy,
+ struct reg_beacon *reg_beacon)
+{
+ unsigned int i;
+ struct ieee80211_supported_band *sband;
+
+ assert_cfg80211_lock();
+
+ if (!wiphy->bands[reg_beacon->chan.band])
+ return;
+
+ sband = wiphy->bands[reg_beacon->chan.band];
+
+ for (i = 0; i < sband->n_channels; i++)
+ handle_reg_beacon(wiphy, i, reg_beacon);
+}
+
+/*
+ * Called upon reg changes or a new wiphy is added
+ */
+static void wiphy_update_beacon_reg(struct wiphy *wiphy)
+{
+ unsigned int i;
+ struct ieee80211_supported_band *sband;
+ struct reg_beacon *reg_beacon;
+
+ assert_cfg80211_lock();
+
+ if (list_empty(®_beacon_list))
+ return;
+
+ list_for_each_entry(reg_beacon, ®_beacon_list, list) {
+ if (!wiphy->bands[reg_beacon->chan.band])
+ continue;
+ sband = wiphy->bands[reg_beacon->chan.band];
+ for (i = 0; i < sband->n_channels; i++)
+ handle_reg_beacon(wiphy, i, reg_beacon);
+ }
+}
+
+static bool reg_is_world_roaming(struct wiphy *wiphy)
+{
+ if (is_world_regdom(cfg80211_regdomain->alpha2) ||
+ (wiphy->regd && is_world_regdom(wiphy->regd->alpha2)))
+ return true;
+ if (last_request &&
+ last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ wiphy->custom_regulatory)
+ return true;
+ return false;
+}
+
+/* Reap the advantages of previously found beacons */
+static void reg_process_beacons(struct wiphy *wiphy)
+{
+ /*
+ * Means we are just firing up cfg80211, so no beacons would
+ * have been processed yet.
+ */
+ if (!last_request)
+ return;
+ if (!reg_is_world_roaming(wiphy))
+ return;
+ wiphy_update_beacon_reg(wiphy);
+}
+
+static bool is_ht40_not_allowed(struct ieee80211_channel *chan)
+{
+ if (!chan)
+ return true;
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ return true;
+ /* This would happen when regulatory rules disallow HT40 completely */
+ if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40)))
+ return true;
+ return false;
+}
+
+static void reg_process_ht_flags_channel(struct wiphy *wiphy,
+ enum ieee80211_band band,
+ unsigned int chan_idx)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *channel;
+ struct ieee80211_channel *channel_before = NULL, *channel_after = NULL;
+ unsigned int i;
+
+ assert_cfg80211_lock();
+
+ sband = wiphy->bands[band];
+ BUG_ON(chan_idx >= sband->n_channels);
+ channel = &sband->channels[chan_idx];
+
+ if (is_ht40_not_allowed(channel)) {
+ channel->flags |= IEEE80211_CHAN_NO_HT40;
+ return;
+ }
+
+ /*
+ * We need to ensure the extension channels exist to
+ * be able to use HT40- or HT40+, this finds them (or not)
+ */
+ for (i = 0; i < sband->n_channels; i++) {
+ struct ieee80211_channel *c = &sband->channels[i];
+ if (c->center_freq == (channel->center_freq - 20))
+ channel_before = c;
+ if (c->center_freq == (channel->center_freq + 20))
+ channel_after = c;
+ }
+
+ /*
+ * Please note that this assumes target bandwidth is 20 MHz,
+ * if that ever changes we also need to change the below logic
+ * to include that as well.
+ */
+ if (is_ht40_not_allowed(channel_before))
+ channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
+ else
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+
+ if (is_ht40_not_allowed(channel_after))
+ channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
+ else
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+}
+
+static void reg_process_ht_flags_band(struct wiphy *wiphy,
+ enum ieee80211_band band)
+{
+ unsigned int i;
+ struct ieee80211_supported_band *sband;
+
+ BUG_ON(!wiphy->bands[band]);
+ sband = wiphy->bands[band];
+
+ for (i = 0; i < sband->n_channels; i++)
+ reg_process_ht_flags_channel(wiphy, band, i);
+}
+
+static void reg_process_ht_flags(struct wiphy *wiphy)