@@ -10,6 +10,7 @@
#include <net/cfg80211.h>
#include "core.h"
#include "rdev-ops.h"
+#include "reg.h"
void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
struct ieee80211_channel *chan,
@@ -327,6 +328,9 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
/* check for the other flags */
if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
return false;
+ if (!reg_check_bandwidth(wiphy, MHZ_TO_KHZ(freq),
+ MHZ_TO_KHZ(bandwidth)))
+ return false;
}
return true;
@@ -389,7 +393,13 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
return false;
}
- /* TODO: missing regulatory check on 80/160 bandwidth */
+ /*
+ * TODO: What if there are only certain 80/160/80+80 MHz channels
+ * allowed by the driver, or only certain combinations?
+ * For 40 MHz the driver can set the NO_HT40 flags, but for
+ * 80/160 MHz and in particular 80+80 MHz this isn't really
+ * feasible -- should we ask the driver here?
+ */
if (width > 20)
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
@@ -121,14 +121,14 @@ static inline void assert_reg_lock(void)
static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
{
- return rcu_dereference_protected(cfg80211_regdomain,
- lockdep_is_held(®_mutex));
+ return rcu_dereference_check(cfg80211_regdomain,
+ lockdep_is_held(®_mutex));
}
static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
{
- return rcu_dereference_protected(wiphy->regd,
- lockdep_is_held(®_mutex));
+ return rcu_dereference_check(wiphy->regd,
+ lockdep_is_held(®_mutex));
}
static void rcu_free_regdom(const struct ieee80211_regdomain *r)
@@ -751,7 +751,7 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
u32 center_freq)
{
- const struct ieee80211_regdomain *regd;
+ const struct ieee80211_regdomain *regd = NULL;
struct regulatory_request *lr = get_last_request();
/*
@@ -759,16 +759,39 @@ const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
* IE has been processed or a user wants to help complaince further
*/
if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- lr->initiator != NL80211_REGDOM_SET_BY_USER &&
- wiphy->regd)
+ lr->initiator != NL80211_REGDOM_SET_BY_USER)
regd = get_wiphy_regdom(wiphy);
- else
+
+ if (!regd)
regd = get_cfg80211_regdom();
return freq_reg_info_regd(wiphy, center_freq, regd);
}
EXPORT_SYMBOL(freq_reg_info);
+bool reg_check_bandwidth(struct wiphy *wiphy,
+ u32 center_freq_khz, u32 bw_khz)
+{
+ const struct ieee80211_reg_rule *reg_rule;
+ bool result = false;
+
+ /*
+ * This interpretation is a bit of a strange quirk in the regulatory
+ * rules definitions that we have today: each 20 MHz channel must fit
+ * entirely into a single regulatory range, but if this range forbids
+ * using more than 20 MHz then it forbids even using a small part of
+ * this for the wider channel.
+ */
+
+ rcu_read_lock();
+ reg_rule = freq_reg_info(wiphy, center_freq_khz);
+ if (!IS_ERR(reg_rule))
+ result = reg_rule->freq_range.max_bandwidth_khz >= bw_khz;
+ rcu_read_unlock();
+
+ return result;
+}
+
#ifdef CONFIG_CFG80211_REG_DEBUG
static const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
{
@@ -35,6 +35,9 @@ int set_regdom(const struct ieee80211_regdomain *rd);
bool reg_last_request_cell_base(void);
+bool reg_check_bandwidth(struct wiphy *wiphy,
+ u32 center_freq_khz, u32 bw_khz);
+
/**
* regulatory_hint_found_beacon - hints a beacon was found on a channel
* @wiphy: the wireless device where the beacon was found on