WiFi Channel Settings.
This commit is contained in:
@@ -458,6 +458,9 @@ public:
|
||||
{
|
||||
this->ws_.close(websocket::close_reason());
|
||||
}
|
||||
virtual std::string getFromAddress() const {
|
||||
return this->fromAddress;
|
||||
}
|
||||
virtual void writeCallback(const std::string &text)
|
||||
{
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual void writeCallback(const std::string& text) = 0;
|
||||
virtual std::string getFromAddress() const = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -43,6 +44,7 @@ private:
|
||||
protected:
|
||||
virtual void onReceive(const std::string_view&text) = 0;
|
||||
public:
|
||||
std::string getFromAddress() const { return writeCallback_->getFromAddress(); }
|
||||
void receive(const std::string_view&text) {
|
||||
onReceive(text);
|
||||
}
|
||||
|
||||
+54
-9
@@ -4,13 +4,27 @@ cmake_minimum_required(VERSION 3.19.0)
|
||||
|
||||
include(FindPkgConfig)
|
||||
|
||||
# Can't get the pkg_check to work.
|
||||
# pkg_check_modules(LIBNL3 "nl-genl-3")
|
||||
# if(!LIBNL3_FOUND)
|
||||
# message(ERROR "libnl-genl-3-dev package not installed.")
|
||||
# else()
|
||||
# message(STATUS "LIBNL3 libraries: ${LIBNL3_LIBRARIES}")
|
||||
# message(STATUS "LBNL3 includes: ${LIBNL3_INCLUDE_DIRS}")
|
||||
# endif()
|
||||
|
||||
# nlgenl-3 library.
|
||||
execute_process(COMMAND ls /usr/include/libnl3/netlink/netlink.h RESULT_VARIABLE LNL3_MISSING OUTPUT_QUIET ERROR_QUIET)
|
||||
if(LNL3_MISSING)
|
||||
message(ERROR " Need to: sudo apt get libnl-3-dev libnl-genl-3-dev ")
|
||||
endif()
|
||||
set(LIBNL3_INCLUDE_DIRS /usr/include/libnl3)
|
||||
set(LIBNL3_LIBRARIES nl-3 nl-genl-3)
|
||||
|
||||
|
||||
pkg_check_modules(SYSTEMD "systemd")
|
||||
if(!SYSTEMD_FOUND)
|
||||
message(ERROR "libsystemd-dev package not installed.")
|
||||
else()
|
||||
message(STATUS "SYSTEMD_LIBRARIES: ${SYSTEMD_LIBRARIES}")
|
||||
message(STATUS "SYSTEMD_INCLUDE_DIRS: ${SYSTEMD_INCLUDE_DIRS}")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -55,8 +69,10 @@ message (STATUS "Cxx flags: ${CMAKE_CXX_FLAGS}" )
|
||||
|
||||
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY)
|
||||
|
||||
add_executable(pipedald BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp
|
||||
RequestHandler.hpp main.cpp json.cpp json.hpp Scratch.cpp Lv2Host.hpp Lv2Host.cpp
|
||||
set (PIPEDAL_SOURCES
|
||||
BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp
|
||||
WifiConfigSettings.hpp WifiConfigSettings.cpp
|
||||
RequestHandler.hpp json.cpp json.hpp Scratch.cpp Lv2Host.hpp Lv2Host.cpp
|
||||
PluginType.hpp PluginType.cpp
|
||||
Lv2Log.hpp Lv2Log.cpp
|
||||
PiPedalSocket.hpp PiPedalSocket.cpp
|
||||
@@ -93,22 +109,51 @@ add_executable(pipedald BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelpe
|
||||
Locale.hpp Locale.cpp
|
||||
Lv2EventBufferWriter.hpp Lv2EventBufferWriter.cpp
|
||||
IpSubnet.hpp
|
||||
)
|
||||
WifiChannels.hpp
|
||||
WifiChannels.cpp
|
||||
RegDb.cpp RegDb.hpp
|
||||
)
|
||||
|
||||
configure_file(config.hpp.in config.hpp)
|
||||
|
||||
#################################
|
||||
add_executable(pipedald ${PIPEDAL_SOURCES} main.cpp
|
||||
)
|
||||
|
||||
include_directories( ${PiPedal_SOURCE_DIR}/. ../build/src)
|
||||
|
||||
target_precompile_headers(pipedald PRIVATE pch.h)
|
||||
|
||||
|
||||
target_include_directories(pipedald PRIVATE
|
||||
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${SYSTEMD_INCLUDE_DIRS}
|
||||
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(pipedald PRIVATE pthread atomic stdc++fs
|
||||
${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${SYSTEMD_LIBRARIES} systemd
|
||||
${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd
|
||||
)
|
||||
#target_link_libraries(pipedald boost_system)
|
||||
|
||||
#################################
|
||||
add_executable(pipedaltest ${PIPEDAL_SOURCES} testMain.cpp
|
||||
jsonTest.cpp
|
||||
WifiChannelsTest.cpp
|
||||
)
|
||||
|
||||
configure_file(config.hpp.in config.hpp)
|
||||
include_directories( ${PiPedal_SOURCE_DIR}/. ../build/src)
|
||||
|
||||
target_precompile_headers(pipedaltest PRIVATE pch.h)
|
||||
|
||||
|
||||
target_include_directories(pipedaltest PRIVATE
|
||||
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(pipedaltest PRIVATE pthread atomic stdc++fs
|
||||
${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd
|
||||
)
|
||||
|
||||
#################################
|
||||
|
||||
|
||||
add_executable(pipedalconfig json.cpp json.hpp
|
||||
|
||||
+31
-9
@@ -95,7 +95,6 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
|
||||
|
||||
this->jackHost->SetNotificationCallbacks(this);
|
||||
|
||||
|
||||
if (configuration.GetMLock())
|
||||
{
|
||||
#ifndef NO_MLOCK
|
||||
@@ -126,10 +125,8 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
|
||||
catch (PiPedalException &e)
|
||||
{
|
||||
Lv2Log::error("Failed to load initial plugin. (%s)", e.what());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IPiPedalModelSubscriber *PiPedalModel::GetNotificationSubscriber(int64_t clientId)
|
||||
@@ -278,7 +275,6 @@ void PiPedalModel::setPedalBoard(int64_t clientId, PedalBoard &pedalBoard)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::setPedalBoardItemEnable(int64_t clientId, int64_t pedalItemId, bool enabled)
|
||||
{
|
||||
std::lock_guard(this->mutex);
|
||||
@@ -318,7 +314,6 @@ PedalBoard PiPedalModel::getPreset(int64_t instanceId)
|
||||
return this->storage.GetPreset(instanceId);
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::setPresetChanged(int64_t clientId, bool value)
|
||||
{
|
||||
if (value != this->hasPresetChanged)
|
||||
@@ -369,14 +364,13 @@ int64_t PiPedalModel::saveCurrentPresetAs(int64_t clientId, const std::string &n
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t PiPedalModel::uploadPreset(const BankFile&bankFile,int64_t uploadAfter)
|
||||
int64_t PiPedalModel::uploadPreset(const BankFile &bankFile, int64_t uploadAfter)
|
||||
{
|
||||
std::lock_guard(this->mutex);
|
||||
|
||||
int64_t newPreset = this->storage.UploadPreset(bankFile,uploadAfter);
|
||||
int64_t newPreset = this->storage.UploadPreset(bankFile, uploadAfter);
|
||||
firePresetsChanged(-1);
|
||||
return newPreset;
|
||||
|
||||
}
|
||||
|
||||
void PiPedalModel::loadPreset(int64_t clientId, int64_t instanceId)
|
||||
@@ -462,6 +456,35 @@ bool PiPedalModel::renamePreset(int64_t clientId, int64_t instanceId, const std:
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::setWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
|
||||
{
|
||||
std::lock_guard lock(this->mutex);
|
||||
// xxx Commit to local!
|
||||
this->storage.SetWifiConfigSettings(wifiConfigSettings);
|
||||
|
||||
{
|
||||
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
|
||||
for (size_t i = 0; i < subscribers.size(); ++i)
|
||||
{
|
||||
t[i] = this->subscribers[i];
|
||||
}
|
||||
size_t n = this->subscribers.size();
|
||||
|
||||
WifiConfigSettings tWifiConfigSettings = storage.GetWifiConfigSettings(); // (the passwordless version)
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
t[i]->OnWifiConfigSettingsChanged(tWifiConfigSettings);
|
||||
}
|
||||
delete[] t;
|
||||
}
|
||||
}
|
||||
WifiConfigSettings PiPedalModel::getWifiConfigSettings()
|
||||
{
|
||||
std::lock_guard lock(this->mutex);
|
||||
return this->storage.GetWifiConfigSettings();
|
||||
}
|
||||
|
||||
JackConfiguration PiPedalModel::getJackConfiguration()
|
||||
{
|
||||
std::lock_guard lock(this->mutex); // copy atomically.
|
||||
@@ -983,4 +1006,3 @@ void PiPedalModel::cancelListenForMidiEvent(int64_t clientId, int64_t clientHand
|
||||
jackHost->SetListenForMidiEvent(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "Banks.hpp"
|
||||
#include "PiPedalConfiguration.hpp"
|
||||
#include "JackServerSettings.hpp"
|
||||
#include "WifiConfigSettings.hpp"
|
||||
|
||||
|
||||
namespace pipedal {
|
||||
@@ -34,6 +35,7 @@ public:
|
||||
virtual void OnLoadPluginPreset(int64_t instanceId,const std::vector<ControlValue>&controlValues) = 0;
|
||||
virtual void OnMidiValueChanged(int64_t instanceId, const std::string&symbol, float value) = 0;
|
||||
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl) = 0;
|
||||
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) = 0;
|
||||
virtual void Close() = 0;
|
||||
};
|
||||
|
||||
@@ -145,6 +147,10 @@ public:
|
||||
void setJackChannelSelection(int64_t clientId,const JackChannelSelection &channelSelection);
|
||||
JackChannelSelection getJackChannelSelection();
|
||||
|
||||
void setWifiConfigSettings(const WifiConfigSettings&wifiConfigSettings);
|
||||
WifiConfigSettings getWifiConfigSettings();
|
||||
|
||||
|
||||
|
||||
int64_t addVuSubscription(int64_t instanceId);
|
||||
void removeVuSubscription(int64_t subscriptionHandle);
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "ShutdownClient.hpp"
|
||||
#include "WifiConfigSettings.hpp"
|
||||
#include "WifiChannels.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace pipedal;
|
||||
@@ -667,6 +669,12 @@ public:
|
||||
{
|
||||
JackHostStatus status = model.getJackStatus();
|
||||
this->Reply(replyTo,"getJackStatus",status);
|
||||
} else if (message == "getWifiChannels")
|
||||
{
|
||||
std::string country;
|
||||
pReader->read(&country);
|
||||
std::vector<WifiChannel> channels = pipedal::getWifiChannels(country.c_str());
|
||||
this->Reply(replyTo,"getWifiChannels",channels);
|
||||
}
|
||||
else if (message == "getPluginPresets")
|
||||
{
|
||||
@@ -688,6 +696,28 @@ public:
|
||||
this->Reply(replyTo,"setJackserverSettings");
|
||||
|
||||
}
|
||||
else if (message == "setWifiConfigSettings") {
|
||||
WifiConfigSettings wifiConfigSettings;
|
||||
pReader->read(&wifiConfigSettings);
|
||||
if (!ShutdownClient::CanUseShutdownClient())
|
||||
{
|
||||
throw PiPedalException("Can't change server settings when running interactively.");
|
||||
}
|
||||
std::string fromAddress = this->getFromAddress();
|
||||
if (!ShutdownClient::IsOnLocalSubnet(fromAddress))
|
||||
{
|
||||
throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
}
|
||||
|
||||
|
||||
this->model.setWifiConfigSettings(wifiConfigSettings);
|
||||
this->Reply(replyTo,"setWifiConfigSettings");
|
||||
}
|
||||
else if (message == "getWifiConfigSettings") {
|
||||
this->Reply(replyTo, "getWifiConfigSettings", model.getWifiConfigSettings());
|
||||
|
||||
}
|
||||
|
||||
else if (message == "getJackServerSettings") {
|
||||
this->Reply(replyTo, "getJackServerSettings", model.GetJackServerSettings());
|
||||
|
||||
@@ -1220,6 +1250,10 @@ public:
|
||||
Send("onJackServerSettingsChanged",jackServerSettings);
|
||||
}
|
||||
|
||||
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) {
|
||||
Send("onWifiConfigSettingsChanged",wifiConfigSettings);
|
||||
}
|
||||
|
||||
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard)
|
||||
{
|
||||
SetCurrentPedalBoardBody body;
|
||||
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
#include "pch.h"
|
||||
#include "RegDb.hpp"
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "PiPedalException.hpp"
|
||||
|
||||
#define REGDB_MAGIC 0x52474442
|
||||
#define REGDB_VERSION 19
|
||||
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace std;
|
||||
|
||||
struct RegDbFileHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t countryOffset;
|
||||
uint32_t countryCount;
|
||||
uint32_t signatureLength;
|
||||
|
||||
void toNs() {
|
||||
this->magic = htonl(this->magic);
|
||||
this->version = htonl(this->version);
|
||||
this->countryOffset = htonl(this->countryOffset);
|
||||
this->countryCount = htonl(this->countryCount);
|
||||
this->signatureLength = htonl(this->signatureLength);
|
||||
}
|
||||
};
|
||||
|
||||
struct CountryHeader {
|
||||
char alpha2[2];
|
||||
uint8_t pad;
|
||||
uint8_t dfsRegion; // first 2 bits only.
|
||||
uint32_t rulesOffset;
|
||||
|
||||
void toNs() {
|
||||
this->rulesOffset = htonl(rulesOffset);
|
||||
}
|
||||
|
||||
};
|
||||
struct RulesCollection {
|
||||
uint32_t ruleCount;
|
||||
uint32_t ruleOffsets[1];
|
||||
|
||||
void toNs() {
|
||||
this->ruleCount = htonl(this->ruleCount);
|
||||
uint32_t *p = ruleOffsets;
|
||||
for (uint32_t i = 0; i < this->ruleCount; ++i)
|
||||
{
|
||||
*p = htonl(*p);
|
||||
++p;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Rule {
|
||||
uint32_t frequencyRangeOffset;
|
||||
uint32_t powerRuleOffset;
|
||||
uint32_t flags;
|
||||
|
||||
void toNs() {
|
||||
frequencyRangeOffset = htonl(frequencyRangeOffset);
|
||||
powerRuleOffset = htonl(powerRuleOffset);
|
||||
flags = htonl(flags);
|
||||
}
|
||||
};
|
||||
|
||||
struct FrequencyRange {
|
||||
uint32_t startFrequency; // in khz.
|
||||
uint32_t endFrequency; // in khz.
|
||||
uint32_t maxBandwidth; // in khz.
|
||||
void toNs() {
|
||||
startFrequency = htonl(startFrequency);
|
||||
endFrequency = htonl(endFrequency);
|
||||
maxBandwidth = htonl(maxBandwidth);
|
||||
}
|
||||
};
|
||||
struct PowerRule {
|
||||
uint32_t maximumAntennaGain;
|
||||
uint32_t maximumEirp;
|
||||
void toNs() {
|
||||
maximumAntennaGain = htonl(maximumAntennaGain);
|
||||
maximumEirp = htonl(maximumEirp);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const char*filePaths[] =
|
||||
{
|
||||
"/usr/lib/crda/regulatory.bin"
|
||||
};
|
||||
|
||||
static std::filesystem::path getFilePath()
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(filePaths)/sizeof(filePaths[0]); ++i)
|
||||
{
|
||||
std::filesystem::path path(filePaths[i]);
|
||||
if (std::filesystem::exists(path)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
throw PiPedalException("Could not find 'regulatory.bin' file.");
|
||||
}
|
||||
|
||||
RegDb::~RegDb()
|
||||
{
|
||||
delete[] pData;
|
||||
}
|
||||
|
||||
RegDb::RegDb()
|
||||
:pData(nullptr)
|
||||
{
|
||||
RegDbFileHeader header;
|
||||
std::filesystem::path path = getFilePath();
|
||||
ifstream f(path);
|
||||
if (!f.is_open()) {
|
||||
stringstream s;
|
||||
s << "Can't read " << path;
|
||||
throw PiPedalException(s.str());
|
||||
}
|
||||
size_t fileSize = std::filesystem::file_size(path);
|
||||
this->pData = new uint8_t[fileSize];
|
||||
f.read((char*)this->pData,fileSize);
|
||||
|
||||
// convert from big-endian to local byte order;
|
||||
RegDbFileHeader *fileHeader = (RegDbFileHeader*)pData;
|
||||
fileHeader->toNs();
|
||||
if (fileHeader->magic != REGDB_MAGIC)
|
||||
{
|
||||
throw PiPedalException("Invalid file format.");
|
||||
}
|
||||
std::unordered_set<uint32_t> visited;
|
||||
|
||||
CountryHeader*countryHeader = (CountryHeader*)(pData + fileHeader->countryOffset);
|
||||
for (uint32_t i = 0; i < fileHeader->countryCount; ++i)
|
||||
{
|
||||
countryHeader->toNs();
|
||||
if (visited.count(countryHeader->rulesOffset) == 0)
|
||||
{
|
||||
visited.insert(countryHeader->rulesOffset);
|
||||
RulesCollection*pRules = (RulesCollection*)(pData + countryHeader->rulesOffset);
|
||||
pRules->toNs();
|
||||
for (uint32_t nRule = 0; nRule < pRules->ruleCount; ++nRule)
|
||||
{
|
||||
uint32_t ruleOffset = pRules->ruleOffsets[nRule];
|
||||
if (visited.count(ruleOffset) == 0)
|
||||
{
|
||||
visited.insert(ruleOffset);
|
||||
Rule *pRule = (Rule*)(pData + ruleOffset);
|
||||
pRule->toNs();
|
||||
|
||||
if (visited.count(pRule->frequencyRangeOffset) == 0)
|
||||
{
|
||||
visited.insert(pRule->frequencyRangeOffset);
|
||||
|
||||
FrequencyRange*pf = (FrequencyRange*)(pData + pRule->frequencyRangeOffset);
|
||||
pf->toNs();
|
||||
}
|
||||
if (visited.count(pRule->powerRuleOffset) == 0)
|
||||
{
|
||||
visited.insert(pRule->powerRuleOffset);
|
||||
|
||||
PowerRule *pr = (PowerRule*)(pData + pRule->powerRuleOffset);
|
||||
pr->toNs();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++countryHeader;
|
||||
}
|
||||
|
||||
}
|
||||
bool CrdbRules::GetFrequencyRule(uint32_t frequencyRuleMHz, FrequencyRule*pResult)
|
||||
{
|
||||
memset(pResult,0,sizeof(FrequencyRule));
|
||||
RulesCollection*pRules = (RulesCollection*)(regDb->pData + countryOffset);
|
||||
uint32_t frequencyRuleKHz = frequencyRuleMHz*1000;
|
||||
for (uint32_t i = 0; i < pRules->ruleCount; ++i)
|
||||
{
|
||||
Rule *pRule = (Rule*)(regDb->pData + pRules->ruleOffsets[i]);
|
||||
FrequencyRange*pf = (FrequencyRange*)(regDb->pData + pRule->frequencyRangeOffset);
|
||||
if (frequencyRuleKHz >= pf->startFrequency && frequencyRuleKHz < pf->endFrequency)
|
||||
{
|
||||
PowerRule *pr = (PowerRule*)(regDb->pData + pRule->powerRuleOffset);
|
||||
pResult->flags = (RegRuleFlags)(pRule->flags);
|
||||
pResult->maximumAntennaGain = pr->maximumAntennaGain;
|
||||
pResult->maximumEirp = pr->maximumEirp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
CrdbRules*RegDb::GetCrdbRules(const char*countryCodeIso633)
|
||||
{
|
||||
const char c0 = countryCodeIso633[0];
|
||||
if (c0 == '\0') return nullptr;
|
||||
const char c1 = countryCodeIso633[1];
|
||||
|
||||
RegDbFileHeader *fileHeader = (RegDbFileHeader*)pData;
|
||||
CountryHeader*countryHeader = (CountryHeader*)(pData + fileHeader->countryOffset);
|
||||
for (uint32_t i = 0; i < fileHeader->countryCount; ++i)
|
||||
{
|
||||
if (countryHeader->alpha2[0] == c0 && countryHeader->alpha2[1] == c1) {
|
||||
return new CrdbRules(
|
||||
this,
|
||||
countryHeader->rulesOffset,
|
||||
(DfsRegion)(countryHeader->dfsRegion & 0x03)
|
||||
);
|
||||
}
|
||||
++countryHeader;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CrdbRules::CrdbRules(RegDb *regDb,uint32_t countryOffset, DfsRegion dfsRegion )
|
||||
: regDb(regDb),
|
||||
countryOffset(countryOffset),
|
||||
dfsRegion(dfsRegion)
|
||||
{
|
||||
}
|
||||
|
||||
DfsRegion CrdbRules::GetDfsRegion() const {
|
||||
return this->dfsRegion;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
// sync with <linux/nl80211.h
|
||||
enum class RegRuleFlags {
|
||||
NO_OFDM = 1<<0,
|
||||
NO_CCK = 1<<1,
|
||||
NO_INDOOR = 1<<2,
|
||||
NO_OUTDOOR = 1<<3,
|
||||
DFS = 1<<4,
|
||||
PTP_ONLY = 1<<5,
|
||||
PTMP_ONLY = 1<<6,
|
||||
NO_IR = 1<<7,
|
||||
__NO_IBSS = 1<<8,
|
||||
AUTO_BW = 1<<11,
|
||||
IR_CONCURRENT = 1<<12,
|
||||
NO_HT40MINUS = 1<<13,
|
||||
NO_HT40PLUS = 1<<14,
|
||||
NO_80MHZ = 1<<15,
|
||||
NO_160MHZ = 1<<16,
|
||||
NO_HE = 1<<17,
|
||||
};
|
||||
|
||||
enum class DfsRegion {
|
||||
Unset = 0,
|
||||
Fcc = 1,
|
||||
Etsi = 2,
|
||||
Japan = 3
|
||||
};
|
||||
|
||||
struct FrequencyRule {
|
||||
uint32_t maximumAntennaGain; // in mBiu (100*dBi)
|
||||
uint32_t maximumEirp; // in mBm (100*dBm)
|
||||
RegRuleFlags flags;
|
||||
|
||||
bool hasFlag(RegRuleFlags flag) const { return (((int)flag) & (int)this->flags) != 0; }
|
||||
};
|
||||
|
||||
class RegDb;
|
||||
|
||||
class CrdbRules {
|
||||
friend class RegDb;
|
||||
RegDb *regDb = nullptr;
|
||||
uint32_t countryOffset;
|
||||
DfsRegion dfsRegion;
|
||||
CrdbRules(RegDb *regDb,uint32_t countryOffset, DfsRegion dfsRegion );
|
||||
public:
|
||||
virtual DfsRegion GetDfsRegion() const;
|
||||
virtual bool GetFrequencyRule(uint32_t frequencyMHz,FrequencyRule*pResult);
|
||||
};
|
||||
|
||||
class RegDb {
|
||||
private:
|
||||
friend class CrdbRules;
|
||||
uint8_t*pData;
|
||||
|
||||
public:
|
||||
RegDb();
|
||||
~RegDb();
|
||||
CrdbRules *GetCrdbRules(const char*countryCodeIso6333);
|
||||
bool isValid() const { return pData != nullptr; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -6,6 +6,9 @@
|
||||
#include <arpa/inet.h>
|
||||
#include "Lv2Log.hpp"
|
||||
#include <sstream>
|
||||
#include <sys/types.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
@@ -138,3 +141,52 @@ bool ShutdownClient::SetJackServerConfiguration(const JackServerSettings & jackS
|
||||
|
||||
return WriteMessage(s.str().c_str());
|
||||
}
|
||||
|
||||
bool ShutdownClient::IsOnLocalSubnet(const std::string&fromAddress)
|
||||
{
|
||||
std::string address = fromAddress;
|
||||
if (address.size() == 0) return false;
|
||||
char lastChar = address[address.size()-1];
|
||||
if (address[0] != '[' || lastChar != ']') // i.e. not an ipv6 address;
|
||||
{
|
||||
size_t pos = address.find_last_of(':');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
address = address.substr(0,pos);
|
||||
}
|
||||
}
|
||||
if (address[0] == '[') return false;
|
||||
|
||||
char buf[512];
|
||||
|
||||
struct in_addr inetAddr;
|
||||
|
||||
memset(&inetAddr,0,sizeof(inetAddr));
|
||||
|
||||
if (inet_pton(AF_INET,address.c_str(),&inetAddr) == 0) {
|
||||
return false;
|
||||
}
|
||||
uint32_t remoteAddress = htonl(inetAddr.s_addr);
|
||||
|
||||
struct ifaddrs *ifap = nullptr;
|
||||
if (getifaddrs(&ifap) != 0) return false;
|
||||
|
||||
bool result = false;
|
||||
for (ifaddrs*p = ifap; p != nullptr; p = p->ifa_next) {
|
||||
if (p->ifa_addr->sa_family == AF_INET && p->ifa_addr != nullptr && p->ifa_netmask != nullptr) { // TODO: Add support for AF_INET6
|
||||
uint32_t netmask = htonl(((sockaddr_in*)(p->ifa_netmask))->sin_addr.s_addr);
|
||||
uint32_t ifAddr = htonl(((sockaddr_in*)(p->ifa_addr))->sin_addr.s_addr);
|
||||
|
||||
if ((netmask & ifAddr) == (netmask & remoteAddress))
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
freeifaddrs(ifap);
|
||||
return result;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ public:
|
||||
static bool CanUseShutdownClient();
|
||||
static bool RequestShutdown(bool restart);
|
||||
static bool SetJackServerConfiguration(const JackServerSettings & jackServerSettings);
|
||||
static bool IsOnLocalSubnet(const std::string&fromAddress);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -11,6 +11,8 @@ using namespace pipedal;
|
||||
const char *BANK_EXTENSION = ".bank";
|
||||
const char *BANKS_FILENAME = "index.banks";
|
||||
|
||||
#define WIFI_CONFIG_SETTINGS_FILENAME "wifiConfigSettings.json";
|
||||
|
||||
Storage::Storage()
|
||||
{
|
||||
SetDataRoot("~/var/PiPedal");
|
||||
@@ -157,6 +159,7 @@ void Storage::Initialize()
|
||||
catch (const std::exception &)
|
||||
{
|
||||
}
|
||||
LoadWifiConfigSettings();
|
||||
}
|
||||
|
||||
void Storage::LoadBank(int64_t instanceId)
|
||||
@@ -683,3 +686,72 @@ int64_t Storage::UploadPreset(const BankFile&bankFile,int64_t uploadAfter)
|
||||
this->SaveCurrentBank();
|
||||
return lastPreset;
|
||||
}
|
||||
|
||||
void Storage::SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings)
|
||||
{
|
||||
WifiConfigSettings copyToSave = wifiConfigSettings;
|
||||
copyToSave.rebootRequired_ = false;
|
||||
if (!copyToSave.enable_)
|
||||
{
|
||||
copyToSave.hasPassword_ = false;
|
||||
}
|
||||
copyToSave.password_ = "";
|
||||
|
||||
std::filesystem::path path = this->dataRoot / WIFI_CONFIG_SETTINGS_FILENAME;
|
||||
{
|
||||
std::ofstream f(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw PiPedalException("Unable to write to " + ((std::string)path));
|
||||
}
|
||||
json_writer writer(f);
|
||||
writer.write(©ToSave);
|
||||
}
|
||||
|
||||
WifiConfigSettings copyToStore = wifiConfigSettings;
|
||||
if (copyToStore.enable_)
|
||||
{
|
||||
copyToStore.hasPassword_ = copyToStore.password_.length() != 0 || this->wifiConfigSettings.hasPassword_;
|
||||
} else {
|
||||
copyToStore.hasPassword_ = false;
|
||||
}
|
||||
copyToStore.password_ = "";
|
||||
copyToStore.rebootRequired_ = true;
|
||||
if (copyToStore.enable_ && !copyToStore.hasPassword_)
|
||||
{
|
||||
copyToStore.hasPassword_ = this->wifiConfigSettings.hasPassword_;
|
||||
}
|
||||
this->wifiConfigSettings = copyToStore;
|
||||
}
|
||||
|
||||
void Storage::LoadWifiConfigSettings()
|
||||
{
|
||||
std::filesystem::path path = this->dataRoot / WIFI_CONFIG_SETTINGS_FILENAME;
|
||||
|
||||
try {
|
||||
if (std::filesystem::is_regular_file(path))
|
||||
{
|
||||
std::ifstream f(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw PiPedalException("Unable to write to " + ((std::string)path));
|
||||
}
|
||||
json_reader reader(f);
|
||||
WifiConfigSettings wifiConfigSettings;
|
||||
reader.read(&wifiConfigSettings);
|
||||
this->wifiConfigSettings = wifiConfigSettings;
|
||||
}
|
||||
} catch (const std::exception&)
|
||||
{
|
||||
|
||||
}
|
||||
this->wifiConfigSettings.valid_ = true;
|
||||
}
|
||||
|
||||
|
||||
WifiConfigSettings Storage::GetWifiConfigSettings()
|
||||
{
|
||||
return this->wifiConfigSettings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+7
-1
@@ -5,6 +5,7 @@
|
||||
#include "Presets.hpp"
|
||||
#include "Banks.hpp"
|
||||
#include "JackConfiguration.hpp"
|
||||
#include "WifiConfigSettings.hpp"
|
||||
|
||||
|
||||
namespace pipedal {
|
||||
@@ -17,7 +18,7 @@ private:
|
||||
std::filesystem::path dataRoot;
|
||||
BankIndex bankIndex;
|
||||
BankFile currentBank;
|
||||
|
||||
|
||||
private:
|
||||
static std::string SafeEncodeName(const std::string& name);
|
||||
static std::string SafeDecodeName(const std::string& name);
|
||||
@@ -39,6 +40,7 @@ private:
|
||||
std::string GetPresetCopyName(const std::string &name);
|
||||
bool isJackChannelSelectionValid = false;
|
||||
JackChannelSelection jackChannelSelection;
|
||||
WifiConfigSettings wifiConfigSettings;
|
||||
public:
|
||||
Storage();
|
||||
void Initialize();
|
||||
@@ -51,6 +53,7 @@ public:
|
||||
const BankIndex & GetBanks() const { return bankIndex; }
|
||||
|
||||
|
||||
void LoadWifiConfigSettings();
|
||||
void LoadBank(int64_t instanceId);
|
||||
const PedalBoard& GetCurrentPreset();
|
||||
void saveCurrentPreset(const PedalBoard&pedalBoard);
|
||||
@@ -77,6 +80,9 @@ public:
|
||||
void SetJackChannelSelection(const JackChannelSelection&channelSelection);
|
||||
const JackChannelSelection&GetJackChannelSelection(const JackConfiguration &jackConfiguration);
|
||||
|
||||
void SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings);
|
||||
WifiConfigSettings GetWifiConfigSettings();
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,615 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include <string>
|
||||
#include <net/if.h>
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/genl/genl.h>
|
||||
#include <netlink/genl/family.h>
|
||||
#include <netlink/genl/ctrl.h>
|
||||
#include <netlink/msg.h>
|
||||
#include <netlink/attr.h>
|
||||
#include <linux/nl80211.h>
|
||||
|
||||
#include <linux/nl80211.h>
|
||||
#include "PiPedalException.hpp"
|
||||
#include "WifiChannels.hpp"
|
||||
#include "Lv2Log.hpp"
|
||||
|
||||
#include "RegDb.hpp"
|
||||
/*
|
||||
Heavily based on code from iw (1) command.
|
||||
https://kernel.googlesource.com/pub/scm/linux/kernel/git/jberg/iw/+/v0.9/COPYING
|
||||
|
||||
Copyright (c) 2007, 2008 Johannes Berg
|
||||
Copyright (c) 2007 Andy Lutomirski
|
||||
Copyright (c) 2007 Mike Kershaw
|
||||
Copyright (c) 2008 Luis R. Rodriguez
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
struct MHzToChannel {
|
||||
char band;
|
||||
int channel;
|
||||
int mhz;
|
||||
};
|
||||
|
||||
MHzToChannel mhzToChannels[] = {
|
||||
{'g', 1, 2412},
|
||||
{'g',2, 2417},
|
||||
{'g',3, 2422},
|
||||
{'g',4, 2427},
|
||||
{'g',5, 2432},
|
||||
{'g',6, 2437},
|
||||
{'g',7, 2442},
|
||||
{'g',8, 2447},
|
||||
{'g',9, 2452},
|
||||
{'g',10, 2457},
|
||||
{'g',11, 2462},
|
||||
{'g',12, 2467},
|
||||
{'g',13, 2472},
|
||||
{'g',14, 2484},
|
||||
{'a',7, 5035},
|
||||
{'a',8, 5040},
|
||||
{'a',9, 5045},
|
||||
{'a',11, 5055},
|
||||
{'a',12, 5060},
|
||||
{'a',16, 5080},
|
||||
{'a',32, 5160},
|
||||
{'a',34, 5170},
|
||||
{'a',36, 5180},
|
||||
{'a',38, 5190},
|
||||
{'a',40, 5200},
|
||||
{'a',42, 5210},
|
||||
{'a',44, 5220},
|
||||
{'a',46, 5230},
|
||||
{'a',48, 5240},
|
||||
{'a',50, 5250},
|
||||
{'a',52, 5260},
|
||||
{'a',54, 5270},
|
||||
{'a',56, 5280},
|
||||
{'a',58, 5290},
|
||||
{'a',60, 5300},
|
||||
{'a',62, 5310},
|
||||
{'a',64, 5320},
|
||||
{'a',68, 5340},
|
||||
{'a',96, 5480},
|
||||
{'a',100, 5500},
|
||||
{'a',102, 5510},
|
||||
{'a',104, 5520},
|
||||
{'a',106, 5530},
|
||||
{'a',108, 5540},
|
||||
{'a',110, 5550},
|
||||
{'a',112, 5560},
|
||||
{'a',114, 5570},
|
||||
{'a',116, 5580},
|
||||
{'a',118, 5590},
|
||||
{'a',120, 5600},
|
||||
{'a',122, 5610},
|
||||
{'a',124, 5620},
|
||||
{'a',126, 5630},
|
||||
{'a',128, 5640},
|
||||
{'a',132, 5660},
|
||||
{'a',134, 5670},
|
||||
{'a',136, 5680},
|
||||
{'a',138, 5690},
|
||||
{'a',140, 5700},
|
||||
{'a',142, 5710},
|
||||
{'a',144, 5720},
|
||||
{'a',149, 5745},
|
||||
{'a',151, 5755},
|
||||
{'a',153, 5765},
|
||||
{'a',155, 5775},
|
||||
{'a',157, 5785},
|
||||
{'a',159, 5795},
|
||||
{'a',161, 5805},
|
||||
{'a',163, 5815},
|
||||
{'a',165, 5825},
|
||||
{'a',167, 5835},
|
||||
{'a',169, 5845},
|
||||
{'a',171, 5855},
|
||||
{'a',173, 5865},
|
||||
{'a',175, 5875},
|
||||
{'a',177, 5885},
|
||||
{'a',180, 5900},
|
||||
{'a',182, 5910},
|
||||
{'a',18, 5915},
|
||||
{'a',184, 5920},
|
||||
{'a',187, 5935},
|
||||
{'a',188, 5940},
|
||||
{'a',189, 5945},
|
||||
{'a',192, 5960},
|
||||
{'a',196, 5980},
|
||||
};
|
||||
|
||||
|
||||
bool MHzToChannelNumber(int mhz, char*pBand, int*pChannel)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(mhzToChannels)/sizeof(mhzToChannels[0]); ++i)
|
||||
{
|
||||
if (std::abs(mhz-mhzToChannels[i].mhz) <= 1) {
|
||||
*pBand = mhzToChannels[i].band;
|
||||
*pChannel = mhzToChannels[i].channel;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static nla_policy *makeFreqPolicy()
|
||||
{
|
||||
nla_policy* result = new nla_policy[NL80211_FREQUENCY_ATTR_MAX + 1];
|
||||
memset(result,0,sizeof(nla_policy[NL80211_FREQUENCY_ATTR_MAX + 1]));
|
||||
|
||||
result[NL80211_FREQUENCY_ATTR_FREQ] = {NLA_U32,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_DISABLED] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_IR] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_RADAR] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_INDOOR_ONLY] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_INDOOR_ONLY] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_20MHZ] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_20MHZ] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_80MHZ] = {NLA_FLAG,0,0};
|
||||
result[NL80211_FREQUENCY_ATTR_NO_160MHZ] = {NLA_FLAG,0,0};
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct nla_policy* freq_policy = makeFreqPolicy();
|
||||
|
||||
|
||||
static nla_policy *makeRatePolicy() {
|
||||
nla_policy* result = new nla_policy[NL80211_BITRATE_ATTR_MAX + 1];
|
||||
memset(result,0,sizeof(nla_policy[NL80211_BITRATE_ATTR_MAX + 1]));
|
||||
result[NL80211_BITRATE_ATTR_RATE] = {.type = NLA_U32};
|
||||
result[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = {.type = NLA_FLAG};
|
||||
return result;
|
||||
};
|
||||
|
||||
static struct nla_policy* rate_policy = makeRatePolicy();
|
||||
|
||||
struct IfType {
|
||||
enum nl80211_iftype iftype;
|
||||
const char*name;
|
||||
};
|
||||
static struct IfType ifTypes[] = {
|
||||
{NL80211_IFTYPE_UNSPECIFIED,"Unspecified"},
|
||||
{NL80211_IFTYPE_ADHOC,"Adhoc"},
|
||||
{NL80211_IFTYPE_STATION,"Station"},
|
||||
{NL80211_IFTYPE_AP,"AP"},
|
||||
{NL80211_IFTYPE_AP_VLAN,"VLAN"},
|
||||
{NL80211_IFTYPE_WDS,"WDS"},
|
||||
{NL80211_IFTYPE_MONITOR,"Monitor"},
|
||||
{NL80211_IFTYPE_MESH_POINT,"Mesh point"},
|
||||
{NL80211_IFTYPE_P2P_CLIENT,"Client"},
|
||||
{NL80211_IFTYPE_P2P_GO,"P2P Go"},
|
||||
{NL80211_IFTYPE_P2P_DEVICE,"P2P"},
|
||||
{NL80211_IFTYPE_OCB,"OCB"},
|
||||
{NL80211_IFTYPE_NAN,"NAN"},
|
||||
|
||||
};
|
||||
const char *iftype_name(enum nl80211_iftype iftype)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(ifTypes)/sizeof(ifTypes[0]); ++i)
|
||||
{
|
||||
if (ifTypes[i].iftype == iftype)
|
||||
{
|
||||
return ifTypes[i].name;
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
static int phy_lookup(const char *name)
|
||||
{
|
||||
char buf[200];
|
||||
int fd, pos;
|
||||
snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
|
||||
fd = open(buf, O_RDONLY);
|
||||
pos = read(fd, buf, sizeof(buf) - 1);
|
||||
if (pos < 0)
|
||||
return -1;
|
||||
buf[pos] = '\0';
|
||||
return atoi(buf);
|
||||
}
|
||||
|
||||
static int getWifiName_callback(struct nl_msg *msg, void *arg);
|
||||
static int getWifiInfo_callback(struct nl_msg *msg, void *arg);
|
||||
|
||||
struct WifiChannelInfo
|
||||
{
|
||||
int channelNumber = -1;
|
||||
char band = 'g'; // 'g' or 'a'
|
||||
int mhz = 0;
|
||||
bool disabled = false;
|
||||
bool ir = true;
|
||||
bool radarDetection = false;
|
||||
std::vector<float> bitrates;
|
||||
bool indoorOnly = false;
|
||||
bool noHt40Minus = false;
|
||||
bool noHt40Plus = false;
|
||||
bool no10MHz = false;
|
||||
bool no20MHz = false;
|
||||
bool no80MHz = false;
|
||||
bool no160MHz = false;
|
||||
};
|
||||
|
||||
struct WifiBand
|
||||
{
|
||||
int index;
|
||||
std::vector<WifiChannelInfo> channels;
|
||||
};
|
||||
|
||||
struct WifiInfo
|
||||
{
|
||||
std::string name;
|
||||
std::vector<WifiBand> bands;
|
||||
std::vector<std::string> supportedIfTypes;
|
||||
};
|
||||
class NetLink
|
||||
{
|
||||
private:
|
||||
|
||||
std::vector<WifiBand> result;
|
||||
nl_sock*socket = nullptr;
|
||||
nl_cache*cache = nullptr;
|
||||
genl_family*nl80211 = nullptr;
|
||||
|
||||
int errorCode = -1;
|
||||
|
||||
static int finish_handler(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
NetLink*this_ = (NetLink*)arg;
|
||||
this_->errorCode = 0;
|
||||
return NL_SKIP;
|
||||
}
|
||||
static int ack_handler(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
NetLink*this_ = (NetLink*)arg;
|
||||
this_->errorCode = 0;
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
|
||||
void *arg)
|
||||
{
|
||||
NetLink*this_ = (NetLink*)arg;
|
||||
this_->errorCode = err->error;
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
|
||||
static int info_phy_handler(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
return ((NetLink *)arg)->InfoPhyHandler(msg, (WifiInfo *)arg);
|
||||
}
|
||||
int InfoPhyHandler(struct nl_msg *msg, WifiInfo *result)
|
||||
{
|
||||
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
|
||||
struct genlmsghdr *gnlh = (genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
|
||||
struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
|
||||
struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
|
||||
struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
|
||||
struct nlattr *nl_band;
|
||||
struct nlattr *nl_freq;
|
||||
struct nlattr *nl_rate;
|
||||
struct nlattr *nl_mode;
|
||||
int bandidx = 1;
|
||||
int rem_band, rem_freq, rem_rate, rem_mode;
|
||||
int open;
|
||||
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||
genlmsg_attrlen(gnlh, 0), NULL);
|
||||
if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
|
||||
return NL_SKIP;
|
||||
if (tb_msg[NL80211_ATTR_WIPHY_NAME])
|
||||
result->name = nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME]);
|
||||
|
||||
nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
|
||||
{
|
||||
WifiBand band;
|
||||
band.index = bandidx;
|
||||
|
||||
bandidx++;
|
||||
nla_parse(tb_band, NL80211_BAND_ATTR_MAX, (nlattr *)nla_data(nl_band),
|
||||
nla_len(nl_band), NULL);
|
||||
|
||||
nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq)
|
||||
{
|
||||
nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, (nlattr *)nla_data(nl_freq),
|
||||
nla_len(nl_freq), freq_policy);
|
||||
if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
|
||||
continue;
|
||||
|
||||
WifiChannelInfo channel;
|
||||
|
||||
channel.mhz = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
|
||||
open = 0;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
|
||||
channel.disabled = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
|
||||
channel.ir = false;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
|
||||
channel.radarDetection = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ])
|
||||
channel.no10MHz = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY])
|
||||
channel.indoorOnly = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
|
||||
channel.no20MHz = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
|
||||
channel.noHt40Minus = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
|
||||
channel.noHt40Plus = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
|
||||
channel.no80MHz = true;
|
||||
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
|
||||
channel.no160MHz = true;
|
||||
|
||||
if (MHzToChannelNumber(channel.mhz,&channel.band, &channel.channelNumber))
|
||||
{
|
||||
band.channels.push_back(std::move(channel));
|
||||
} else {
|
||||
// throw PiPedalException("Frequency not recognized.");
|
||||
}
|
||||
}
|
||||
nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate)
|
||||
{
|
||||
nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, (nlattr *)nla_data(nl_rate),
|
||||
nla_len(nl_rate), rate_policy);
|
||||
if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
|
||||
continue;
|
||||
//channel.bitRates.push_back(0.1f * nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
|
||||
}
|
||||
result->bands.push_back(std::move(band));
|
||||
}
|
||||
if (!tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES])
|
||||
return NL_SKIP;
|
||||
|
||||
nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode)
|
||||
result->supportedIfTypes.push_back(iftype_name((nl80211_iftype)(nl_mode->nla_type)));
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
public:
|
||||
~NetLink()
|
||||
{
|
||||
if (this->cache)
|
||||
{
|
||||
nl_cache_free(cache);
|
||||
cache = nullptr;
|
||||
}
|
||||
if (nl80211 != nullptr)
|
||||
{
|
||||
genl_family_put(nl80211); // (release reference)
|
||||
nl80211 = nullptr;
|
||||
}
|
||||
if (socket)
|
||||
{
|
||||
nl_close(socket);
|
||||
nl_socket_free(socket);
|
||||
socket = nullptr;
|
||||
}
|
||||
}
|
||||
NetLink()
|
||||
{
|
||||
socket = nl_socket_alloc();
|
||||
if (!socket)
|
||||
{
|
||||
throw PiPedalException("Can't allocate netlink socket.");
|
||||
}
|
||||
nl_socket_set_buffer_size(socket, 8192, 8192);
|
||||
|
||||
if (genl_connect(socket))
|
||||
{
|
||||
throw PiPedalException("Failed to connect to netlink socket.");
|
||||
}
|
||||
if (genl_ctrl_alloc_cache(this->socket,&this->cache) != 0) {
|
||||
throw PiPedalException("Failed to allocate generic netlink cache.");
|
||||
}
|
||||
this->nl80211 = genl_ctrl_search_by_name(this->cache, "nl80211");
|
||||
if (!this->nl80211) {
|
||||
throw PiPedalException("nl80211 family not found.");
|
||||
}
|
||||
}
|
||||
void GetAvailableChannels(const char *phyName, WifiInfo *result)
|
||||
{
|
||||
|
||||
struct nl_msg *msg = nullptr;
|
||||
struct nl_cb*cb = nullptr;
|
||||
int devidx = 0;
|
||||
int err;
|
||||
|
||||
devidx = phy_lookup(phyName);
|
||||
try {
|
||||
|
||||
|
||||
msg = nlmsg_alloc();
|
||||
if (!msg)
|
||||
{
|
||||
throw PiPedalException("failed to allocate netlink message");
|
||||
}
|
||||
cb = nl_cb_alloc(NL_CB_DEFAULT);
|
||||
if (!cb)
|
||||
{
|
||||
throw PiPedalException("failed to allocate netlink callbacks");
|
||||
}
|
||||
genlmsg_put(msg, 0, 0, genl_family_get_id(this->nl80211), 0,
|
||||
0, NL80211_CMD_GET_WIPHY, 0);
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
|
||||
|
||||
this->errorCode = 0;
|
||||
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, info_phy_handler, result);
|
||||
|
||||
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, this);
|
||||
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, this);
|
||||
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, this);
|
||||
|
||||
err = nl_send_auto_complete(this->socket, msg);
|
||||
|
||||
if (err < 0)
|
||||
throw PiPedalException("Command failed.");
|
||||
nl_recvmsgs(this->socket, cb);
|
||||
nlmsg_free(msg);
|
||||
if (this->errorCode != 0)
|
||||
{
|
||||
throw PiPedalException("Command failed.");
|
||||
}
|
||||
nl_cb_put(cb);
|
||||
return;
|
||||
nla_put_failure:
|
||||
throw PiPedalException("NLA Put failure.");
|
||||
} catch (const std::exception&e)
|
||||
{
|
||||
if (cb != nullptr)
|
||||
{
|
||||
nl_cb_put(cb);
|
||||
cb = nullptr;
|
||||
}
|
||||
if (msg != nullptr)
|
||||
{
|
||||
nlmsg_free(msg);
|
||||
msg = nullptr;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WifiInfo getWifiInfo(const char *phyName)
|
||||
{
|
||||
int index = phy_lookup(phyName);
|
||||
if (index == -1)
|
||||
{
|
||||
throw PiPedalArgumentException("No such device.");
|
||||
}
|
||||
|
||||
NetLink netLink;
|
||||
WifiInfo result;
|
||||
netLink.GetAvailableChannels(phyName,&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
const char*allowOutdoorsLocales[] = {
|
||||
"US","TW","SG",
|
||||
|
||||
};
|
||||
|
||||
static bool allowOutdoors(const char*countryCode)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(allowOutdoorsLocales)/sizeof(allowOutdoorsLocales[0]); ++i)
|
||||
{
|
||||
if (strcmp(allowOutdoorsLocales[i],countryCode) == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool firstTime = true;
|
||||
static std::unique_ptr<RegDb> g_regDb;
|
||||
std::vector<WifiChannel> pipedal::getWifiChannels(const char*countryIso3661)
|
||||
{
|
||||
// Use the wifi physical device to get a list of channels
|
||||
// that the hardware supports.
|
||||
// The override the hardware's regulation flags using regulation.bin
|
||||
// database info for the selected country code.
|
||||
// If the regulation.bin database doesn't have a matching record,
|
||||
// just pass the data that the wifi reported.
|
||||
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
try {
|
||||
g_regDb = std::make_unique<RegDb>();
|
||||
} catch (const std::exception& e)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "Failed to open the Wi-Fi regulations.bin database. " << e.what();
|
||||
Lv2Log::error(s.str());
|
||||
}
|
||||
}
|
||||
std::vector<WifiChannel> result;
|
||||
|
||||
WifiInfo wifiInfo = getWifiInfo("phy0");
|
||||
std::unique_ptr<CrdbRules> crdbRules = nullptr;
|
||||
if (g_regDb) {
|
||||
crdbRules = std::unique_ptr<CrdbRules>(g_regDb->GetCrdbRules(countryIso3661));
|
||||
if (!crdbRules) {
|
||||
crdbRules = std::unique_ptr<CrdbRules>(g_regDb->GetCrdbRules("00")); // unset default.
|
||||
}
|
||||
}
|
||||
for (auto &band : wifiInfo.bands)
|
||||
{
|
||||
for (auto &channel: band.channels) {
|
||||
if (crdbRules)
|
||||
{
|
||||
FrequencyRule crdbRule;
|
||||
if (crdbRules->GetFrequencyRule(channel.mhz,&crdbRule))
|
||||
{
|
||||
channel.disabled = false;
|
||||
channel.radarDetection = crdbRule.hasFlag(RegRuleFlags::DFS);
|
||||
channel.ir = !crdbRule.hasFlag(RegRuleFlags::NO_IR);
|
||||
channel.indoorOnly = crdbRule.hasFlag(RegRuleFlags::NO_OUTDOOR);
|
||||
if (crdbRule.hasFlag(RegRuleFlags::PTP_ONLY)
|
||||
|| crdbRule.hasFlag(RegRuleFlags::PTMP_ONLY))
|
||||
{
|
||||
channel.disabled = true;
|
||||
|
||||
}
|
||||
} else {
|
||||
channel.disabled = true;
|
||||
}
|
||||
}
|
||||
if ((!channel.disabled) && (!channel.radarDetection) && channel.ir)
|
||||
{
|
||||
WifiChannel ch;
|
||||
std::stringstream s;
|
||||
s << channel.band << channel.channelNumber;
|
||||
ch.channelId_ = s.str();
|
||||
|
||||
std::stringstream t;
|
||||
t << channel.channelNumber;
|
||||
if (channel.band == 'a')
|
||||
t << " (5GHz)";
|
||||
else
|
||||
t << " (2.4GHz)";
|
||||
|
||||
if (channel.indoorOnly) {
|
||||
t << " Indoor only";
|
||||
}
|
||||
ch.channelName_ = t.str();
|
||||
result.push_back(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(WifiChannel)
|
||||
JSON_MAP_REFERENCE(WifiChannel,channelId)
|
||||
JSON_MAP_REFERENCE(WifiChannel,channelName)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "json.hpp"
|
||||
namespace pipedal {
|
||||
|
||||
class WifiChannel {
|
||||
public:
|
||||
std::string channelId_;
|
||||
std::string channelName_;
|
||||
|
||||
DECLARE_JSON_MAP(WifiChannel);
|
||||
};
|
||||
|
||||
std::vector<WifiChannel> getWifiChannels(const char*countryIso3661);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "pch.h"
|
||||
#include "catch.hpp"
|
||||
#include <sstream>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "WifiChannels.hpp"
|
||||
|
||||
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
|
||||
|
||||
TEST_CASE( "Wifi Channel Test", "[wifi_channels_test]" ) {
|
||||
std::vector<WifiChannel> result = pipedal::getWifiChannels("CA");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "pch.h"
|
||||
#include "WifiConfigSettings.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
JSON_MAP_BEGIN(WifiConfigSettings)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,valid)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,wifiWarningGiven)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,rebootRequired)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,enable)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,hotspotName)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,hasPassword)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,password)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,countryCode)
|
||||
JSON_MAP_REFERENCE(WifiConfigSettings,channel)
|
||||
JSON_MAP_END()
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
class WifiConfigSettings {
|
||||
public:
|
||||
bool valid_ = false;
|
||||
bool wifiWarningGiven_ = false;
|
||||
bool rebootRequired_ = false;
|
||||
bool enable_ = false;
|
||||
std::string countryCode_ = "US"; // iso 3661
|
||||
std::string hotspotName_ = "pipedal";
|
||||
bool hasPassword_ = false;
|
||||
std::string password_;
|
||||
std::string channel_ = "g6";
|
||||
public:
|
||||
DECLARE_JSON_MAP(WifiConfigSettings);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
#include "pch.h"
|
||||
#include "catch.hpp"
|
||||
#include <sstream>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
class JsonTestTarget {
|
||||
private:
|
||||
JsonTestTarget(const JsonTestTarget&) {} // hide copy constructor.
|
||||
JsonTestTarget& operator=(const JsonTestTarget&) { return *this;} // hide assignment.
|
||||
public:
|
||||
JsonTestTarget() // make sure reading works without a default cosntructor.
|
||||
{
|
||||
}
|
||||
bool operator==(const JsonTestTarget &other) const
|
||||
{
|
||||
return this->int_ == other.int_
|
||||
&& this->float_ == other.float_
|
||||
&& this->double_ == other.double_
|
||||
&& this->string_ == other.string_
|
||||
&& std::equal(this->ints_.begin(),this->ints_.end(), other.ints_.begin(),other.ints_.end())
|
||||
&& std::equal(this->strings_.begin(),this->strings_.end(), other.strings_.begin(),other.strings_.end())
|
||||
;
|
||||
|
||||
}
|
||||
public:
|
||||
int int_ = 1;
|
||||
float float_ = 3;
|
||||
double double_ = 4;
|
||||
|
||||
public:
|
||||
std::string string_{"5"};
|
||||
|
||||
std::vector<int> ints_ { 1,2,3,4,5};
|
||||
std::vector<std::string> strings_ { "a","b","c","d"};
|
||||
std::vector<std::vector<int> > compound_array_ { { 1,2},{3},{4,5,6},{9}};
|
||||
|
||||
public:
|
||||
static json_map::storage_type<JsonTestTarget> jmap;
|
||||
|
||||
|
||||
public:
|
||||
JsonTestTarget(json_reader& reader)
|
||||
{
|
||||
#ifdef JUNK
|
||||
while (true)
|
||||
{
|
||||
const char*name = reader.read_member_name();
|
||||
if (name == nullptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
json_map::storage_type<JsonTestTarget> JsonTestTarget::jmap {{
|
||||
//json_map::reference("char_", &JsonTestTarget::char_),
|
||||
json_map::reference("int", &JsonTestTarget::int_),
|
||||
json_map::reference("floatt", &JsonTestTarget::float_),
|
||||
json_map::reference("double", &JsonTestTarget::double_),
|
||||
json_map::reference("ints", &JsonTestTarget::ints_),
|
||||
json_map::reference("string", &JsonTestTarget::string_),
|
||||
json_map::reference("strings", &JsonTestTarget::strings_),
|
||||
json_map::reference("compoundarray", &JsonTestTarget::compound_array_),
|
||||
}};
|
||||
|
||||
|
||||
|
||||
TEST_CASE( "json write", "[json_write_test]" ) {
|
||||
std::stringstream os;
|
||||
json_writer writer { os };
|
||||
|
||||
JsonTestTarget testTarget;
|
||||
|
||||
writer.write(testTarget);
|
||||
|
||||
std::cout << os.str();
|
||||
}
|
||||
|
||||
static std::string get_json()
|
||||
{
|
||||
std::stringstream os;
|
||||
json_writer writer { os };
|
||||
|
||||
JsonTestTarget testTarget;
|
||||
|
||||
writer.write(testTarget);
|
||||
return os.str();
|
||||
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE( "json read", "[json_read_test]" ) {
|
||||
|
||||
JsonTestTarget source;
|
||||
source.ints_ = { 1,7,4};
|
||||
source.string_ = "xyz";
|
||||
source.double_ = 99483.1837;
|
||||
|
||||
std::stringstream os;
|
||||
json_writer writer(os);
|
||||
writer.write(source);
|
||||
std::string json = os.str();
|
||||
|
||||
std::stringstream input (json);
|
||||
json_reader reader { input };
|
||||
|
||||
JsonTestTarget dest;
|
||||
reader.read(&dest);
|
||||
REQUIRE(reader.is_complete());
|
||||
|
||||
REQUIRE( source == dest);
|
||||
|
||||
|
||||
JsonTestTarget *pDest;
|
||||
std::stringstream input2(json);
|
||||
json_reader reader2 { input2};
|
||||
reader2.read(&pDest);
|
||||
|
||||
|
||||
//JsonTestTarget *testTarget = reader.read_object<JsonTestTarget>();
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE( "json smart ptrs", "[json_smart_ptrs]" ) {
|
||||
std::string json =get_json();
|
||||
|
||||
|
||||
{
|
||||
std::unique_ptr<JsonTestTarget> uniquePtr;
|
||||
std::stringstream input(json);
|
||||
json_reader reader { input };
|
||||
reader.read(&uniquePtr);
|
||||
}
|
||||
{
|
||||
std::shared_ptr<JsonTestTarget> sharedPtr;
|
||||
std::stringstream input(json);
|
||||
json_reader reader { input };
|
||||
reader.read(&sharedPtr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "pch.h"
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch/catch.hpp>
|
||||
|
||||
uint16_t g_ShutdownPort = 0;
|
||||
Reference in New Issue
Block a user