Просмотр исходного кода

[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.
あく 3 лет назад
Родитель
Сommit
fae392d84e

+ 1 - 1
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);
 }
 
 /* 

+ 1 - 1
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,
 )

+ 94 - 1
applications/subghz/subghz_cli.c

@@ -16,9 +16,14 @@
 #include <notification/notification_messages.h>
 #include <flipper_format/flipper_format_i.h>
 
+#include <flipper.pb.h>
+#include <pb_decode.h>
+
 #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
 }

+ 1 - 1
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;

+ 1 - 1
assets/protobuf

@@ -1 +1 @@
-Subproject commit cc5918dc488ac3617012ce5377114e086b447324
+Subproject commit 6727eaf287db077dcd28719cd764f5804712223e

+ 1 - 0
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();
 

+ 7 - 1
firmware/targets/f7/furi_hal/furi_hal_info.c

@@ -1,6 +1,11 @@
 #include <furi_hal_info.h>
-#include <furi_hal.h>
+#include <furi_hal_region.h>
+#include <furi_hal_version.h>
+#include <furi_hal_bt.h>
+#include <furi_hal_crypto.h>
+
 #include <shci.h>
+#include <m-string.h>
 #include <protobuf_version.h>
 
 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);

+ 135 - 0
firmware/targets/f7/furi_hal/furi_hal_region.c

@@ -0,0 +1,135 @@
+#include <furi_hal_region.h>
+#include <furi_hal_version.h>
+
+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;
+}

+ 2 - 45
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 <furi_hal_region.h>
 #include <furi_hal_version.h>
 #include <furi_hal_rtc.h>
 #include <furi_hal_gpio.h>
@@ -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;

+ 1 - 0
firmware/targets/furi_hal_include/furi_hal.h

@@ -18,6 +18,7 @@ template <unsigned int N> 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"

+ 73 - 0
firmware/targets/furi_hal_include/furi_hal_region.h

@@ -0,0 +1,73 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+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);

+ 0 - 8
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

+ 1 - 1
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;
     }