Use actual list of regulatory domains for wifi dialog.

This commit is contained in:
Robin E. R. Davies
2024-11-20 23:40:04 -05:00
parent 0dfe7b8080
commit 0ca7545b3f
12 changed files with 661 additions and 332 deletions
+2 -1
View File
@@ -137,7 +137,8 @@
//"[json_variants]" // subtest of your choice, or none to run all of the tests. //"[json_variants]" // subtest of your choice, or none to run all of the tests.
//"[inverting_mutex_test]" //"[inverting_mutex_test]"
// "[utf8_to_utf32]" // "[utf8_to_utf32]"
"[pipedal_alsa_test]" //"[pipedal_alsa_test]"
"[wifi_channels_test]"
], ],
"stopAtEntry": false, "stopAtEntry": false,
+68 -33
View File
@@ -18,6 +18,7 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "RegDb.hpp" #include "RegDb.hpp"
#include "ss.hpp" #include "ss.hpp"
#include <fstream> #include <fstream>
#include <filesystem> #include <filesystem>
@@ -30,8 +31,12 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <memory> #include <memory>
#include "json_variant.hpp"
#include "json.hpp"
#include <iostream> #include <iostream>
#include <fstream>
#define REGDB_MAGIC 0x52474442 #define REGDB_MAGIC 0x52474442
#define REGDB_VERSION 19 // pre-bookwork #define REGDB_VERSION 19 // pre-bookwork
#define REGDB_VERSION_2 20 // bookworm and later. #define REGDB_VERSION_2 20 // bookworm and later.
@@ -61,14 +66,15 @@ const WifiRegulations& RegDb::getWifiRegulations(const std::string&countryIso366
throw std::runtime_error("Invalid country code."); throw std::runtime_error("Invalid country code.");
} }
template <typename T> template <typename T>
T NlSwap(T value) T NlSwap(T value)
{ {
if constexpr (std::endian::native == std::endian::big) if constexpr (std::endian::native == std::endian::big)
{ {
return value; return value;
} else { }
else
{
auto value_rep = std::bit_cast<std::array<std::uint8_t, sizeof(T)>, T>(value); auto value_rep = std::bit_cast<std::array<std::uint8_t, sizeof(T)>, T>(value);
std::ranges::reverse(value_rep); std::ranges::reverse(value_rep);
return std::bit_cast<T>(value_rep); return std::bit_cast<T>(value_rep);
@@ -76,13 +82,15 @@ T NlSwap(T value)
} }
template <typename T> template <typename T>
class BigEndian { class BigEndian
{
public: public:
BigEndian() : m_value(0) {} BigEndian() : m_value(0) {}
explicit BigEndian(T value) : m_value(NlSwap(value)) {} explicit BigEndian(T value) : m_value(NlSwap(value)) {}
T value() const { return NlSwap(m_value); } T value() const { return NlSwap(m_value); }
bool operator==(const BigEndian<T> &other) const { return m_value == other.m_value; } bool operator==(const BigEndian<T> &other) const { return m_value == other.m_value; }
explicit operator bool() const { return m_value != 0; } explicit operator bool() const { return m_value != 0; }
private: private:
T m_value; T m_value;
}; };
@@ -143,15 +151,13 @@ struct RulesCollection19
} }
Rule *getRule( Rule *getRule(
uint32_t nRule, uint32_t nRule,
char *pData char *pData)
)
{ {
uint32_t ruleOffset = NlSwap(ruleOffsets[nRule]); uint32_t ruleOffset = NlSwap(ruleOffsets[nRule]);
return (Rule *)(pData + ruleOffset); return (Rule *)(pData + ruleOffset);
} }
}; };
struct FrequencyRange struct FrequencyRange
{ {
uint32_t startFrequency; // in khz. uint32_t startFrequency; // in khz.
@@ -255,7 +261,6 @@ void RegDb::Load19()
{ {
WifiRegulations wifiRegulations; WifiRegulations wifiRegulations;
CountryHeader countryHeader = pCountryHeader[i]; CountryHeader countryHeader = pCountryHeader[i];
countryHeader.toNs(); countryHeader.toNs();
wifiRegulations.reg_alpha2 = SS(countryHeader.alpha2[0] << countryHeader.alpha2[1]); wifiRegulations.reg_alpha2 = SS(countryHeader.alpha2[0] << countryHeader.alpha2[1]);
@@ -312,10 +317,8 @@ T ALIGN(T value,int bits)
intptr_t mask = (1 << bits) - 1; intptr_t mask = (1 << bits) - 1;
v = (v + mask) & (~mask); v = (v + mask) & (~mask);
return (T)v; return (T)v;
} }
inline BigEndian<uint32_t> cpu_to_be32(uint32_t value) inline BigEndian<uint32_t> cpu_to_be32(uint32_t value)
{ {
return BigEndian<uint32_t>(value); return BigEndian<uint32_t>(value);
@@ -326,7 +329,6 @@ inline BigEndian<uint16_t> cpu_to_be16(uint16_t value)
return BigEndian<uint16_t>(value); return BigEndian<uint16_t>(value);
} }
inline uint16_t be16_to_cpu(const BigEndian<uint16_t> &value) inline uint16_t be16_to_cpu(const BigEndian<uint16_t> &value)
{ {
return value.value(); return value.value();
@@ -344,13 +346,15 @@ bool regdb_has_valid_signature(const u8 *data, unsigned int size) { return true;
///////////////////////// /////////////////////////
// shamelessly lifted from linux net/wireless/reg.c // shamelessly lifted from linux net/wireless/reg.c
struct fwdb_country { struct fwdb_country
{
u8 alpha2[2]; u8 alpha2[2];
__be16 coll_ptr; __be16 coll_ptr;
/* this struct cannot be extended */ /* this struct cannot be extended */
} __packed __aligned(4); } __packed __aligned(4);
struct fwdb_collection { struct fwdb_collection
{
u8 len; u8 len;
u8 n_rules; u8 n_rules;
u8 dfs_region; u8 dfs_region;
@@ -358,14 +362,15 @@ struct fwdb_collection {
/* aligned to 2, then followed by __be16 array of rule pointers */ /* aligned to 2, then followed by __be16 array of rule pointers */
} __packed __aligned(4); } __packed __aligned(4);
struct fwdb_header { struct fwdb_header
{
__be32 magic; __be32 magic;
__be32 version; __be32 version;
struct fwdb_country country[]; struct fwdb_country country[];
} __packed __aligned(4); } __packed __aligned(4);
enum fwdb_flags
enum fwdb_flags { {
FWDB_FLAG_NO_OFDM = BIT(0), FWDB_FLAG_NO_OFDM = BIT(0),
FWDB_FLAG_NO_OUTDOOR = BIT(1), FWDB_FLAG_NO_OUTDOOR = BIT(1),
FWDB_FLAG_DFS = BIT(2), FWDB_FLAG_DFS = BIT(2),
@@ -373,18 +378,21 @@ enum fwdb_flags {
FWDB_FLAG_AUTO_BW = BIT(4), FWDB_FLAG_AUTO_BW = BIT(4),
}; };
struct fwdb_wmm_ac { struct fwdb_wmm_ac
{
u8 ecw; u8 ecw;
u8 aifsn; u8 aifsn;
__be16 cot; __be16 cot;
} __packed; } __packed;
struct fwdb_wmm_rule { struct fwdb_wmm_rule
{
struct fwdb_wmm_ac client[IEEE80211_NUM_ACS]; struct fwdb_wmm_ac client[IEEE80211_NUM_ACS];
struct fwdb_wmm_ac ap[IEEE80211_NUM_ACS]; struct fwdb_wmm_ac ap[IEEE80211_NUM_ACS];
} __packed; } __packed;
struct fwdb_rule { struct fwdb_rule
{
u8 len; u8 len;
u8 flags; u8 flags;
__be16 max_eirp; __be16 max_eirp;
@@ -397,7 +405,6 @@ struct fwdb_rule {
#define FWDB_MAGIC 0x52474442 #define FWDB_MAGIC 0x52474442
#define FWDB_VERSION 20 #define FWDB_VERSION 20
static int ecw2cw(int ecw) static int ecw2cw(int ecw)
{ {
return (1 << ecw) - 1; return (1 << ecw) - 1;
@@ -408,7 +415,8 @@ static bool valid_wmm(struct fwdb_wmm_rule *rule)
struct fwdb_wmm_ac *ac = (struct fwdb_wmm_ac *)rule; struct fwdb_wmm_ac *ac = (struct fwdb_wmm_ac *)rule;
int i; int i;
for (i = 0; i < IEEE80211_NUM_ACS * 2; i++) { for (i = 0; i < IEEE80211_NUM_ACS * 2; i++)
{
u16 cw_min = ecw2cw((ac[i].ecw & 0xf0) >> 4); u16 cw_min = ecw2cw((ac[i].ecw & 0xf0) >> 4);
u16 cw_max = ecw2cw(ac[i].ecw & 0x0f); u16 cw_max = ecw2cw(ac[i].ecw & 0x0f);
u8 aifsn = ac[i].aifsn; u8 aifsn = ac[i].aifsn;
@@ -433,7 +441,8 @@ static bool valid_rule(const u8 *data, unsigned int size, u16 rule_ptr)
/* mandatory fields */ /* mandatory fields */
if (rule->len < offsetofend(struct fwdb_rule, max_bw)) if (rule->len < offsetofend(struct fwdb_rule, max_bw))
return false; return false;
if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr)) { if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr))
{
u32 wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2; u32 wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2;
struct fwdb_wmm_rule *wmm; struct fwdb_wmm_rule *wmm;
@@ -448,7 +457,6 @@ static bool valid_rule(const u8 *data, unsigned int size, u16 rule_ptr)
return true; return true;
} }
static bool valid_country(const u8 *data, unsigned int size, static bool valid_country(const u8 *data, unsigned int size,
const struct fwdb_country *country) const struct fwdb_country *country)
{ {
@@ -463,7 +471,8 @@ static bool valid_country(const u8 *data, unsigned int size,
/* make sure base struct and all rules fit */ /* make sure base struct and all rules fit */
if ((u8 *)coll + ALIGN(coll->len, 2) + if ((u8 *)coll + ALIGN(coll->len, 2) +
(coll->n_rules * 2) > data + size) (coll->n_rules * 2) >
data + size)
return false; return false;
/* mandatory fields must exist */ /* mandatory fields must exist */
@@ -472,7 +481,8 @@ static bool valid_country(const u8 *data, unsigned int size,
rules_ptr = (__be16 *)((u8 *)coll + ALIGN(coll->len, 2)); rules_ptr = (__be16 *)((u8 *)coll + ALIGN(coll->len, 2));
for (i = 0; i < coll->n_rules; i++) { for (i = 0; i < coll->n_rules; i++)
{
u16 rule_ptr = be16_to_cpu(rules_ptr[i]); u16 rule_ptr = be16_to_cpu(rules_ptr[i]);
if (!valid_rule(data, size, rule_ptr)) if (!valid_rule(data, size, rule_ptr))
@@ -482,7 +492,6 @@ static bool valid_country(const u8 *data, unsigned int size,
return true; return true;
} }
static bool valid_regdb(const u8 *data, unsigned int size) static bool valid_regdb(const u8 *data, unsigned int size)
{ {
const struct fwdb_header *hdr = (fwdb_header *)data; const struct fwdb_header *hdr = (fwdb_header *)data;
@@ -501,7 +510,8 @@ static bool valid_regdb(const u8 *data, unsigned int size)
return false; return false;
country = &hdr->country[0]; country = &hdr->country[0];
while ((u8 *)(country + 1) <= data + size) { while ((u8 *)(country + 1) <= data + size)
{
if (!country->coll_ptr) if (!country->coll_ptr)
break; break;
if (!valid_country(data, size, country)) if (!valid_country(data, size, country))
@@ -553,7 +563,6 @@ static void set_wmm_rule(const struct fwdb_header *db,
// rrule->has_wmm = true; // rrule->has_wmm = true;
// } // }
static WifiRegulations regdb_load_country(const struct fwdb_header *db, static WifiRegulations regdb_load_country(const struct fwdb_header *db,
const struct fwdb_country *country) const struct fwdb_country *country)
{ {
@@ -567,7 +576,8 @@ static WifiRegulations regdb_load_country(const struct fwdb_header *db,
wifiRegulations.dfs_region = (DfsRegion)(coll->dfs_region); wifiRegulations.dfs_region = (DfsRegion)(coll->dfs_region);
unsigned int nRules = coll->n_rules; unsigned int nRules = coll->n_rules;
for (i = 0; i < nRules; i++) { for (i = 0; i < nRules; i++)
{
const __be16 *rules_ptr = (const __be16 *)((u8 *)coll + ALIGN(coll->len, 2)); const __be16 *rules_ptr = (const __be16 *)((u8 *)coll + ALIGN(coll->len, 2));
unsigned int rule_ptr = be16_to_cpu(rules_ptr[i]) << 2; unsigned int rule_ptr = be16_to_cpu(rules_ptr[i]) << 2;
struct fwdb_rule *rule = (fwdb_rule *)((u8 *)db + rule_ptr); struct fwdb_rule *rule = (fwdb_rule *)((u8 *)db + rule_ptr);
@@ -605,7 +615,6 @@ static WifiRegulations regdb_load_country(const struct fwdb_header *db,
return wifiRegulations; return wifiRegulations;
} }
static std::vector<WifiRegulations> load_regdb(u8 *pData) static std::vector<WifiRegulations> load_regdb(u8 *pData)
{ {
const struct fwdb_header *hdr = (const fwdb_header *)pData; const struct fwdb_header *hdr = (const fwdb_header *)pData;
@@ -619,15 +628,14 @@ static std::vector<WifiRegulations> load_regdb(u8*pData)
std::vector<WifiRegulations> result; std::vector<WifiRegulations> result;
country = &hdr->country[0]; country = &hdr->country[0];
while (country->coll_ptr) { while (country->coll_ptr)
{
result.push_back(regdb_load_country(hdr, country)); result.push_back(regdb_load_country(hdr, country));
country++; country++;
} }
return result; return result;
} }
void RegDb::Load20() void RegDb::Load20()
{ {
u8 *data = (u8 *)ptr(); u8 *data = (u8 *)ptr();
@@ -635,8 +643,35 @@ void RegDb::Load20()
if (!valid_regdb(data, size)) if (!valid_regdb(data, size))
{ {
throw std::runtime_error("Invalid file format."); throw std::runtime_error("Invalid file format.");
} }
this->regulations = load_regdb(data); this->regulations = load_regdb(data);
this->isValid = true; this->isValid = true;
} }
static std::map<std::string,std::string> getIsoNamesDict(const std::filesystem::path&filename)
{
std::map<std::string,std::string> result;
std::ifstream f(filename);
if (!f.is_open())
{
throw std::runtime_error("Can't open WiFi regulatory domain names file.");
}
json_reader reader(f);
reader.read(&result);
return result;
}
std::map<std::string,std::string> RegDb::getRegulatoryDomains(const std::filesystem::path&isoFilenamesfile) const
{
std::map<std::string,std::string> keyToNameDict = getIsoNamesDict(isoFilenamesfile);
std::map<std::string,std::string> result;
for (const auto &regulation : this->regulations)
{
if (keyToNameDict.contains(regulation.reg_alpha2))
{
result[regulation.reg_alpha2] = keyToNameDict[regulation.reg_alpha2];
}
}
return result;
}
+1
View File
@@ -61,6 +61,7 @@ namespace pipedal
const WifiRegulations&getWifiRegulations(const std::string&countryIso3661) const; const WifiRegulations&getWifiRegulations(const std::string&countryIso3661) const;
std::map<std::string,std::string> getRegulatoryDomains(const std::filesystem::path&namesFile="/etc/pipedal/config/iso_codes.json") const;
private: private:
bool isValid = false; bool isValid = false;
+248
View File
@@ -0,0 +1,248 @@
{
"AF": "Afghanistan",
"AX": "Aland Islands",
"AL": "Albania",
"DZ": "Algeria",
"AS": "American Samoa",
"AD": "Andorra",
"AO": "Angola",
"AI": "Anguilla",
"AQ": "Antarctica",
"AG": "Antigua And Barbuda",
"AR": "Argentina",
"AM": "Armenia",
"AW": "Aruba",
"AU": "Australia",
"AT": "Austria",
"AZ": "Azerbaijan",
"BS": "Bahamas",
"BH": "Bahrain",
"BD": "Bangladesh",
"BB": "Barbados",
"BY": "Belarus",
"BE": "Belgium",
"BZ": "Belize",
"BJ": "Benin",
"BM": "Bermuda",
"BT": "Bhutan",
"BO": "Bolivia",
"BA": "Bosnia And Herzegovina",
"BW": "Botswana",
"BV": "Bouvet Island",
"BR": "Brazil",
"IO": "British Indian Ocean Territory",
"BN": "Brunei Darussalam",
"BG": "Bulgaria",
"BF": "Burkina Faso",
"BI": "Burundi",
"KH": "Cambodia",
"CM": "Cameroon",
"CA": "Canada",
"CV": "Cape Verde",
"KY": "Cayman Islands",
"CF": "Central African Republic",
"TD": "Chad",
"CL": "Chile",
"CN": "China",
"CX": "Christmas Island",
"CC": "Cocos (Keeling) Islands",
"CO": "Colombia",
"KM": "Comoros",
"CG": "Congo",
"CD": "Congo, Democratic Republic",
"CK": "Cook Islands",
"CR": "Costa Rica",
"CI": "Cote D\"Ivoire",
"HR": "Croatia",
"CU": "Cuba",
"CY": "Cyprus",
"CZ": "Czech Republic",
"DK": "Denmark",
"DJ": "Djibouti",
"DM": "Dominica",
"DO": "Dominican Republic",
"EC": "Ecuador",
"EG": "Egypt",
"SV": "El Salvador",
"GQ": "Equatorial Guinea",
"ER": "Eritrea",
"EE": "Estonia",
"ET": "Ethiopia",
"FK": "Falkland Islands (Malvinas)",
"FO": "Faroe Islands",
"FJ": "Fiji",
"FI": "Finland",
"FR": "France",
"GF": "French Guiana",
"PF": "French Polynesia",
"TF": "French Southern Territories",
"GA": "Gabon",
"GM": "Gambia",
"GE": "Georgia",
"DE": "Germany",
"GH": "Ghana",
"GI": "Gibraltar",
"GR": "Greece",
"GL": "Greenland",
"GD": "Grenada",
"GP": "Guadeloupe",
"GU": "Guam",
"GT": "Guatemala",
"GG": "Guernsey",
"GN": "Guinea",
"GW": "Guinea-Bissau",
"GY": "Guyana",
"HT": "Haiti",
"HM": "Heard Island & Mcdonald Islands",
"VA": "Holy See (Vatican City State)",
"HN": "Honduras",
"HK": "Hong Kong",
"HU": "Hungary",
"IS": "Iceland",
"IN": "India",
"ID": "Indonesia",
"IR": "Iran, Islamic Republic Of",
"IQ": "Iraq",
"IE": "Ireland",
"IM": "Isle Of Man",
"IL": "Israel",
"IT": "Italy",
"JM": "Jamaica",
"JP": "Japan",
"JE": "Jersey",
"JO": "Jordan",
"KZ": "Kazakhstan",
"KE": "Kenya",
"KI": "Kiribati",
"KR": "Korea",
"KP": "North Korea",
"KW": "Kuwait",
"KG": "Kyrgyzstan",
"LA": "Lao People\"s Democratic Republic",
"LV": "Latvia",
"LB": "Lebanon",
"LS": "Lesotho",
"LR": "Liberia",
"LY": "Libyan Arab Jamahiriya",
"LI": "Liechtenstein",
"LT": "Lithuania",
"LU": "Luxembourg",
"MO": "Macao",
"MK": "Macedonia",
"MG": "Madagascar",
"MW": "Malawi",
"MY": "Malaysia",
"MV": "Maldives",
"ML": "Mali",
"MT": "Malta",
"MH": "Marshall Islands",
"MQ": "Martinique",
"MR": "Mauritania",
"MU": "Mauritius",
"YT": "Mayotte",
"MX": "Mexico",
"FM": "Micronesia, Federated States Of",
"MD": "Moldova",
"MC": "Monaco",
"MN": "Mongolia",
"ME": "Montenegro",
"MS": "Montserrat",
"MA": "Morocco",
"MZ": "Mozambique",
"MM": "Myanmar",
"NA": "Namibia",
"NR": "Nauru",
"NP": "Nepal",
"NL": "Netherlands",
"AN": "Netherlands Antilles",
"NC": "New Caledonia",
"NZ": "New Zealand",
"NI": "Nicaragua",
"NE": "Niger",
"NG": "Nigeria",
"NU": "Niue",
"NF": "Norfolk Island",
"MP": "Northern Mariana Islands",
"NO": "Norway",
"OM": "Oman",
"PK": "Pakistan",
"PW": "Palau",
"PS": "Palestinian Territory, Occupied",
"PA": "Panama",
"PG": "Papua New Guinea",
"PY": "Paraguay",
"PE": "Peru",
"PH": "Philippines",
"PN": "Pitcairn",
"PL": "Poland",
"PT": "Portugal",
"PR": "Puerto Rico",
"QA": "Qatar",
"RE": "Reunion",
"RO": "Romania",
"RU": "Russian Federation",
"RW": "Rwanda",
"BL": "Saint Barthelemy",
"SH": "Saint Helena",
"KN": "Saint Kitts And Nevis",
"LC": "Saint Lucia",
"MF": "Saint Martin",
"PM": "Saint Pierre And Miquelon",
"VC": "Saint Vincent And Grenadines",
"WS": "Samoa",
"SM": "San Marino",
"ST": "Sao Tome And Principe",
"SA": "Saudi Arabia",
"SN": "Senegal",
"RS": "Serbia",
"SC": "Seychelles",
"SL": "Sierra Leone",
"SG": "Singapore",
"SK": "Slovakia",
"SI": "Slovenia",
"SB": "Solomon Islands",
"SO": "Somalia",
"ZA": "South Africa",
"GS": "South Georgia And Sandwich Isl.",
"ES": "Spain",
"LK": "Sri Lanka",
"SD": "Sudan",
"SR": "Suriname",
"SJ": "Svalbard And Jan Mayen",
"SZ": "Swaziland",
"SE": "Sweden",
"CH": "Switzerland",
"SY": "Syrian Arab Republic",
"TW": "Taiwan",
"TJ": "Tajikistan",
"TZ": "Tanzania",
"TH": "Thailand",
"TL": "Timor-Leste",
"TG": "Togo",
"TK": "Tokelau",
"TO": "Tonga",
"TT": "Trinidad And Tobago",
"TN": "Tunisia",
"TR": "Turkey",
"TM": "Turkmenistan",
"TC": "Turks And Caicos Islands",
"TV": "Tuvalu",
"UG": "Uganda",
"UA": "Ukraine",
"AE": "United Arab Emirates",
"GB": "United Kingdom",
"US": "United States",
"UM": "United States Outlying Islands",
"UY": "Uruguay",
"UZ": "Uzbekistan",
"VU": "Vanuatu",
"VE": "Venezuela",
"VN": "Vietnam",
"VG": "Virgin Islands, British",
"VI": "Virgin Islands, U.S.",
"WF": "Wallis And Futuna",
"EH": "Western Sahara",
"YE": "Yemen",
"ZM": "Zambia",
"ZW": "Zimbabwe"
}
+1 -4
View File
@@ -1018,10 +1018,7 @@ export class PiPedalModel //implements PiPedalModel
} }
try { try {
let isoFetch = await fetch(new Request('iso_codes.json')); this.countryCodes = await this.getWebSocket().request<{ [Name: string]: string }>("getWifiRegulatoryDomains");
this.countryCodes = (await isoFetch.json()) as { [Name: string]: string };
this.clientId = (await this.getWebSocket().request<number>("hello")) as number; this.clientId = (await this.getWebSocket().request<number>("hello")) as number;
-6
View File
@@ -44,16 +44,10 @@ static void DiscoveryTest()
} }
void ChannelConfigtest()
{
cout << "--- Channel Config Test" << endl;
}
TEST_CASE( "ALSA Test", "[pipedal_alsa_test][Build][Dev]" ) { TEST_CASE( "ALSA Test", "[pipedal_alsa_test][Build][Dev]" ) {
DiscoveryTest(); DiscoveryTest();
ChannelConfigTest();
} }
+14
View File
@@ -31,6 +31,7 @@
#include "AdminClient.hpp" #include "AdminClient.hpp"
#include "SplitEffect.hpp" #include "SplitEffect.hpp"
#include "CpuGovernor.hpp" #include "CpuGovernor.hpp"
#include "RegDb.hpp"
#include "RingBufferReader.hpp" #include "RingBufferReader.hpp"
#include "PiPedalUI.hpp" #include "PiPedalUI.hpp"
#include "atom_object.hpp" #include "atom_object.hpp"
@@ -2722,3 +2723,16 @@ bool PiPedalModel::GetHasWifi()
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
return hasWifi; return hasWifi;
} }
std::map<std::string,std::string> PiPedalModel::GetWifiRegulatoryDomains()
{
std::map<std::string,std::string> result;
try {
auto& regDb = RegDb::GetInstance();
result = regDb.getRegulatoryDomains(storage.GetConfigRoot() / "iso_codes.json");
} catch (const std::exception&e)
{
Lv2Log::warning(SS("Unable to query Wifi Regulatory domains. " << e.what()));
}
return result;
}
+2
View File
@@ -91,6 +91,7 @@ namespace pipedal
// virtual void OnPatchPropertyChanged(int64_t clientId, int64_t instanceId,const std::string& propertyUri,const json_variant& value) = 0; // virtual void OnPatchPropertyChanged(int64_t clientId, int64_t instanceId,const std::string& propertyUri,const json_variant& value) = 0;
virtual void OnErrorMessage(const std::string &message) = 0; virtual void OnErrorMessage(const std::string &message) = 0;
virtual void OnLv2PluginsChanging() = 0; virtual void OnLv2PluginsChanging() = 0;
virtual void OnNetworkChanging(bool hotspotConnected) = 0; virtual void OnNetworkChanging(bool hotspotConnected) = 0;
virtual void OnHasWifiChanged(bool hasWifi) = 0; virtual void OnHasWifiChanged(bool hasWifi) = 0;
virtual void Close() = 0; virtual void Close() = 0;
@@ -304,6 +305,7 @@ namespace pipedal
void SetRestartListener(std::function<void(void)> &&listener); void SetRestartListener(std::function<void(void)> &&listener);
void OnLv2PluginsChanged(); void OnLv2PluginsChanged();
void SetOnboarding(bool value); void SetOnboarding(bool value);
std::map<std::string,std::string> GetWifiRegulatoryDomains();
void UpdateDnsSd(); void UpdateDnsSd();
+5
View File
@@ -1640,6 +1640,11 @@ public:
pReader->read(&value); pReader->read(&value);
this->model.SetOnboarding(value); this->model.SetOnboarding(value);
} }
else if (message == "getWifiRegulatoryDomains")
{
auto regulatoryDomains = this->model.GetWifiRegulatoryDomains();
this->Reply(replyTo, "getWifiRegulatoryDomains", regulatoryDomains);
}
else else
{ {
Lv2Log::error("Unknown message received: %s", message.c_str()); Lv2Log::error("Unknown message received: %s", message.c_str());
+65 -51
View File
@@ -43,7 +43,6 @@ const char *BANKS_FILENAME = "index.banks";
#define USER_SETTINGS_FILENAME "userSettings.json"; #define USER_SETTINGS_FILENAME "userSettings.json";
static bool isSubdirectory(const fs::path &path, const fs::path &basePath) static bool isSubdirectory(const fs::path &path, const fs::path &basePath)
{ {
auto iPath = path.begin(); auto iPath = path.begin();
@@ -74,7 +73,6 @@ static bool isSubdirectory(const fs::path&path, const fs::path&basePath)
static bool hasSyntheticModRoot(const UiFileProperty &fileProperty) static bool hasSyntheticModRoot(const UiFileProperty &fileProperty)
{ {
return (fileProperty.modDirectories().size() > 1 || (fileProperty.modDirectories().size() == 1 && fileProperty.useLegacyModDirectory())); return (fileProperty.modDirectories().size() > 1 || (fileProperty.modDirectories().size() == 1 && fileProperty.useLegacyModDirectory()));
} }
Storage::Storage() Storage::Storage()
@@ -187,7 +185,8 @@ std::filesystem::path ResolveHomePath(const std::filesystem::path &path)
{ {
homeDirectory = getenv("USERPROFILE"); homeDirectory = getenv("USERPROFILE");
} }
if (homeDirectory == nullptr) { if (homeDirectory == nullptr)
{
return path; return path;
} }
std::filesystem::path result = homeDirectory; std::filesystem::path result = homeDirectory;
@@ -213,6 +212,15 @@ void Storage::SetDataRoot(const std::filesystem::path &path)
this->dataRoot = ResolveHomePath(path); this->dataRoot = ResolveHomePath(path);
} }
const std::filesystem::path &Storage::GetConfigRoot()
{
return this->configRoot;
}
const std::filesystem::path &Storage::GetDataRoot()
{
return this->dataRoot;
}
static void CopyDirectory(const std::filesystem::path &source, const std::filesystem::path &destination) static void CopyDirectory(const std::filesystem::path &source, const std::filesystem::path &destination)
{ {
for (auto &directoryEntry : std::filesystem::directory_iterator(source)) for (auto &directoryEntry : std::filesystem::directory_iterator(source))
@@ -1001,7 +1009,9 @@ bool Storage::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings
copyToSave.hasPassword_ = previousValue.hasPassword_; copyToSave.hasPassword_ = previousValue.hasPassword_;
copyToSave.password_ = previousValue.password_; copyToSave.password_ = previousValue.password_;
copyToSave.hasSavedPassword_ = previousValue.hasPassword_; copyToSave.hasSavedPassword_ = previousValue.hasPassword_;
} else { }
else
{
if (copyToSave.IsEnabled()) if (copyToSave.IsEnabled())
{ {
copyToSave.hasSavedPassword_ = copyToSave.hasPassword_; copyToSave.hasSavedPassword_ = copyToSave.hasPassword_;
@@ -1160,14 +1170,16 @@ void Storage::MergePluginPresets(const std::string &pluginUri, const PluginPrese
else else
{ {
path = GetPluginPresetPath(pluginUri); path = GetPluginPresetPath(pluginUri);
try { try
{
std::ifstream f(path); std::ifstream f(path);
if (f.is_open()) if (f.is_open())
{ {
json_reader reader(f); json_reader reader(f);
reader.read(&existingPresets); reader.read(&existingPresets);
} }
} catch (const std::exception &e) }
catch (const std::exception &e)
{ {
Lv2Log::error(SS("Storage::MergePluginPresets: Can't reading existing plugin presets." << e.what())); Lv2Log::error(SS("Storage::MergePluginPresets: Can't reading existing plugin presets." << e.what()));
} }
@@ -1534,7 +1546,8 @@ std::vector<MidiBinding> Storage::GetSystemMidiBindings()
std::vector<MidiBinding> result; std::vector<MidiBinding> result;
std::filesystem::path fileName = this->dataRoot / "config" / "SystemMidiBindings.json"; std::filesystem::path fileName = this->dataRoot / "config" / "SystemMidiBindings.json";
if (!std::filesystem::exists(fileName)) { if (!std::filesystem::exists(fileName))
{
// pick up from legacy location? // pick up from legacy location?
fileName = this->configRoot / "config" / "SystemMidiBindings.json"; fileName = this->configRoot / "config" / "SystemMidiBindings.json";
} }
@@ -1542,7 +1555,8 @@ std::vector<MidiBinding> Storage::GetSystemMidiBindings()
f.open(fileName); f.open(fileName);
if (f.is_open()) if (f.is_open())
{ {
try { try
{
json_reader reader(f); json_reader reader(f);
reader.read(&result); reader.read(&result);
if (result.size() == 2) if (result.size() == 2)
@@ -1604,13 +1618,11 @@ static bool containsDirectorySeparator(const std::string &value)
return false; return false;
} }
static void ThrowPermissionDeniedError() static void ThrowPermissionDeniedError()
{ {
throw std::logic_error("Permission denied."); throw std::logic_error("Permission denied.");
} }
std::vector<std::string> Storage::GetFileList(const UiFileProperty &fileProperty) std::vector<std::string> Storage::GetFileList(const UiFileProperty &fileProperty)
{ {
if (!UiFileProperty::IsDirectoryNameValid(fileProperty.directory())) if (!UiFileProperty::IsDirectoryNameValid(fileProperty.directory()))
@@ -1652,9 +1664,8 @@ std::vector<std::string> Storage::GetFileList(const UiFileProperty &fileProperty
// sort lexicographically // sort lexicographically
auto collator = Locale::GetInstance()->GetCollator(); auto collator = Locale::GetInstance()->GetCollator();
std::sort(result.begin(), result.end(), [&collator](const std::string&left,const std::string&right) { std::sort(result.begin(), result.end(), [&collator](const std::string &left, const std::string &right)
return collator->Compare(left,right) < 0; { return collator->Compare(left, right) < 0; });
});
return result; return result;
} }
@@ -1679,7 +1690,6 @@ static bool ensureNoDotDot(const std::filesystem::path&path)
{ {
return false; return false;
} }
} }
} }
return true; return true;
@@ -1708,11 +1718,12 @@ static void AddFilesToResult(
if (fileProperty.IsValidExtension(path.extension().string())) if (fileProperty.IsValidExtension(path.extension().string()))
{ {
resultFiles.push_back( resultFiles.push_back(
FileEntry(path,name,false,false) FileEntry(path, name, false, false));
);
} }
} }
} else if (dir_entry.is_directory()) { }
else if (dir_entry.is_directory())
{
resultFiles.push_back(FileEntry{path, name, true, fs::is_symlink(path)}); resultFiles.push_back(FileEntry{path, name, true, fs::is_symlink(path)});
} }
} }
@@ -1726,13 +1737,13 @@ static void AddFilesToResult(
auto collator = Locale::GetInstance()->GetCollator(); auto collator = Locale::GetInstance()->GetCollator();
std::sort(resultFiles.begin(), resultFiles.end(),[&collator](const FileEntry&l, const FileEntry&r) { std::sort(resultFiles.begin(), resultFiles.end(), [&collator](const FileEntry &l, const FileEntry &r)
{
if (l.isDirectory_ != r.isDirectory_) if (l.isDirectory_ != r.isDirectory_)
{ {
return l.isDirectory_ > r.isDirectory_; return l.isDirectory_ > r.isDirectory_;
} }
return collator->Compare(l.displayName_,r.displayName_) < 0; return collator->Compare(l.displayName_,r.displayName_) < 0; });
});
} }
FileRequestResult Storage::GetModFileList2(const std::string &relativePath, const UiFileProperty &fileProperty) FileRequestResult Storage::GetModFileList2(const std::string &relativePath, const UiFileProperty &fileProperty)
{ {
@@ -1750,16 +1761,13 @@ FileRequestResult Storage::GetModFileList2(const std::string &relativePath,const
if (directoryInfo) if (directoryInfo)
{ {
result.files_.push_back( result.files_.push_back(
FileEntry(uploadsDirectory / directoryInfo->pipedalPath,directoryInfo->displayName,true,true) FileEntry(uploadsDirectory / directoryInfo->pipedalPath, directoryInfo->displayName, true, true));
);
} }
} }
if (fileProperty.useLegacyModDirectory()) if (fileProperty.useLegacyModDirectory())
{ {
result.files_.push_back( result.files_.push_back(
FileEntry(uploadsDirectory / fileProperty.directory(),fs::path(fileProperty.directory()).filename().string(),true,true) FileEntry(uploadsDirectory / fileProperty.directory(), fs::path(fileProperty.directory()).filename().string(), true, true));
);
} }
result.breadcrumbs_.push_back({"", "Home"}); result.breadcrumbs_.push_back({"", "Home"});
return result; return result;
@@ -1780,7 +1788,6 @@ FileRequestResult Storage::GetModFileList2(const std::string &relativePath,const
result.breadcrumbs_.push_back({modDirectoryPath.string(), modDirectoryInfo->displayName}); result.breadcrumbs_.push_back({modDirectoryPath.string(), modDirectoryInfo->displayName});
break; break;
} }
} }
} }
if (modDirectoryPath.empty() && fileProperty.useLegacyModDirectory()) if (modDirectoryPath.empty() && fileProperty.useLegacyModDirectory())
@@ -1789,7 +1796,9 @@ FileRequestResult Storage::GetModFileList2(const std::string &relativePath,const
{ {
modDirectoryPath = uploadsDirectory / fileProperty.directory(); modDirectoryPath = uploadsDirectory / fileProperty.directory();
result.breadcrumbs_.push_back({modDirectoryPath.string(), fs::path(fileProperty.directory()).filename().string()}); result.breadcrumbs_.push_back({modDirectoryPath.string(), fs::path(fileProperty.directory()).filename().string()});
} else { }
else
{
ThrowPermissionDeniedError(); ThrowPermissionDeniedError();
} }
} }
@@ -1802,7 +1811,8 @@ FileRequestResult Storage::GetModFileList2(const std::string &relativePath,const
// skip past one or more segments in the modDiretoryPath. // skip past one or more segments in the modDiretoryPath.
for (auto iModPath = modPath.begin(); iModPath != modPath.end(); ++iModPath) for (auto iModPath = modPath.begin(); iModPath != modPath.end(); ++iModPath)
{ {
if (iRp != rp.end()) { if (iRp != rp.end())
{
++iRp; ++iRp;
} }
} }
@@ -1817,7 +1827,6 @@ FileRequestResult Storage::GetModFileList2(const std::string &relativePath,const
return result; return result;
} }
FileRequestResult Storage::GetFileList2(const std::string &relativePath_, const UiFileProperty &fileProperty) FileRequestResult Storage::GetFileList2(const std::string &relativePath_, const UiFileProperty &fileProperty)
{ {
std::string absolutePath = relativePath_; std::string absolutePath = relativePath_;
@@ -1842,7 +1851,6 @@ FileRequestResult Storage::GetFileList2(const std::string &relativePath_,const U
absolutePath = pluginRootDirectory.string(); absolutePath = pluginRootDirectory.string();
} }
{ {
result.breadcrumbs_.push_back({"", "Home"}); result.breadcrumbs_.push_back({"", "Home"});
fs::path fsAbsolutePath{absolutePath}; fs::path fsAbsolutePath{absolutePath};
@@ -1872,7 +1880,6 @@ FileRequestResult Storage::GetFileList2(const std::string &relativePath_,const U
return result; return result;
} }
bool Storage::IsValidSampleFileName(const std::filesystem::path &fileName) bool Storage::IsValidSampleFileName(const std::filesystem::path &fileName)
{ {
if (!fileName.is_absolute()) if (!fileName.is_absolute())
@@ -1888,10 +1895,12 @@ bool Storage::IsValidSampleFileName(const std::filesystem::path &fileName)
for (auto i = audioFilePath.begin(); i != audioFilePath.end(); ++i) for (auto i = audioFilePath.begin(); i != audioFilePath.end(); ++i)
{ {
if (iTarget == parentDirectory.end()) { if (iTarget == parentDirectory.end())
{
return false; return false;
} }
if (*i != *iTarget) { if (*i != *iTarget)
{
return false; return false;
} }
++iTarget; ++iTarget;
@@ -1927,14 +1936,17 @@ void Storage::DeleteSampleFile(const std::filesystem::path &fileName)
if (std::filesystem::is_symlink(fileName)) if (std::filesystem::is_symlink(fileName))
{ {
std::filesystem::remove(fileName); std::filesystem::remove(fileName);
} else { }
else
{
if (fileName.string().length() > 1) // guard against rm -rf / (bitter experience) if (fileName.string().length() > 1) // guard against rm -rf / (bitter experience)
{ {
std::filesystem::remove_all(fileName); std::filesystem::remove_all(fileName);
} }
} }
} }
else { else
{
std::filesystem::remove(fileName); std::filesystem::remove(fileName);
} }
} }
@@ -1975,7 +1987,8 @@ std::string Storage::UploadUserFile(const std::string &directory, const std::str
throw std::logic_error("patchProperty directory not implemented."); throw std::logic_error("patchProperty directory not implemented.");
} }
{ {
try { try
{
std::filesystem::create_directories(path.parent_path()); std::filesystem::create_directories(path.parent_path());
pipedal::ofstream_synced f(path, std::ios_base::trunc | std::ios_base::binary); pipedal::ofstream_synced f(path, std::ios_base::trunc | std::ios_base::binary);
@@ -1991,16 +2004,19 @@ std::string Storage::UploadUserFile(const std::string &directory, const std::str
{ {
size_t thisTime = std::min(BUFFER_SIZE, contentLength); size_t thisTime = std::min(BUFFER_SIZE, contentLength);
stream.read(pBuffer, (std::streamsize)thisTime); stream.read(pBuffer, (std::streamsize)thisTime);
if (!stream) { if (!stream)
{
throw std::runtime_error("Unable to read body input stream."); throw std::runtime_error("Unable to read body input stream.");
} }
f.write(pBuffer, (std::streamsize)thisTime); f.write(pBuffer, (std::streamsize)thisTime);
if (!f) { if (!f)
{
throw std::runtime_error("Failed to write to upload file."); throw std::runtime_error("Failed to write to upload file.");
} }
contentLength -= thisTime; contentLength -= thisTime;
} }
} catch (const std::exception &e) }
catch (const std::exception &e)
{ {
Lv2Log::error(SS("Upload failed. " << e.what())); Lv2Log::error(SS("Upload failed. " << e.what()));
std::filesystem::remove(path); std::filesystem::remove(path);
@@ -2027,7 +2043,6 @@ std::string Storage::CreateNewSampleDirectory(const std::string&relativePath, co
} }
std::filesystem::create_directories(path); std::filesystem::create_directories(path);
return path; return path;
} }
std::string Storage::RenameFilePropertyFile( std::string Storage::RenameFilePropertyFile(
const std::string &oldRelativePath, const std::string &oldRelativePath,
@@ -2058,14 +2073,15 @@ std::string Storage::RenameFilePropertyFile(
if (std::filesystem::is_directory(newPath)) if (std::filesystem::is_directory(newPath))
{ {
throw std::runtime_error("A directory with that name already exists."); throw std::runtime_error("A directory with that name already exists.");
} else { }
else
{
throw std::runtime_error("A file with that name already exists."); throw std::runtime_error("A file with that name already exists.");
} }
} }
std::filesystem::rename(oldPath, newPath); std::filesystem::rename(oldPath, newPath);
return newPath; return newPath;
} }
void Storage::FillSampleDirectoryTree(FilePropertyDirectoryTree *node, const std::filesystem::path &directory) const void Storage::FillSampleDirectoryTree(FilePropertyDirectoryTree *node, const std::filesystem::path &directory) const
@@ -2102,23 +2118,23 @@ FilePropertyDirectoryTree::ptr Storage::GetFilePropertydirectoryTree(const UiFil
{ {
auto childPath = uploadDirectory / modDirectoryInfo->pipedalPath; auto childPath = uploadDirectory / modDirectoryInfo->pipedalPath;
FilePropertyDirectoryTree::ptr child = std::make_unique<FilePropertyDirectoryTree>( FilePropertyDirectoryTree::ptr child = std::make_unique<FilePropertyDirectoryTree>(
childPath,modDirectoryInfo->displayName childPath, modDirectoryInfo->displayName);
);
FillSampleDirectoryTree(child.get(), childPath); FillSampleDirectoryTree(child.get(), childPath);
result->children_.push_back(std::move(child)); result->children_.push_back(std::move(child));
} }
} }
if (uiFileProperty.useLegacyModDirectory()) { if (uiFileProperty.useLegacyModDirectory())
{
auto childPath = uploadDirectory / uiFileProperty.directory(); auto childPath = uploadDirectory / uiFileProperty.directory();
FilePropertyDirectoryTree::ptr child = std::make_unique<FilePropertyDirectoryTree>( FilePropertyDirectoryTree::ptr child = std::make_unique<FilePropertyDirectoryTree>(
childPath childPath);
);
FillSampleDirectoryTree(child.get(), childPath); FillSampleDirectoryTree(child.get(), childPath);
result->children_.push_back(std::move(child)); result->children_.push_back(std::move(child));
} }
return result; return result;
}
} else { else
{
if (uiFileProperty.directory().empty()) if (uiFileProperty.directory().empty())
{ {
throw std::runtime_error("Invalid uiFileProperty"); throw std::runtime_error("Invalid uiFileProperty");
@@ -2133,10 +2149,8 @@ FilePropertyDirectoryTree::ptr Storage::GetFilePropertydirectoryTree(const UiFil
FillSampleDirectoryTree(result.get(), rootDirectory); FillSampleDirectoryTree(result.get(), rootDirectory);
return result; return result;
} }
} }
const PluginPresetIndex &Storage::GetPluginPresetIndex() const PluginPresetIndex &Storage::GetPluginPresetIndex()
{ {
return pluginPresetIndex; return pluginPresetIndex;
+2
View File
@@ -109,6 +109,8 @@ public:
void SetDataRoot(const std::filesystem::path& path); void SetDataRoot(const std::filesystem::path& path);
void SetConfigRoot(const std::filesystem::path& path); void SetConfigRoot(const std::filesystem::path& path);
const std::filesystem::path&GetConfigRoot();
const std::filesystem::path&GetDataRoot();
std::filesystem::path GetPluginUploadDirectory() const; std::filesystem::path GetPluginUploadDirectory() const;
+16
View File
@@ -230,8 +230,24 @@ static void DisplayRegulations(const std::string& country)
} }
} }
void DisplayRegDomains()
{
std::cout << "==== Regulatory domains === " << std::endl;
std::filesystem::path dbPath = "test_data/debian_bookworm_regulatory.db";
RegDb regdb{dbPath};
REQUIRE(regdb.IsValid());
auto regDomains = regdb.getRegulatoryDomains("config/iso_codes.json");
for (const auto&regDomain: regDomains)
{
std::cout << " " << regDomain.first << ": " << regDomain.second << std::endl;
}
}
TEST_CASE("Wifi Channel Test", "[wifi_channels_test]") TEST_CASE("Wifi Channel Test", "[wifi_channels_test]")
{ {
DisplayRegDomains();
DisplayRegulations("CA"); DisplayRegulations("CA");
DisplayRegulations("US"); DisplayRegulations("US");
DisplayRegulations("JP"); DisplayRegulations("JP");