371 lines
12 KiB
C++
371 lines
12 KiB
C++
#include "ChannelInfo.hpp"
|
|
#include "WifiRegulations.hpp"
|
|
#include "RegDb.hpp"
|
|
#include <set>
|
|
#include <algorithm>
|
|
#include "ss.hpp"
|
|
|
|
using namespace pipedal;
|
|
|
|
// To do: no support for ac 80GHz channels (which pi supports).
|
|
// Wifi 6 ax channels are explicitly disabled (pi doesn't support them).
|
|
|
|
|
|
struct OpClassChannels
|
|
{
|
|
WifiMode band;
|
|
int32_t regDomain;
|
|
int32_t firstChannel;
|
|
int32_t lastChannel;
|
|
int32_t increment;
|
|
WifiBandwidth bandwidth;
|
|
bool p2p_support;
|
|
};
|
|
// from wpa_supplicant.
|
|
static std::vector<OpClassChannels> opClasses = {
|
|
{WifiMode::IEEE80211G, 81, 1, 13, 1, WifiBandwidth::BW20, true},
|
|
{WifiMode::IEEE80211G, 82, 14, 14, 1, WifiBandwidth::BW20, false},
|
|
|
|
/* Do not enable HT40 on 2.4 GHz for P2P use for now */
|
|
{WifiMode::IEEE80211G, 83, 1, 9, 1, WifiBandwidth::BW40PLUS, false},
|
|
{WifiMode::IEEE80211G, 84, 5, 13, 1, WifiBandwidth::BW40MINUS, false},
|
|
|
|
{WifiMode::IEEE80211A, 115, 36, 48, 4, WifiBandwidth::BW20, true},
|
|
{WifiMode::IEEE80211A, 116, 36, 44, 8, WifiBandwidth::BW40PLUS, true},
|
|
{WifiMode::IEEE80211A, 117, 40, 48, 8, WifiBandwidth::BW40MINUS, true},
|
|
{WifiMode::IEEE80211A, 118, 52, 64, 4, WifiBandwidth::BW20, false},
|
|
{WifiMode::IEEE80211A, 119, 52, 60, 8, WifiBandwidth::BW40PLUS, false},
|
|
{WifiMode::IEEE80211A, 120, 56, 64, 8, WifiBandwidth::BW40MINUS, false},
|
|
{WifiMode::IEEE80211A, 121, 100, 140, 4, WifiBandwidth::BW20, false},
|
|
{WifiMode::IEEE80211A, 122, 100, 132, 8, WifiBandwidth::BW40PLUS, false},
|
|
{WifiMode::IEEE80211A, 123, 104, 136, 8, WifiBandwidth::BW40MINUS, false},
|
|
{WifiMode::IEEE80211A, 124, 149, 161, 4, WifiBandwidth::BW20, true},
|
|
{WifiMode::IEEE80211A, 125, 149, 177, 4, WifiBandwidth::BW20, true},
|
|
{WifiMode::IEEE80211A, 126, 149, 173, 8, WifiBandwidth::BW40PLUS, true},
|
|
{WifiMode::IEEE80211A, 127, 153, 177, 8, WifiBandwidth::BW40MINUS, true},
|
|
|
|
/*
|
|
* IEEE P802.11ax/D8.0 (WIFI 6)Table E-4 actually talks about channel center
|
|
* frequency index 42, 58, 106, 122, 138, 155, 171 with channel spacing
|
|
* of 80 MHz, but currently use the following definition for simplicity
|
|
* (these center frequencies are not actual channels, which makes
|
|
* wpas_p2p_verify_channel() fail). wpas_p2p_verify_80mhz() should take
|
|
* care of removing invalid channels.
|
|
*/
|
|
|
|
// WiFi 5.
|
|
// {WifiMode::IEEE80211A, 128, 36, 177, 4, WifiBandwidth::BW80, true},
|
|
// {WifiMode::IEEE80211A, 129, 36, 177, 4, WifiBandwidth::BW160, true},
|
|
// {WifiMode::IEEE80211A, 130, 36, 177, 4, WifiBandwidth::BW80P80, true},
|
|
|
|
// WiFi 6.
|
|
// {WifiMode::IEEE80211AX, 131, 1, 233, 4, WifiBandwidth::BW20, true},
|
|
// {WifiMode::IEEE80211AX, 132, 1, 233, 8, WifiBandwidth::BW40PLUS, true},
|
|
// {WifiMode::IEEE80211AX, 133, 1, 233, 16, WifiBandwidth::BW80, true},
|
|
// {WifiMode::IEEE80211AX, 134, 1, 233, 32, WifiBandwidth::BW160, true},
|
|
// {WifiMode::IEEE80211AX, 135, 1, 233, 16, WifiBandwidth::BW80P80, false},
|
|
// {WifiMode::IEEE80211AX, 136, 2, 2, 4, WifiBandwidth::BW20, false},
|
|
|
|
|
|
/* IEEE Std 802.11ad-2012 and P802.ay/D5.0 60 GHz operating classes.
|
|
* Class 180 has the legacy channels 1-6. Classes 181-183 include
|
|
* channels which implement channel bonding features.
|
|
*/
|
|
// {WifiMode::IEEE80211AD, 180, 1, 6, 1, WifiBandwidth::BW2160, true},
|
|
// {WifiMode::IEEE80211AD, 181, 9, 13, 1, WifiBandwidth::BW4320, true},
|
|
// {WifiMode::IEEE80211AD, 182, 17, 20, 1, WifiBandwidth::BW6480, true},
|
|
// {WifiMode::IEEE80211AD, 183, 25, 27, 1, WifiBandwidth::BW8640, true},
|
|
|
|
};
|
|
|
|
static int32_t ChannelToFrequency(int32_t channel)
|
|
{
|
|
if (channel >= 1 && channel <= 13)
|
|
{
|
|
return 2412 + (channel - 1) * 5;
|
|
}
|
|
if (channel == 14)
|
|
{
|
|
return 2484;
|
|
}
|
|
if (channel >= 36 && channel <= 165)
|
|
{
|
|
return 5180 + (channel - 36) * 5;
|
|
}
|
|
return -1;
|
|
}
|
|
static bool BandwidthPermitted(int32_t frequency,WifiBandwidth bandwidth, const WifiRule &rule)
|
|
{
|
|
int32_t channelWidth = -1;
|
|
switch (bandwidth)
|
|
{
|
|
case WifiBandwidth::BW20:
|
|
channelWidth = 20;
|
|
break;
|
|
case WifiBandwidth::BW40PLUS:
|
|
channelWidth = 40;
|
|
if (rule.HasFlag(RegRuleFlags::NO_HT40PLUS))
|
|
return false;
|
|
break;
|
|
case WifiBandwidth::BW40MINUS:
|
|
if (rule.HasFlag(RegRuleFlags::NO_HT40MINUS))
|
|
return false;
|
|
channelWidth = 40;
|
|
break;
|
|
case WifiBandwidth::BW40: // wifi 6 only. do NOT use for a/g channels.
|
|
channelWidth = 40;
|
|
break;
|
|
case WifiBandwidth::BW80:
|
|
if (rule.HasFlag(RegRuleFlags::NO_80MHZ))
|
|
return false;
|
|
channelWidth = 80;
|
|
break;
|
|
case WifiBandwidth::BW160:
|
|
if (rule.HasFlag(RegRuleFlags::NO_160MHZ))
|
|
return false;
|
|
channelWidth = 160;
|
|
break;
|
|
case WifiBandwidth::BW80P80:
|
|
if (rule.HasFlag(RegRuleFlags::NO_160MHZ))
|
|
return false;
|
|
channelWidth = 160;
|
|
break;
|
|
case WifiBandwidth::BW2160:
|
|
channelWidth = 2160;
|
|
break;
|
|
case WifiBandwidth::BW4320:
|
|
channelWidth = 4320;
|
|
break;
|
|
case WifiBandwidth::BW6480:
|
|
channelWidth = 6480;
|
|
break;
|
|
case WifiBandwidth::BW8640:
|
|
channelWidth = 8640;
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
if (channelWidth * 1000 > rule.max_bandwidth_khz)
|
|
{
|
|
return false;
|
|
}
|
|
int32_t minFrequency = frequency-channelWidth/2;
|
|
int32_t maxFrequency = minFrequency+channelWidth;
|
|
if (bandwidth == WifiBandwidth::BW40PLUS)
|
|
{
|
|
minFrequency = frequency-channelWidth/4;
|
|
maxFrequency = minFrequency+channelWidth;
|
|
} else if (bandwidth == WifiBandwidth::BW40MINUS)
|
|
{
|
|
maxFrequency = frequency+channelWidth/4;
|
|
minFrequency = maxFrequency-channelWidth;
|
|
}
|
|
if (minFrequency*1000 < rule.start_freq_khz || maxFrequency*1000 > rule.end_freq_khz)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
WifiInfo pipedal::getWifiInfo(const std::string&countryIso3661)
|
|
{
|
|
const RegDb ®Db = RegDb::GetInstance();
|
|
const WifiRegulations ®ulations = regDb.getWifiRegulations(countryIso3661);
|
|
|
|
WifiInfo info;
|
|
info.reg_alpha2 = regulations.reg_alpha2;
|
|
info.dfsRegion = regulations.dfs_region;
|
|
|
|
|
|
for (const auto &classMap : opClasses)
|
|
{
|
|
if (!classMap.p2p_support)
|
|
{
|
|
continue;
|
|
}
|
|
if (classMap.band != WifiMode::IEEE80211A && classMap.band != WifiMode::IEEE80211G)
|
|
{
|
|
continue;
|
|
}
|
|
for (auto channel = classMap.firstChannel; channel <= classMap.lastChannel; channel += classMap.increment)
|
|
{
|
|
if (!classMap.p2p_support)
|
|
continue;
|
|
|
|
int32_t frequency = ChannelToFrequency(channel);
|
|
if (frequency == -1)
|
|
continue;
|
|
|
|
const auto *rule = regulations.GetRule(frequency);
|
|
if (!rule)
|
|
continue;
|
|
|
|
if (rule->HasFlag(
|
|
RegRuleFlags::NO_IR | RegRuleFlags::DFS | RegRuleFlags::PTP_ONLY | RegRuleFlags::PTMP_ONLY))
|
|
{
|
|
continue;
|
|
}
|
|
if (rule->HasFlag(RegRuleFlags::DFS))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
WifiBandwidth bandwidth = classMap.bandwidth;
|
|
if (!BandwidthPermitted(frequency,bandwidth, *rule))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
WifiChannelInfo channelInfo;
|
|
channelInfo.channelNumber = channel;
|
|
channelInfo.hardwareMode = classMap.band;
|
|
channelInfo.disabled = false;
|
|
channelInfo.indoorOnly = rule->HasFlag(RegRuleFlags::NO_OUTDOOR);
|
|
channelInfo.outdoorsOnly = rule->HasFlag(RegRuleFlags::NO_INDOOR);
|
|
channelInfo.no10MHz = rule->max_bandwidth_khz < 10 * 1000;
|
|
channelInfo.no20MHz = rule->max_bandwidth_khz < 20 * 1000;
|
|
channelInfo.no80MHz = rule->max_bandwidth_khz < 80 * 1000;
|
|
channelInfo.noHt40Minus = rule->max_bandwidth_khz < 40 * 1000 || rule->HasFlag(RegRuleFlags::NO_HT40MINUS);
|
|
channelInfo.noHt40Plus = rule->max_bandwidth_khz < 40 * 1000 || rule->HasFlag(RegRuleFlags::NO_HT40PLUS);
|
|
|
|
channelInfo.mhz = frequency;
|
|
channelInfo.no10MHz = rule->HasFlag(RegRuleFlags::NO_IR);
|
|
channelInfo.regDomain = classMap.regDomain;
|
|
channelInfo.maxAntennaGain = rule->max_antenna_gain;
|
|
channelInfo.maxEirp = rule->max_eirp;
|
|
channelInfo.bandwidth = bandwidth;
|
|
|
|
info.channels.push_back(std::move(channelInfo));
|
|
}
|
|
}
|
|
return info;
|
|
}
|
|
|
|
|
|
std::vector<int32_t> pipedal::getValidChannels(const std::string&countryIso3661,int32_t maxChannelWidthMhz)
|
|
{
|
|
std::set<int32_t> channels;
|
|
|
|
|
|
WifiBandwidth maxBandwidth;
|
|
if (maxChannelWidthMhz <= 20)
|
|
{
|
|
maxBandwidth = WifiBandwidth::BW20;
|
|
} else if (maxChannelWidthMhz <= 40)
|
|
{
|
|
maxBandwidth = WifiBandwidth::BW40;
|
|
} else if (maxChannelWidthMhz <= 80)
|
|
{
|
|
maxBandwidth = WifiBandwidth::BW80;
|
|
} else // if (maxChannelWidthMhz <= 160)
|
|
{
|
|
maxBandwidth = WifiBandwidth::BW160;
|
|
}
|
|
RegDb ®Db = RegDb::GetInstance();
|
|
auto info = pipedal::getWifiInfo(countryIso3661);
|
|
|
|
for (const auto&channelInfo: info.channels)
|
|
{
|
|
channels.insert(channelInfo.channelNumber);
|
|
}
|
|
|
|
|
|
std::vector<int32_t> result;
|
|
result.reserve(channels.size());
|
|
for (auto v : channels)
|
|
{
|
|
result.push_back(v);
|
|
}
|
|
std::sort(result.begin(),result.end());
|
|
return result;
|
|
}
|
|
int32_t pipedal::getWifiRegClass(const std::string &countryIso3661, int32_t channel, int32_t maxChannelWidthMhz)
|
|
{
|
|
WifiBandwidth maxBandwidth;
|
|
if (maxChannelWidthMhz <= 20)
|
|
{
|
|
maxBandwidth = WifiBandwidth::BW20;
|
|
} else if (maxChannelWidthMhz <= 40)
|
|
{
|
|
maxBandwidth = WifiBandwidth::BW40;
|
|
} else if (maxChannelWidthMhz <= 80)
|
|
{
|
|
maxBandwidth = WifiBandwidth::BW80;
|
|
} else // if (maxChannelWidthMhz <= 160)
|
|
{
|
|
maxBandwidth = WifiBandwidth::BW160;
|
|
}
|
|
RegDb ®Db = RegDb::GetInstance();
|
|
auto info = pipedal::getWifiInfo(countryIso3661);
|
|
|
|
const WifiChannelInfo *bestChannel = nullptr;
|
|
for (const auto&channelInfo: info.channels)
|
|
{
|
|
if (channelInfo.channelNumber == channel && channelInfo.bandwidth <= maxBandwidth)
|
|
{
|
|
if (bestChannel == nullptr || bestChannel->bandwidth < channelInfo.bandwidth)
|
|
{
|
|
bestChannel = &channelInfo;
|
|
}
|
|
|
|
}
|
|
}
|
|
if (bestChannel)
|
|
{
|
|
if (channel >= 1 && channel <= 13) { // alway 20Ghz.
|
|
return 81;
|
|
}
|
|
return bestChannel->regDomain;
|
|
}
|
|
std::vector<int32_t> valid_channels;
|
|
try {
|
|
valid_channels = getValidChannels(countryIso3661,40);
|
|
}
|
|
catch (const std::exception& /**/)
|
|
{
|
|
std::stringstream ss;
|
|
ss << "Channel " << channel << " is not permitted in the country " << countryIso3661 << ".";
|
|
throw std::runtime_error(ss.str());
|
|
|
|
}
|
|
|
|
{
|
|
std::stringstream ss;
|
|
ss << "Channel " << channel << " is not permitted in the country " << countryIso3661 << ". Permitted channels:\n ";
|
|
bool first = true;
|
|
for (auto channel: valid_channels)
|
|
{
|
|
if (!first) ss << ", ";
|
|
first = false;
|
|
ss << channel;
|
|
}
|
|
ss << ".";
|
|
throw std::runtime_error(ss.str());
|
|
}
|
|
|
|
|
|
// Hard-coded values lifted from wpa_supplicant sources. I don't really think it respect the regulatory db.
|
|
|
|
/* Operating class 81 - 2.4 GHz band channels 1..13 */
|
|
// int32_t regClass = -1;
|
|
// if (channel >= 1 && channel <= 11)
|
|
// {
|
|
// regClass = 81;
|
|
// }
|
|
// // lower 5 GHZ.
|
|
// if (channel == 36 || channel == 40 || channel == 44 || channel == 48)
|
|
// {
|
|
// regClass = 115; // 20 Ghz
|
|
// }
|
|
// if (channel == 149 || channel == 153 || channel == 157 || channel == 161)
|
|
// {
|
|
// regClass = 124; // 20Ghz nomadic.
|
|
// }
|
|
// if (regClass == -1)
|
|
// {
|
|
// return -1;
|
|
// }
|
|
}
|
|
|