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

SubGhz: sending / receiving messages via subghz (#851)

* SubGhz: add worker subghz_txrx
* SubGhz: added support for transferring Russian characters and support for backspace in CLI subghz_txrx
* SubGhz: refactoring subghz_txrx_worker, added a callback for accepting data in an empty one RX buffer
* SubGhz: fix conflict
* SubGhz: fix syntax errors
* Cli: document string_move usage and its behavior
* FuriHal: update subghz api and documentation. Subghz: move chat to subghz cli subcommand.
* Subghz: update text in chat cli

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Skorpionm 4 лет назад
Родитель
Сommit
b912cc7991

+ 10 - 0
applications/cli/cli.c

@@ -113,7 +113,9 @@ void cli_prompt(Cli* cli) {
 }
 
 void cli_reset(Cli* cli) {
+    // cli->last_line is cleared and cli->line's buffer moved to cli->last_line
     string_move(cli->last_line, cli->line);
+    // Reiniting cli->line
     string_init(cli->line);
     cli->cursor_position = 0;
 }
@@ -129,7 +131,11 @@ static void cli_handle_backspace(Cli* cli) {
         string_reserve(temp, string_size(cli->line) - 1);
         string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position - 1);
         string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position);
+
+        // cli->line is cleared and temp's buffer moved to cli->line
         string_move(cli->line, temp);
+        // NO MEMORY LEAK, STOP REPORTING IT
+
         cli->cursor_position--;
     } else {
         cli_putc(CliSymbolAsciiBell);
@@ -332,7 +338,11 @@ void cli_process_input(Cli* cli) {
             string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position);
             string_push_back(temp, c);
             string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position);
+
+            // cli->line is cleared and temp's buffer moved to cli->line
             string_move(cli->line, temp);
+            // NO MEMORY LEAK, STOP REPORTING IT
+
             // Print character in replace mode
             printf("\e[4h%c\e[4l", c);
             fflush(stdout);

+ 118 - 8
applications/subghz/subghz_cli.c

@@ -9,11 +9,12 @@
 #include <lib/subghz/subghz_keystore.h>
 #include <lib/subghz/protocols/subghz_protocol_common.h>
 #include <lib/subghz/protocols/subghz_protocol_princeton.h>
+#include <lib/subghz/subghz_tx_rx_worker.h>
 
 #define SUBGHZ_FREQUENCY_RANGE_STR \
     "299999755...348000000 or 386999938...464000000 or 778999847...928000000"
 
-void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) {
+static void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) {
     uint32_t frequency = 433920000;
 
     if(string_size(args)) {
@@ -56,7 +57,7 @@ void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) {
     furi_hal_power_suppress_charge_exit();
 }
 
-void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) {
+static void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) {
     uint32_t frequency = 433920000;
 
     if(string_size(args)) {
@@ -96,7 +97,7 @@ void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) {
     furi_hal_subghz_sleep();
 }
 
-void subghz_cli_command_tx(Cli* cli, string_t args, void* context) {
+static void subghz_cli_command_tx(Cli* cli, string_t args, void* context) {
     uint32_t frequency = 433920000;
     uint32_t key = 0x0074BADE;
     size_t repeat = 10;
@@ -187,7 +188,7 @@ static void subghz_cli_command_rx_text_callback(string_t text, void* context) {
     printf("%s", string_get_cstr(text));
 }
 
-void subghz_cli_command_rx(Cli* cli, string_t args, void* context) {
+static void subghz_cli_command_rx(Cli* cli, string_t args, void* context) {
     uint32_t frequency = 433920000;
 
     if(string_size(args)) {
@@ -260,7 +261,7 @@ void subghz_cli_command_rx(Cli* cli, string_t args, void* context) {
     free(instance);
 }
 
-void subghz_cli_command_print_usage() {
+static void subghz_cli_command_print_usage() {
     printf("Usage:\r\n");
     printf("subghz <cmd> <args>\r\n");
     printf("Cmd list:\r\n");
@@ -268,9 +269,10 @@ void subghz_cli_command_print_usage() {
         "\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n");
     printf(
         "\tencrypt_raw <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt RAW data\r\n");
+    printf("\tchat <frequency:in Herz>\t - Chat with other Flippers\r\n");
 }
 
-void subghz_cli_command_encrypt_keeloq(Cli* cli, string_t args) {
+static void subghz_cli_command_encrypt_keeloq(Cli* cli, string_t args) {
     uint8_t iv[16];
 
     string_t source;
@@ -312,7 +314,7 @@ void subghz_cli_command_encrypt_keeloq(Cli* cli, string_t args) {
     string_clear(source);
 }
 
-void subghz_cli_command_encrypt_raw(Cli* cli, string_t args) {
+static void subghz_cli_command_encrypt_raw(Cli* cli, string_t args) {
     uint8_t iv[16];
 
     string_t source;
@@ -348,7 +350,110 @@ void subghz_cli_command_encrypt_raw(Cli* cli, string_t args) {
     string_clear(source);
 }
 
-void subghz_cli_command(Cli* cli, string_t args, void* context) {
+static void subghz_cli_command_chat(Cli* cli, string_t args) {
+    uint32_t frequency = 433920000;
+
+    if(string_size(args)) {
+        int ret = sscanf(string_get_cstr(args), "%lu", &frequency);
+        if(ret != 1) {
+            printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
+            cli_print_usage("subghz_txrx", "<Frequency in HZ>", string_get_cstr(args));
+            return;
+        }
+        if(!furi_hal_subghz_is_frequency_valid(frequency)) {
+            printf(
+                "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
+                frequency);
+            return;
+        }
+    }
+    if(!furi_hal_subghz_is_tx_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 ",
+            frequency);
+        return;
+    }
+
+    SubGhzTxRxWorker* subghz_txrx = subghz_tx_rx_worker_alloc();
+    subghz_tx_rx_worker_start(subghz_txrx, frequency);
+
+    printf("Receiving at frequency %lu Hz\r\n", frequency);
+    printf("Press CTRL+C to stop\r\n");
+
+    furi_hal_power_suppress_charge_enter();
+    size_t message_max_len = 64;
+    uint8_t message[64] = {0};
+    string_t input;
+    string_init(input);
+    string_t name;
+    string_init(name);
+    char c;
+    bool exit = false;
+
+    string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr());
+    string_set(input, name);
+    printf("%s", string_get_cstr(input));
+    fflush(stdout);
+
+    while(!exit) {
+        if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 0) == 1) {
+            if(c == CliSymbolAsciiETX) {
+                printf("\r\n");
+                exit = true;
+                break;
+            } else if((c >= 0x20 && c < 0x7F) || (c >= 0x80 && c < 0xF0)) {
+                putc(c, stdout);
+                fflush(stdout);
+                string_push_back(input, c);
+            } else if(c == CliSymbolAsciiBackspace) {
+                size_t len = string_size(input);
+                if(len > string_size(name)) {
+                    string_set_strn(input, string_get_cstr(input), len - 1);
+                    printf("\r");
+                    for(uint8_t i = 0; i < len; i++) {
+                        printf(" ");
+                    }
+                    printf("\r%s", string_get_cstr(input));
+                    fflush(stdout);
+                }
+            } else if(c == CliSymbolAsciiCR) {
+                printf("\r\n");
+                subghz_tx_rx_worker_write(
+                    subghz_txrx, (uint8_t*)string_get_cstr(input), strlen(string_get_cstr(input)));
+                string_printf(input, "%s", string_get_cstr(name));
+                printf("%s", string_get_cstr(input));
+                fflush(stdout);
+            }
+        }
+
+        if(subghz_tx_rx_worker_available(subghz_txrx)) {
+            memset(message, 0x00, message_max_len);
+            subghz_tx_rx_worker_read(subghz_txrx, message, message_max_len);
+            printf("\r");
+            for(uint8_t i = 0; i < 80; i++) {
+                printf(" ");
+            }
+
+            printf("\r %s\r\n", message);
+
+            printf("%s", string_get_cstr(input));
+            fflush(stdout);
+        }
+    }
+
+    printf("\r\nExit chat\r\n");
+    string_clear(input);
+    string_clear(name);
+    furi_hal_power_suppress_charge_exit();
+
+    if(subghz_tx_rx_worker_is_running(subghz_txrx)) {
+        subghz_tx_rx_worker_stop(subghz_txrx);
+        subghz_tx_rx_worker_free(subghz_txrx);
+    }
+}
+
+static void subghz_cli_command(Cli* cli, string_t args, void* context) {
     string_t cmd;
     string_init(cmd);
 
@@ -368,6 +473,11 @@ void subghz_cli_command(Cli* cli, string_t args, void* context) {
             break;
         }
 
+        if(string_cmp_str(cmd, "chat") == 0) {
+            subghz_cli_command_chat(cli, args);
+            break;
+        }
+
         subghz_cli_command_print_usage();
     } while(false);
 

+ 99 - 12
firmware/targets/f6/furi-hal/furi-hal-subghz.c

@@ -244,6 +244,54 @@ static const uint8_t furi_hal_subghz_preset_2fsk_dev4_76khz_async_regs[][2] = {
     /* End  */
     {0, 0},
 };
+static const uint8_t furi_hal_subghz_preset_msk_99_97kb_async_regs[][2] = {
+    /* GPIO GD0 */
+    {CC1101_IOCFG0, 0x06},
+
+    {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION
+    {CC1101_SYNC1, 0x46},
+    {CC1101_SYNC0, 0x4C},
+    {CC1101_ADDR, 0x00},
+    {CC1101_PKTLEN, 0x00},
+    {CC1101_CHANNR, 0x00},
+
+    {CC1101_PKTCTRL0, 0x05},
+
+    {CC1101_FSCTRL0, 0x23},
+    {CC1101_FSCTRL1, 0x06},
+
+    {CC1101_MDMCFG0, 0xF8},
+    {CC1101_MDMCFG1, 0x22},
+    {CC1101_MDMCFG2, 0x72},
+    {CC1101_MDMCFG3, 0xF8},
+    {CC1101_MDMCFG4, 0x5B},
+    {CC1101_DEVIATN, 0x47},
+
+    {CC1101_MCSM0, 0x18},
+    {CC1101_FOCCFG, 0x16},
+
+    {CC1101_AGCCTRL0, 0xB2},
+    {CC1101_AGCCTRL1, 0x00},
+    {CC1101_AGCCTRL2, 0xC7},
+
+    {CC1101_FREND0, 0x10},
+    {CC1101_FREND1, 0x56},
+
+    {CC1101_FSCAL3, 0xE9},
+    {CC1101_FSCAL2, 0x2A},
+    {CC1101_FSCAL1, 0x00},
+    {CC1101_FSCAL0, 0x1F},
+
+    {CC1101_BSCFG, 0x1C},
+    {CC1101_FSTEST, 0x59},
+
+    {CC1101_TEST2, 0x81},
+    {CC1101_TEST1, 0x35},
+    {CC1101_TEST0, 0x09},
+    /* End  */
+    {0, 0},
+};
+
 static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = {
     0x00,
     0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12
@@ -261,9 +309,16 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = {
     0x00,
     0x00,
     0x00,
-    0x00
-
-};
+    0x00};
+static const uint8_t furi_hal_subghz_preset_msk_async_patable[8] = {
+    0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00};
 
 void furi_hal_subghz_init() {
     furi_assert(furi_hal_subghz_state == SubGhzStateInit);
@@ -344,6 +399,9 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) {
     } else if(preset == FuriHalSubGhzPreset2FSKDev476Async) {
         furi_hal_subghz_load_registers(furi_hal_subghz_preset_2fsk_dev4_76khz_async_regs);
         furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable);
+    } else if(preset == FuriHalSubGhzPresetMSK99_97KbAsync) {
+        furi_hal_subghz_load_registers(furi_hal_subghz_preset_msk_99_97kb_async_regs);
+        furi_hal_subghz_load_patable(furi_hal_subghz_preset_msk_async_patable);
     } else {
         furi_crash(NULL);
     }
@@ -369,6 +427,7 @@ void furi_hal_subghz_load_patable(const uint8_t data[8]) {
 void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size) {
     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
     cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz);
+    cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_FIFO, size);
     cc1101_write_fifo(&furi_hal_spi_bus_handle_subghz, data, size);
     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
 }
@@ -379,6 +438,31 @@ void furi_hal_subghz_flush_rx() {
     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
 }
 
+bool furi_hal_subghz_rx_pipe_not_empty() {
+    CC1101RxBytes status[1];
+    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
+    cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status);
+    furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
+    // TODO: you can add a buffer overflow flag if needed
+    if(status->NUM_RXBYTES > 0) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool furi_hal_subghz_is_rx_data_crc_valid() {
+    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
+    uint8_t data[1];
+    cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data);
+    furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
+    if(((data[0] >> 7) & 0x01)) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
 void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size) {
     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
     cc1101_read_fifo(&furi_hal_spi_bus_handle_subghz, data, size);
@@ -460,18 +544,16 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) {
     return value;
 }
 
-uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
-    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
-
+bool furi_hal_subghz_is_tx_allowed(uint32_t value) {
     //checking regional settings
-    bool txrx = false;
+    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 {
-            txrx = true;
+            is_allowed = true;
         }
         break;
     case FuriHalVersionRegionUsCaAu:
@@ -480,7 +562,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
            !(value >= 433050000 && value <= 434790000) &&
            !(value >= 915000000 && value <= 928000000)) {
         } else {
-            txrx = true;
+            is_allowed = true;
         }
         break;
     case FuriHalVersionRegionJp:
@@ -488,16 +570,21 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
         if(!(value >= 312000000 && value <= 315250000) &&
            !(value >= 920500000 && value <= 923500000)) {
         } else {
-            txrx = true;
+            is_allowed = true;
         }
         break;
 
     default:
-        txrx = true;
+        is_allowed = true;
         break;
     }
+    return is_allowed;
+}
+
+uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
+    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
 
-    if(txrx) {
+    if(furi_hal_subghz_is_tx_allowed(value)) {
         furi_hal_subghz_regulation = SubGhzRegulationTxRx;
     } else {
         furi_hal_subghz_regulation = SubGhzRegulationOnlyRx;

+ 99 - 12
firmware/targets/f7/furi-hal/furi-hal-subghz.c

@@ -244,6 +244,54 @@ static const uint8_t furi_hal_subghz_preset_2fsk_dev4_76khz_async_regs[][2] = {
     /* End  */
     {0, 0},
 };
+static const uint8_t furi_hal_subghz_preset_msk_99_97kb_async_regs[][2] = {
+    /* GPIO GD0 */
+    {CC1101_IOCFG0, 0x06},
+
+    {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION
+    {CC1101_SYNC1, 0x46},
+    {CC1101_SYNC0, 0x4C},
+    {CC1101_ADDR, 0x00},
+    {CC1101_PKTLEN, 0x00},
+    {CC1101_CHANNR, 0x00},
+
+    {CC1101_PKTCTRL0, 0x05},
+
+    {CC1101_FSCTRL0, 0x23},
+    {CC1101_FSCTRL1, 0x06},
+
+    {CC1101_MDMCFG0, 0xF8},
+    {CC1101_MDMCFG1, 0x22},
+    {CC1101_MDMCFG2, 0x72},
+    {CC1101_MDMCFG3, 0xF8},
+    {CC1101_MDMCFG4, 0x5B},
+    {CC1101_DEVIATN, 0x47},
+
+    {CC1101_MCSM0, 0x18},
+    {CC1101_FOCCFG, 0x16},
+
+    {CC1101_AGCCTRL0, 0xB2},
+    {CC1101_AGCCTRL1, 0x00},
+    {CC1101_AGCCTRL2, 0xC7},
+
+    {CC1101_FREND0, 0x10},
+    {CC1101_FREND1, 0x56},
+
+    {CC1101_FSCAL3, 0xE9},
+    {CC1101_FSCAL2, 0x2A},
+    {CC1101_FSCAL1, 0x00},
+    {CC1101_FSCAL0, 0x1F},
+
+    {CC1101_BSCFG, 0x1C},
+    {CC1101_FSTEST, 0x59},
+
+    {CC1101_TEST2, 0x81},
+    {CC1101_TEST1, 0x35},
+    {CC1101_TEST0, 0x09},
+    /* End  */
+    {0, 0},
+};
+
 static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = {
     0x00,
     0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12
@@ -261,9 +309,16 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = {
     0x00,
     0x00,
     0x00,
-    0x00
-
-};
+    0x00};
+static const uint8_t furi_hal_subghz_preset_msk_async_patable[8] = {
+    0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00};
 
 void furi_hal_subghz_init() {
     furi_assert(furi_hal_subghz_state == SubGhzStateInit);
@@ -344,6 +399,9 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) {
     } else if(preset == FuriHalSubGhzPreset2FSKDev476Async) {
         furi_hal_subghz_load_registers(furi_hal_subghz_preset_2fsk_dev4_76khz_async_regs);
         furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable);
+    } else if(preset == FuriHalSubGhzPresetMSK99_97KbAsync) {
+        furi_hal_subghz_load_registers(furi_hal_subghz_preset_msk_99_97kb_async_regs);
+        furi_hal_subghz_load_patable(furi_hal_subghz_preset_msk_async_patable);
     } else {
         furi_crash(NULL);
     }
@@ -369,6 +427,7 @@ void furi_hal_subghz_load_patable(const uint8_t data[8]) {
 void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size) {
     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
     cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz);
+    cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_FIFO, size);
     cc1101_write_fifo(&furi_hal_spi_bus_handle_subghz, data, size);
     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
 }
@@ -379,6 +438,31 @@ void furi_hal_subghz_flush_rx() {
     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
 }
 
+bool furi_hal_subghz_rx_pipe_not_empty() {
+    CC1101RxBytes status[1];
+    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
+    cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status);
+    furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
+    // TODO: you can add a buffer overflow flag if needed
+    if(status->NUM_RXBYTES > 0) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool furi_hal_subghz_is_rx_data_crc_valid() {
+    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
+    uint8_t data[1];
+    cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data);
+    furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
+    if(((data[0] >> 7) & 0x01)) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
 void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size) {
     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
     cc1101_read_fifo(&furi_hal_spi_bus_handle_subghz, data, size);
@@ -460,18 +544,16 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) {
     return value;
 }
 
-uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
-    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
-
+bool furi_hal_subghz_is_tx_allowed(uint32_t value) {
     //checking regional settings
-    bool txrx = false;
+    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 {
-            txrx = true;
+            is_allowed = true;
         }
         break;
     case FuriHalVersionRegionUsCaAu:
@@ -480,7 +562,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
            !(value >= 433050000 && value <= 434790000) &&
            !(value >= 915000000 && value <= 928000000)) {
         } else {
-            txrx = true;
+            is_allowed = true;
         }
         break;
     case FuriHalVersionRegionJp:
@@ -488,16 +570,21 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
         if(!(value >= 312000000 && value <= 315250000) &&
            !(value >= 920500000 && value <= 923500000)) {
         } else {
-            txrx = true;
+            is_allowed = true;
         }
         break;
 
     default:
-        txrx = true;
+        is_allowed = true;
         break;
     }
+    return is_allowed;
+}
+
+uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
+    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
 
-    if(txrx) {
+    if(furi_hal_subghz_is_tx_allowed(value)) {
         furi_hal_subghz_regulation = SubGhzRegulationTxRx;
     } else {
         furi_hal_subghz_regulation = SubGhzRegulationOnlyRx;

+ 21 - 0
firmware/targets/furi-hal-include/furi-hal-subghz.h

@@ -20,6 +20,7 @@ typedef enum {
     FuriHalSubGhzPresetOok650Async,     /**< OOK, bandwidth 650kHz, asynchronous */
     FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */
     FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 4.760742 kHz, asynchronous */
+    FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */
 } FuriHalSubGhzPreset;
 
 /** Switchable Radio Paths */
@@ -90,6 +91,18 @@ void furi_hal_subghz_load_patable(const uint8_t data[8]);
  */
 void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size);
 
+/** Check if recieve pipe is not empty
+ *
+ * @return     true if not empty
+ */
+bool furi_hal_subghz_rx_pipe_not_empty();
+
+/** Check if recieved data crc is valid
+ *
+ * @return     true if valid
+ */
+bool furi_hal_subghz_is_rx_data_crc_valid();
+
 /** Read packet from FIFO
  *
  * @param      data  pointer
@@ -148,6 +161,14 @@ 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

+ 11 - 8
lib/drivers/cc1101.c

@@ -22,14 +22,14 @@ CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t
     while(hal_gpio_read(handle->miso));
     furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT);
 
-    assert((rx[0].CHIP_RDYn|rx[1].CHIP_RDYn) == 0);
+    assert((rx[0].CHIP_RDYn | rx[1].CHIP_RDYn) == 0);
     return rx[1];
 }
 
 CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data) {
     assert(sizeof(CC1101Status) == 1);
-    uint8_t tx[2] = { reg|CC1101_READ, 0};
-    CC1101Status rx[2] = { 0 };
+    uint8_t tx[2] = {reg | CC1101_READ, 0};
+    CC1101Status rx[2] = {0};
 
     while(hal_gpio_read(handle->miso));
     furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT);
@@ -58,8 +58,6 @@ uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle) {
 }
 
 void cc1101_reset(FuriHalSpiBusHandle* handle) {
-    delay_us(1000);
-    delay_us(1000);
     cc1101_strobe(handle, CC1101_STROBE_SRES);
 }
 
@@ -130,7 +128,7 @@ void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]) {
     while(hal_gpio_read(handle->miso));
     furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, sizeof(rx), CC1101_TIMEOUT);
 
-    assert((rx[0].CHIP_RDYn|rx[8].CHIP_RDYn) == 0);
+    assert((rx[0].CHIP_RDYn | rx[8].CHIP_RDYn) == 0);
 }
 
 uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size) {
@@ -159,9 +157,14 @@ uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* si
 
     // First byte - packet length
     furi_hal_spi_bus_trx(handle, buff_tx, buff_rx, 2, CC1101_TIMEOUT);
-    *size = buff_rx[1];
+
+    // Check that the packet is placed in the receive buffer
+    if(buff_rx[1] > 64) {
+        *size = 64;
+    } else {
+        *size = buff_rx[1];
+    }
     furi_hal_spi_bus_trx(handle, &buff_tx[1], data, *size, CC1101_TIMEOUT);
-    cc1101_flush_rx(handle);
 
     return *size;
 }

+ 3 - 4
lib/drivers/cc1101_regs.h

@@ -89,22 +89,21 @@ extern "C" {
 #define CC1101_STATUS_PARTNUM           0x30    /** Chip ID Part Number */
 #define CC1101_STATUS_VERSION           0x31    /** Chip ID Version */
 #define CC1101_STATUS_FREQEST           0x32    /** Frequency Offset Estimate from Demodulator */
-#define CC1101_STATUS_LQI               0x33    /** Demodulator Estimate for Link Quality */
+#define CC1101_STATUS_LQI               0x33    /** Demodulator Estimate for Link Quality, 7bit-CRC, 6..0-LQI*/
 #define CC1101_STATUS_RSSI              0x34    /** Received Signal Strength Indication */
 #define CC1101_STATUS_MARCSTATE         0x35    /** Main Radio Control State Machine State */
 #define CC1101_STATUS_WORTIME1          0x36    /** High Byte of WOR Time */
 #define CC1101_STATUS_WORTIME0          0x37    /** Low Byte of WOR Time */
 #define CC1101_STATUS_PKTSTATUS         0x38    /** Current GDOx Status and Packet Status */
 #define CC1101_STATUS_VCO_VC_DAC        0x39    /** Current Setting from PLL Calibration Module */
-#define CC1101_STATUS_TXBYTES           0x3A    /** Underflow and Number of Bytes */
-#define CC1101_STATUS_RXBYTES           0x3B    /** Overflow and Number of Bytes */
+#define CC1101_STATUS_TXBYTES           0x3A    /** Underflow and Number of Bytes, 7bit-Underflow, 6..0-Number of Bytes*/
+#define CC1101_STATUS_RXBYTES           0x3B    /** Overflow and Number of Bytes, 7bit-Overflow*, 6..0-Number of Bytes*/
 #define CC1101_STATUS_RCCTRL1_STATUS    0x3C    /** Last RC Oscillator Calibration Result */
 #define CC1101_STATUS_RCCTRL0_STATUS    0x3D    /** Last RC Oscillator Calibration Result */
 
 /* Some special registers, use CC1101_BURST to read/write data */
 #define CC1101_PATABLE                  0x3E    /** PATABLE register number, an 8-byte table that defines the PA control settings */
 #define CC1101_FIFO                     0x3F    /** FIFO register nunmber, can be combined with CC1101_WRITE and/or CC1101_BURST */
-
 #define CC1101_IOCFG_INV                (1<<6)  /** IOCFG inversion */
 
 typedef enum {

+ 273 - 0
lib/subghz/subghz_tx_rx_worker.c

@@ -0,0 +1,273 @@
+#include "subghz_tx_rx_worker.h"
+
+#include <stream_buffer.h>
+#include <furi.h>
+
+#define TAG "SubGhzTxRxWorker"
+
+#define GUBGHZ_TXRX_WORKER_BUF_SIZE 2048
+//you can not set more than 62 because it will not fit into the FIFO CC1101
+#define GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE 60
+
+#define GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40
+
+struct SubGhzTxRxWorker {
+    FuriThread* thread;
+    StreamBufferHandle_t stream_tx;
+    StreamBufferHandle_t stream_rx;
+
+    volatile bool worker_running;
+    volatile bool worker_stoping;
+
+    SubGhzTxRxWorkerStatus satus;
+
+    uint32_t frequency;
+
+    SubGhzTxRxWorkerCallbackHaveRead callback_have_read;
+    void* context_have_read;
+};
+
+bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) {
+    furi_assert(instance);
+    bool ret = false;
+    size_t stream_tx_free_byte = xStreamBufferSpacesAvailable(instance->stream_tx);
+    if(size && (stream_tx_free_byte >= size)) {
+        if(xStreamBufferSend(
+               instance->stream_tx, data, size, GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF) ==
+           size) {
+            ret = true;
+        }
+    }
+    return ret;
+}
+
+size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance) {
+    furi_assert(instance);
+    return xStreamBufferBytesAvailable(instance->stream_rx);
+}
+
+size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) {
+    furi_assert(instance);
+    size_t len = 0;
+    size_t stream_rx_byte = xStreamBufferBytesAvailable(instance->stream_rx);
+
+    if(stream_rx_byte > 0) {
+        if(stream_rx_byte <= size) {
+            len = xStreamBufferReceive(
+                instance->stream_rx,
+                data,
+                stream_rx_byte,
+                GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
+        } else {
+            len = xStreamBufferReceive(
+                instance->stream_rx, data, size, GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
+        }
+    }
+    return len;
+}
+
+void subghz_tx_rx_worker_set_callback_have_read(
+    SubGhzTxRxWorker* instance,
+    SubGhzTxRxWorkerCallbackHaveRead callback,
+    void* context) {
+    furi_assert(instance);
+    furi_assert(callback);
+    furi_assert(context);
+    instance->callback_have_read = callback;
+    instance->context_have_read = context;
+}
+
+bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* size) {
+    uint8_t timeout = 20;
+    bool ret = false;
+    if(instance->satus != SubGhzTxRxWorkerStatusRx) {
+        furi_hal_subghz_rx();
+        instance->satus = SubGhzTxRxWorkerStatusRx;
+        osDelay(1);
+    }
+    //waiting for reception to complete
+    while(hal_gpio_read(&gpio_cc1101_g0)) {
+        osDelay(1);
+        if(!--timeout) {
+            FURI_LOG_W(TAG, "RX cc1101_g0 timeout");
+            furi_hal_subghz_flush_rx();
+            furi_hal_subghz_rx();
+            break;
+        }
+    }
+
+    if(furi_hal_subghz_rx_pipe_not_empty()) {
+        if(furi_hal_subghz_is_rx_data_crc_valid()) {
+            furi_hal_subghz_read_packet(data, size);
+            ret = true;
+        }
+        furi_hal_subghz_flush_rx();
+        furi_hal_subghz_rx();
+    }
+    return ret;
+}
+
+void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) {
+    uint8_t timeout = 40;
+    if(instance->satus != SubGhzTxRxWorkerStatusIDLE) {
+        furi_hal_subghz_idle();
+    }
+    furi_hal_subghz_write_packet(data, size);
+    instance->satus = SubGhzTxRxWorkerStatusTx;
+
+    furi_hal_subghz_tx(); //start send
+
+    while(!hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted
+        osDelay(1);
+        if(!--timeout) {
+            FURI_LOG_W(TAG, "TX !cc1101_g0 timeout");
+            break;
+        }
+    }
+    while(hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet
+        osDelay(1);
+        if(!--timeout) {
+            FURI_LOG_W(TAG, "TX cc1101_g0 timeout");
+            break;
+        }
+    }
+    furi_hal_subghz_idle();
+    instance->satus = SubGhzTxRxWorkerStatusIDLE;
+}
+/** Worker thread
+ * 
+ * @param context 
+ * @return exit code 
+ */
+static int32_t subghz_tx_rx_worker_thread(void* context) {
+    SubGhzTxRxWorker* instance = context;
+    FURI_LOG_I(TAG, "Worker start");
+
+    furi_hal_subghz_reset();
+    furi_hal_subghz_idle();
+    furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync);
+    hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+
+    furi_hal_subghz_set_frequency_and_path(instance->frequency);
+    furi_hal_subghz_flush_rx();
+
+    uint8_t data[GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE] = {0};
+    size_t size_tx = 0;
+    uint8_t size_rx[1] = {0};
+    uint8_t timeout_tx = 0;
+    bool callback_rx = false;
+
+    while(instance->worker_running) {
+        //transmit
+        size_tx = xStreamBufferBytesAvailable(instance->stream_tx);
+        if(size_tx > 0 && !timeout_tx) {
+            timeout_tx = 20; //20ms
+            if(size_tx > GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE) {
+                xStreamBufferReceive(
+                    instance->stream_tx,
+                    &data,
+                    GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE,
+                    GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
+                subghz_tx_rx_worker_tx(instance, data, GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE);
+            } else {
+                //todo checking that he managed to write all the data to the TX buffer
+                xStreamBufferReceive(
+                    instance->stream_tx, &data, size_tx, GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
+                subghz_tx_rx_worker_tx(instance, data, size_tx);
+            }
+        } else {
+            //recive
+            if(subghz_tx_rx_worker_rx(instance, data, size_rx)) {
+                if(xStreamBufferSpacesAvailable(instance->stream_rx) >= size_rx[0]) {
+                    if(instance->callback_have_read &&
+                       xStreamBufferBytesAvailable(instance->stream_rx) == 0) {
+                        callback_rx = true;
+                    }
+                    //todo checking that he managed to write all the data to the RX buffer
+                    xStreamBufferSend(
+                        instance->stream_rx,
+                        &data,
+                        size_rx[0],
+                        GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
+                    if(callback_rx) {
+                        instance->callback_have_read(instance->context_have_read);
+                        callback_rx = false;
+                    }
+                } else {
+                    //todo RX buffer overflow
+                }
+            }
+        }
+
+        if(timeout_tx) timeout_tx--;
+        osDelay(1);
+    }
+
+    furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
+    furi_hal_subghz_sleep();
+
+    FURI_LOG_I(TAG, "Worker stop");
+    return 0;
+}
+
+SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() {
+    SubGhzTxRxWorker* instance = furi_alloc(sizeof(SubGhzTxRxWorker));
+
+    instance->thread = furi_thread_alloc();
+    furi_thread_set_name(instance->thread, "SubghzTxRxWorker");
+    furi_thread_set_stack_size(instance->thread, 2048);
+    furi_thread_set_context(instance->thread, instance);
+    furi_thread_set_callback(instance->thread, subghz_tx_rx_worker_thread);
+    instance->stream_tx =
+        xStreamBufferCreate(sizeof(uint8_t) * GUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t));
+    instance->stream_rx =
+        xStreamBufferCreate(sizeof(uint8_t) * GUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t));
+
+    instance->satus = SubGhzTxRxWorkerStatusIDLE;
+    instance->worker_stoping = true;
+
+    return instance;
+}
+
+void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance) {
+    furi_assert(instance);
+
+    vStreamBufferDelete(instance->stream_tx);
+    vStreamBufferDelete(instance->stream_rx);
+    furi_thread_free(instance->thread);
+
+    free(instance);
+}
+
+bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) {
+    furi_assert(instance);
+    furi_assert(!instance->worker_running);
+    bool res = false;
+    xStreamBufferReset(instance->stream_tx);
+    xStreamBufferReset(instance->stream_rx);
+
+    instance->worker_running = true;
+
+    furi_thread_start(instance->thread);
+
+    if(furi_hal_subghz_is_tx_allowed(frequency)) {
+        instance->frequency = frequency;
+        res = true;
+    }
+
+    return res;
+}
+
+void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance) {
+    furi_assert(instance);
+    furi_assert(instance->worker_running);
+
+    instance->worker_running = false;
+
+    furi_thread_join(instance->thread);
+}
+
+bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance) {
+    furi_assert(instance);
+    return instance->worker_running;
+}

+ 81 - 0
lib/subghz/subghz_tx_rx_worker.h

@@ -0,0 +1,81 @@
+#pragma once
+
+#include <furi-hal.h>
+
+typedef void (*SubGhzTxRxWorkerCallbackHaveRead)(void* context);
+
+typedef struct SubGhzTxRxWorker SubGhzTxRxWorker;
+
+typedef enum {
+    SubGhzTxRxWorkerStatusIDLE,
+    SubGhzTxRxWorkerStatusTx,
+    SubGhzTxRxWorkerStatusRx,
+} SubGhzTxRxWorkerStatus;
+
+/** SubGhzTxRxWorker, add data to transfer
+ * 
+ * @param instance  SubGhzTxRxWorker instance
+ * @param data      *data
+ * @param size      data size
+ * @return bool     true if ok
+ */
+bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size);
+
+/** SubGhzTxRxWorker, get available data
+ * 
+ * @param instance   SubGhzTxRxWorker instance
+ * @return size_t    data size
+ */
+size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance);
+
+/** SubGhzTxRxWorker, read data
+ * 
+ * @param instance   SubGhzTxRxWorker instance
+ * @param data       *data
+ * @param size       max data size, which can be read
+ * @return size_t    data size, how much is actually read
+ */
+size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size);
+
+/** Сallback SubGhzTxRxWorker when there is data to read in an empty buffer
+ * 
+ * @param instance SubGhzTxRxWorker instance
+ * @param callback SubGhzTxRxWorkerCallbackHaveRead callback
+ * @param context
+ */
+void subghz_tx_rx_worker_set_callback_have_read(
+    SubGhzTxRxWorker* instance,
+    SubGhzTxRxWorkerCallbackHaveRead callback,
+    void* context);
+
+/** Allocate SubGhzTxRxWorker
+ * 
+ * @return SubGhzTxRxWorker* 
+ */
+SubGhzTxRxWorker* subghz_tx_rx_worker_alloc();
+
+/** Free SubGhzTxRxWorker
+ * 
+ * @param instance SubGhzTxRxWorker instance
+ */
+void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance);
+
+/** Start SubGhzTxRxWorker
+ * 
+ * @param instance SubGhzTxRxWorker instance
+ * @return bool - true if ok
+ */
+bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency);
+
+/** Stop SubGhzTxRxWorker
+ * 
+ * @param instance SubGhzTxRxWorker instance
+ */
+void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance);
+
+/** Check if worker is running
+ * 
+ * @param instance SubGhzTxRxWorker instance
+ * @return bool - true if running
+ */
+bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance);