From fae392d84eaa3f50d78e8d1b7fc3b19ac6f3c3d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 11 Aug 2022 18:21:56 +0900 Subject: [PATCH] [FL-2677] SubGhz: region provisioning (#1574) * FuriHal: region HAL draft * FuriHal,SubGhz: complete region provisioning. * Rpc: fix null pointer dereference. * Cli: device info formatting * FuriHal: region provisioning fixes and documentation. --- applications/cli/cli_commands.c | 2 +- applications/loader/application.fam | 2 +- applications/subghz/subghz_cli.c | 95 +++++++++++- applications/subghz/subghz_i.c | 2 +- assets/protobuf | 2 +- firmware/targets/f7/furi_hal/furi_hal.c | 1 + firmware/targets/f7/furi_hal/furi_hal_info.c | 8 +- .../targets/f7/furi_hal/furi_hal_region.c | 135 ++++++++++++++++++ .../targets/f7/furi_hal/furi_hal_subghz.c | 47 +----- firmware/targets/furi_hal_include/furi_hal.h | 1 + .../furi_hal_include/furi_hal_region.h | 73 ++++++++++ .../furi_hal_include/furi_hal_subghz.h | 8 -- lib/subghz/subghz_tx_rx_worker.c | 2 +- 13 files changed, 318 insertions(+), 60 deletions(-) create mode 100644 firmware/targets/f7/furi_hal/furi_hal_region.c create mode 100644 firmware/targets/furi_hal_include/furi_hal_region.h diff --git a/applications/cli/cli_commands.c b/applications/cli/cli_commands.c index 19887796..177a274a 100644 --- a/applications/cli/cli_commands.c +++ b/applications/cli/cli_commands.c @@ -15,7 +15,7 @@ void cli_command_device_info_callback(const char* key, const char* value, bool last, void* context) { UNUSED(context); UNUSED(last); - printf("%-24s: %s\r\n", key, value); + printf("%-30s: %s\r\n", key, value); } /* diff --git a/applications/loader/application.fam b/applications/loader/application.fam index 16351f8d..c1ba4e54 100644 --- a/applications/loader/application.fam +++ b/applications/loader/application.fam @@ -5,6 +5,6 @@ App( entry_point="loader_srv", cdefines=["SRV_LOADER"], requires=["gui"], - stack_size=1 * 1024, + stack_size=2 * 1024, order=90, ) diff --git a/applications/subghz/subghz_cli.c b/applications/subghz/subghz_cli.c index 2b6fdd84..09dae048 100644 --- a/applications/subghz/subghz_cli.c +++ b/applications/subghz/subghz_cli.c @@ -16,9 +16,14 @@ #include #include +#include +#include + #define SUBGHZ_FREQUENCY_RANGE_STR \ "299999755...348000000 or 386999938...464000000 or 778999847...928000000" +#define SUBGHZ_REGION_FILENAME "/int/.region_data" + void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -533,7 +538,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) { return; } } - if(!furi_hal_subghz_is_tx_allowed(frequency)) { + if(!furi_hal_region_is_frequency_allowed(frequency)) { printf( "In your region, only reception on this frequency (%lu) is allowed,\r\n" "the actual operation of the application is not possible\r\n ", @@ -756,6 +761,46 @@ static void subghz_cli_command(Cli* cli, string_t args, void* context) { string_clear(cmd); } +static bool + subghz_on_system_start_istream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { + File* file = istream->state; + uint16_t ret = storage_file_read(file, buf, count); + return (count == ret); +} + +static bool subghz_on_system_start_istream_decode_band( + pb_istream_t* stream, + const pb_field_t* field, + void** arg) { + (void)field; + FuriHalRegion* region = *arg; + + PB_Region_Band band = {0}; + if(!pb_decode(stream, PB_Region_Band_fields, &band)) { + FURI_LOG_E("SubGhzOnStart", "PB Region band decode error: %s", PB_GET_ERROR(stream)); + return false; + } + + region->bands_count += 1; + region = + realloc(region, sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count); + size_t pos = region->bands_count - 1; + region->bands[pos].start = band.start; + region->bands[pos].end = band.end; + region->bands[pos].power_limit = band.power_limit; + region->bands[pos].duty_cycle = band.duty_cycle; + *arg = region; + + FURI_LOG_I( + "SubGhzOnStart", + "Add allowed band: start %dHz, stop %dHz, power_limit %ddBm, duty_cycle %d%%", + band.start, + band.end, + band.power_limit, + band.duty_cycle); + return true; +} + void subghz_on_system_start() { #ifdef SRV_CLI Cli* cli = furi_record_open(RECORD_CLI); @@ -766,4 +811,52 @@ void subghz_on_system_start() { #else UNUSED(subghz_cli_command); #endif + +#ifdef SRV_STORAGE + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + FileInfo fileinfo = {0}; + PB_Region pb_region = {0}; + pb_region.bands.funcs.decode = subghz_on_system_start_istream_decode_band; + + do { + if(storage_common_stat(storage, SUBGHZ_REGION_FILENAME, &fileinfo) != FSE_OK || + fileinfo.size == 0) { + FURI_LOG_W("SubGhzOnStart", "Region data is missing or empty"); + break; + } + + if(!storage_file_open(file, SUBGHZ_REGION_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E("SubGhzOnStart", "Unable to open region data"); + break; + } + + pb_istream_t istream = { + .callback = subghz_on_system_start_istream_read, + .state = file, + .errmsg = NULL, + .bytes_left = fileinfo.size, + }; + + pb_region.bands.arg = malloc(sizeof(FuriHalRegion)); + if(!pb_decode(&istream, PB_Region_fields, &pb_region)) { + FURI_LOG_E("SubGhzOnStart", "Invalid region data"); + free(pb_region.bands.arg); + break; + } + + FuriHalRegion* region = pb_region.bands.arg; + memcpy( + region->country_code, + pb_region.country_code->bytes, + pb_region.country_code->size < 4 ? pb_region.country_code->size : 3); + furi_hal_region_set(region); + } while(0); + + pb_release(PB_Region_fields, &pb_region); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +#else + UNUSED(subghz_cli_command); +#endif } diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index 00cc922c..b08b658a 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -278,7 +278,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { break; } - if(!furi_hal_subghz_is_tx_allowed(temp_data32)) { + if(!furi_hal_region_is_frequency_allowed(temp_data32)) { FURI_LOG_E(TAG, "This frequency can only be used for RX in your region"); load_key_state = SubGhzLoadKeyStateOnlyRx; break; diff --git a/assets/protobuf b/assets/protobuf index cc5918dc..6727eaf2 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit cc5918dc488ac3617012ce5377114e086b447324 +Subproject commit 6727eaf287db077dcd28719cd764f5804712223e diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 23f40973..d0856127 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -49,6 +49,7 @@ void furi_hal_init() { FURI_LOG_I(TAG, "GPIO OK"); furi_hal_version_init(); + furi_hal_region_init(); furi_hal_spi_init(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index cf7140eb..1f75ea33 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -1,6 +1,11 @@ #include -#include +#include +#include +#include +#include + #include +#include #include void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) { @@ -45,6 +50,7 @@ void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) { out("hardware_color", string_get_cstr(value), false, context); string_printf(value, "%d", furi_hal_version_get_hw_region()); out("hardware_region", string_get_cstr(value), false, context); + out("hardware_region_provisioned", furi_hal_region_get_name(), false, context); const char* name = furi_hal_version_get_name_ptr(); if(name) { out("hardware_name", name, false, context); diff --git a/firmware/targets/f7/furi_hal/furi_hal_region.c b/firmware/targets/f7/furi_hal/furi_hal_region.c new file mode 100644 index 00000000..143d5c18 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_region.c @@ -0,0 +1,135 @@ +#include +#include + +const FuriHalRegion furi_hal_region_zero = { + .country_code = "00", + .bands_count = 1, + .bands = { + { + .start = 0, + .end = 1000000000, + .power_limit = 12, + .duty_cycle = 50, + }, + }}; + +const FuriHalRegion furi_hal_region_eu_ru = { + .country_code = "EU", + .bands_count = 2, + .bands = { + { + .start = 433050000, + .end = 434790000, + .power_limit = 12, + .duty_cycle = 50, + }, + { + .start = 868150000, + .end = 868550000, + .power_limit = 12, + .duty_cycle = 50, + }}}; + +const FuriHalRegion furi_hal_region_us_ca_au = { + .country_code = "US", + .bands_count = 3, + .bands = { + { + .start = 304100000, + .end = 321950000, + .power_limit = 12, + .duty_cycle = 50, + }, + { + .start = 433050000, + .end = 434790000, + .power_limit = 12, + .duty_cycle = 50, + }, + { + .start = 915000000, + .end = 928000000, + .power_limit = 12, + .duty_cycle = 50, + }}}; + +const FuriHalRegion furi_hal_region_jp = { + .country_code = "JP", + .bands_count = 2, + .bands = { + { + .start = 312000000, + .end = 315250000, + .power_limit = 12, + .duty_cycle = 50, + }, + { + .start = 920500000, + .end = 923500000, + .power_limit = 12, + .duty_cycle = 50, + }}}; + +static const FuriHalRegion* furi_hal_region = NULL; + +void furi_hal_region_init() { + FuriHalVersionRegion region = furi_hal_version_get_hw_region(); + + if(region == FuriHalVersionRegionUnknown) { + furi_hal_region = &furi_hal_region_zero; + } else if(region == FuriHalVersionRegionEuRu) { + furi_hal_region = &furi_hal_region_eu_ru; + } else if(region == FuriHalVersionRegionUsCaAu) { + furi_hal_region = &furi_hal_region_us_ca_au; + } else if(region == FuriHalVersionRegionJp) { + furi_hal_region = &furi_hal_region_jp; + } +} + +const FuriHalRegion* furi_hal_region_get() { + return furi_hal_region; +} + +void furi_hal_region_set(FuriHalRegion* region) { + furi_hal_region = region; +} + +bool furi_hal_region_is_provisioned() { + return furi_hal_region != NULL; +} + +const char* furi_hal_region_get_name() { + if(furi_hal_region) { + return furi_hal_region->country_code; + } else { + return "--"; + } +} + +bool furi_hal_region_is_frequency_allowed(uint32_t frequency) { + if(!furi_hal_region) { + return false; + } + + const FuriHalRegionBand* band = furi_hal_region_get_band(frequency); + if(!band) { + return false; + } + + return true; +} + +const FuriHalRegionBand* furi_hal_region_get_band(uint32_t frequency) { + if(!furi_hal_region) { + return NULL; + } + + for(size_t i = 0; i < furi_hal_region->bands_count; i++) { + if(furi_hal_region->bands[i].start <= frequency && + furi_hal_region->bands[i].end >= frequency) { + return &furi_hal_region->bands[i]; + } + } + + return NULL; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 650f8ac3..ade46238 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -1,6 +1,7 @@ #include "furi_hal_subghz.h" #include "furi_hal_subghz_configs.h" +#include #include #include #include @@ -308,52 +309,8 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { return value; } -bool furi_hal_subghz_is_tx_allowed(uint32_t value) { - //checking regional settings - bool is_allowed = false; - switch(furi_hal_version_get_hw_region()) { - case FuriHalVersionRegionEuRu: - //433,05..434,79; 868,15..868,55 - if(!(value >= 433050000 && value <= 434790000) && - !(value >= 868150000 && value <= 868550000)) { - } else { - is_allowed = true; - } - break; - case FuriHalVersionRegionUsCaAu: - //304,10..321,95; 433,05..434,79; 915,00..928,00 - if(!(value >= 304100000 && value <= 321950000) && - !(value >= 433050000 && value <= 434790000) && - !(value >= 915000000 && value <= 928000000)) { - } else { - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - if((value >= 304100000 && value <= 321950000) && - ((furi_hal_subghz.preset == FuriHalSubGhzPresetOok270Async) || - (furi_hal_subghz.preset == FuriHalSubGhzPresetOok650Async))) { - furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable_au); - } - } - is_allowed = true; - } - break; - case FuriHalVersionRegionJp: - //312,00..315,25; 920,50..923,50 - if(!(value >= 312000000 && value <= 315250000) && - !(value >= 920500000 && value <= 923500000)) { - } else { - is_allowed = true; - } - break; - - default: - is_allowed = true; - break; - } - return is_allowed; -} - uint32_t furi_hal_subghz_set_frequency(uint32_t value) { - if(furi_hal_subghz_is_tx_allowed(value)) { + if(furi_hal_region_is_frequency_allowed(value)) { furi_hal_subghz.regulation = SubGhzRegulationTxRx; } else { furi_hal_subghz.regulation = SubGhzRegulationOnlyRx; diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 4d1fc970..2a372a6c 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -18,6 +18,7 @@ template struct STOP_EXTERNING_ME {}; #include "furi_hal_sd.h" #include "furi_hal_i2c.h" #include "furi_hal_resources.h" +#include "furi_hal_region.h" #include "furi_hal_rtc.h" #include "furi_hal_speaker.h" #include "furi_hal_gpio.h" diff --git a/firmware/targets/furi_hal_include/furi_hal_region.h b/firmware/targets/furi_hal_include/furi_hal_region.h new file mode 100644 index 00000000..10d519ab --- /dev/null +++ b/firmware/targets/furi_hal_include/furi_hal_region.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include + +typedef struct { + uint32_t start; + uint32_t end; + int8_t power_limit; + uint8_t duty_cycle; +} FuriHalRegionBand; + +typedef struct { + char country_code[4]; + uint16_t bands_count; + FuriHalRegionBand bands[]; +} FuriHalRegion; + +/** Initialize region */ +void furi_hal_region_init(); + +/** Get Region Data. + * + * Region data may be allocated in Flash or in RAM. + * Keep in mind that we don't do memory management on our side. + * + * @return pointer to FuriHalRegion instance (in RAM or Flash, check before freeing on region update) + */ +const FuriHalRegion* furi_hal_region_get(); + +/** Set device region data + * + * @param region pointer to the FuriHalRegion + */ +void furi_hal_region_set(FuriHalRegion* region); + +/** Check if region data provisioned + * + * @return true if provisioned, false otherwise + */ +bool furi_hal_region_is_provisioned(); + +/** Get region name + * + * 2 letter Region code according to iso 3166 standard + * There are 2 extra values that we use in special cases: + * - "00" - developer edition, unlocked + * - "WW" - world wide, region provisioned by default + * - "--" - no provisioned region + * + * @return Pointer to string + */ +const char* furi_hal_region_get_name(); + +/** Сheck if transmission is allowed on this frequency for your flipper region + * + * @param[in] frequency The frequency + * @param value frequency in Hz + * + * @return true if allowed + */ +bool furi_hal_region_is_frequency_allowed(uint32_t frequency); + +/** Get band data for frequency + * + * + * + * @param[in] frequency The frequency + * + * @return { description_of_the_return_value } + */ +const FuriHalRegionBand* furi_hal_region_get_band(uint32_t frequency); diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/furi_hal_include/furi_hal_subghz.h index b6d132ac..d610b01b 100644 --- a/firmware/targets/furi_hal_include/furi_hal_subghz.h +++ b/firmware/targets/furi_hal_include/furi_hal_subghz.h @@ -180,14 +180,6 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value); */ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); -/** Сheck if transmission is allowed on this frequency for your flipper region - * - * @param value frequency in Hz - * - * @return true if allowed - */ -bool furi_hal_subghz_is_tx_allowed(uint32_t value); - /** Set frequency * * @param value frequency in Hz diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 74a6183c..78a18693 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -237,7 +237,7 @@ bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { instance->worker_running = true; - if(furi_hal_subghz_is_tx_allowed(frequency)) { + if(furi_hal_region_is_frequency_allowed(frequency)) { instance->frequency = frequency; res = true; }