|
@@ -11,43 +11,252 @@
|
|
|
#include "core.h"
|
|
|
#include "rdev-ops.h"
|
|
|
|
|
|
-bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
|
|
|
- struct cfg80211_chan_def *chandef)
|
|
|
+void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
|
|
|
+ struct ieee80211_channel *chan,
|
|
|
+ enum nl80211_channel_type chan_type)
|
|
|
{
|
|
|
- struct ieee80211_channel *sec_chan;
|
|
|
- int diff;
|
|
|
+ if (WARN_ON(!chan))
|
|
|
+ return;
|
|
|
|
|
|
- trace_cfg80211_reg_can_beacon(wiphy, chandef);
|
|
|
+ chandef->chan = chan;
|
|
|
+ chandef->center_freq2 = 0;
|
|
|
|
|
|
- switch (chandef->_type) {
|
|
|
+ switch (chan_type) {
|
|
|
+ case NL80211_CHAN_NO_HT:
|
|
|
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
|
|
|
+ chandef->center_freq1 = chan->center_freq;
|
|
|
+ break;
|
|
|
+ case NL80211_CHAN_HT20:
|
|
|
+ chandef->width = NL80211_CHAN_WIDTH_20;
|
|
|
+ chandef->center_freq1 = chan->center_freq;
|
|
|
+ break;
|
|
|
case NL80211_CHAN_HT40PLUS:
|
|
|
- diff = 20;
|
|
|
+ chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
+ chandef->center_freq1 = chan->center_freq + 10;
|
|
|
break;
|
|
|
case NL80211_CHAN_HT40MINUS:
|
|
|
- diff = -20;
|
|
|
+ chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
+ chandef->center_freq1 = chan->center_freq - 10;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ WARN_ON(1);
|
|
|
+ }
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(cfg80211_chandef_create);
|
|
|
+
|
|
|
+bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef)
|
|
|
+{
|
|
|
+ u32 control_freq;
|
|
|
+
|
|
|
+ if (!chandef->chan)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ control_freq = chandef->chan->center_freq;
|
|
|
+
|
|
|
+ switch (chandef->width) {
|
|
|
+ case NL80211_CHAN_WIDTH_20:
|
|
|
+ case NL80211_CHAN_WIDTH_20_NOHT:
|
|
|
+ if (chandef->center_freq1 != control_freq)
|
|
|
+ return false;
|
|
|
+ if (chandef->center_freq2)
|
|
|
+ return false;
|
|
|
+ break;
|
|
|
+ case NL80211_CHAN_WIDTH_40:
|
|
|
+ if (chandef->center_freq1 != control_freq + 10 &&
|
|
|
+ chandef->center_freq1 != control_freq - 10)
|
|
|
+ return false;
|
|
|
+ if (chandef->center_freq2)
|
|
|
+ return false;
|
|
|
+ break;
|
|
|
+ case NL80211_CHAN_WIDTH_80P80:
|
|
|
+ if (chandef->center_freq1 != control_freq + 30 &&
|
|
|
+ chandef->center_freq1 != control_freq + 10 &&
|
|
|
+ chandef->center_freq1 != control_freq - 10 &&
|
|
|
+ chandef->center_freq1 != control_freq - 30)
|
|
|
+ return false;
|
|
|
+ if (!chandef->center_freq2)
|
|
|
+ return false;
|
|
|
+ break;
|
|
|
+ case NL80211_CHAN_WIDTH_80:
|
|
|
+ if (chandef->center_freq1 != control_freq + 30 &&
|
|
|
+ chandef->center_freq1 != control_freq + 10 &&
|
|
|
+ chandef->center_freq1 != control_freq - 10 &&
|
|
|
+ chandef->center_freq1 != control_freq - 30)
|
|
|
+ return false;
|
|
|
+ if (chandef->center_freq2)
|
|
|
+ return false;
|
|
|
+ break;
|
|
|
+ case NL80211_CHAN_WIDTH_160:
|
|
|
+ if (chandef->center_freq1 != control_freq + 70 &&
|
|
|
+ chandef->center_freq1 != control_freq + 50 &&
|
|
|
+ chandef->center_freq1 != control_freq + 30 &&
|
|
|
+ chandef->center_freq1 != control_freq + 10 &&
|
|
|
+ chandef->center_freq1 != control_freq - 10 &&
|
|
|
+ chandef->center_freq1 != control_freq - 30 &&
|
|
|
+ chandef->center_freq1 != control_freq - 50 &&
|
|
|
+ chandef->center_freq1 != control_freq - 70)
|
|
|
+ return false;
|
|
|
+ if (chandef->center_freq2)
|
|
|
+ return false;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
|
|
|
+ int *pri40, int *pri80)
|
|
|
+{
|
|
|
+ int tmp;
|
|
|
+
|
|
|
+ switch (c->width) {
|
|
|
+ case NL80211_CHAN_WIDTH_40:
|
|
|
+ *pri40 = c->center_freq1;
|
|
|
+ *pri80 = 0;
|
|
|
+ break;
|
|
|
+ case NL80211_CHAN_WIDTH_80:
|
|
|
+ case NL80211_CHAN_WIDTH_80P80:
|
|
|
+ *pri80 = c->center_freq1;
|
|
|
+ /* n_P20 */
|
|
|
+ tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
|
|
|
+ /* n_P40 */
|
|
|
+ tmp /= 2;
|
|
|
+ /* freq_P40 */
|
|
|
+ *pri40 = c->center_freq1 - 20 + 40 * tmp;
|
|
|
+ break;
|
|
|
+ case NL80211_CHAN_WIDTH_160:
|
|
|
+ /* n_P20 */
|
|
|
+ tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
|
|
|
+ /* n_P40 */
|
|
|
+ tmp /= 2;
|
|
|
+ /* freq_P40 */
|
|
|
+ *pri40 = c->center_freq1 - 60 + 40 * tmp;
|
|
|
+ /* n_P80 */
|
|
|
+ tmp /= 2;
|
|
|
+ *pri80 = c->center_freq1 - 40 + 80 * tmp;
|
|
|
break;
|
|
|
default:
|
|
|
- trace_cfg80211_return_bool(true);
|
|
|
- return true;
|
|
|
+ WARN_ON_ONCE(1);
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+const struct cfg80211_chan_def *
|
|
|
+cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
|
|
|
+ const struct cfg80211_chan_def *c2)
|
|
|
+{
|
|
|
+ u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
|
|
|
|
|
|
- sec_chan = ieee80211_get_channel(wiphy,
|
|
|
- chandef->chan->center_freq + diff);
|
|
|
- if (!sec_chan) {
|
|
|
+ /* If they are identical, return */
|
|
|
+ if (cfg80211_chandef_identical(c1, c2))
|
|
|
+ return c1;
|
|
|
+
|
|
|
+ /* otherwise, must have same control channel */
|
|
|
+ if (c1->chan != c2->chan)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If they have the same width, but aren't identical,
|
|
|
+ * then they can't be compatible.
|
|
|
+ */
|
|
|
+ if (c1->width == c2->width)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
|
|
|
+ c1->width == NL80211_CHAN_WIDTH_20)
|
|
|
+ return c2;
|
|
|
+
|
|
|
+ if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
|
|
|
+ c2->width == NL80211_CHAN_WIDTH_20)
|
|
|
+ return c1;
|
|
|
+
|
|
|
+ chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
|
|
|
+ chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
|
|
|
+
|
|
|
+ if (c1_pri40 != c2_pri40)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ WARN_ON(!c1_pri80 && !c2_pri80);
|
|
|
+ if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ if (c1->width > c2->width)
|
|
|
+ return c1;
|
|
|
+ return c2;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(cfg80211_chandef_compatible);
|
|
|
+
|
|
|
+bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
|
|
+ u32 center_freq, u32 bandwidth,
|
|
|
+ u32 prohibited_flags)
|
|
|
+{
|
|
|
+ struct ieee80211_channel *c;
|
|
|
+ u32 freq;
|
|
|
+
|
|
|
+ for (freq = center_freq - bandwidth/2 + 10;
|
|
|
+ freq <= center_freq + bandwidth/2 - 10;
|
|
|
+ freq += 20) {
|
|
|
+ c = ieee80211_get_channel(wiphy, freq);
|
|
|
+ if (!c || c->flags & prohibited_flags)
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static bool cfg80211_check_beacon_chans(struct wiphy *wiphy,
|
|
|
+ u32 center_freq, u32 bw)
|
|
|
+{
|
|
|
+ return cfg80211_secondary_chans_ok(wiphy, center_freq, bw,
|
|
|
+ IEEE80211_CHAN_DISABLED |
|
|
|
+ IEEE80211_CHAN_PASSIVE_SCAN |
|
|
|
+ IEEE80211_CHAN_NO_IBSS |
|
|
|
+ IEEE80211_CHAN_RADAR);
|
|
|
+}
|
|
|
+
|
|
|
+bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
|
|
|
+ struct cfg80211_chan_def *chandef)
|
|
|
+{
|
|
|
+ u32 width;
|
|
|
+ bool res;
|
|
|
+
|
|
|
+ trace_cfg80211_reg_can_beacon(wiphy, chandef);
|
|
|
+
|
|
|
+ if (WARN_ON(!cfg80211_chan_def_valid(chandef))) {
|
|
|
trace_cfg80211_return_bool(false);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- /* we'll need a DFS capability later */
|
|
|
- if (sec_chan->flags & (IEEE80211_CHAN_DISABLED |
|
|
|
- IEEE80211_CHAN_PASSIVE_SCAN |
|
|
|
- IEEE80211_CHAN_NO_IBSS |
|
|
|
- IEEE80211_CHAN_RADAR)) {
|
|
|
+ switch (chandef->width) {
|
|
|
+ case NL80211_CHAN_WIDTH_20_NOHT:
|
|
|
+ case NL80211_CHAN_WIDTH_20:
|
|
|
+ width = 20;
|
|
|
+ break;
|
|
|
+ case NL80211_CHAN_WIDTH_40:
|
|
|
+ width = 40;
|
|
|
+ break;
|
|
|
+ case NL80211_CHAN_WIDTH_80:
|
|
|
+ case NL80211_CHAN_WIDTH_80P80:
|
|
|
+ width = 80;
|
|
|
+ break;
|
|
|
+ case NL80211_CHAN_WIDTH_160:
|
|
|
+ width = 160;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ WARN_ON_ONCE(1);
|
|
|
trace_cfg80211_return_bool(false);
|
|
|
return false;
|
|
|
}
|
|
|
- trace_cfg80211_return_bool(true);
|
|
|
- return true;
|
|
|
+
|
|
|
+ res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width);
|
|
|
+
|
|
|
+ if (res && chandef->center_freq2)
|
|
|
+ res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2,
|
|
|
+ width);
|
|
|
+
|
|
|
+ trace_cfg80211_return_bool(res);
|
|
|
+ return res;
|
|
|
}
|
|
|
EXPORT_SYMBOL(cfg80211_reg_can_beacon);
|
|
|
|