// Copyright (c) 2022 Robin Davies // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "pch.h" #include "RegDb.hpp" #include #include #include #include #include "PiPedalException.hpp" #include #include #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 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; }