From d73d00779788db49d5306f51fb4c789b54568281 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Mon, 15 Jan 2024 09:38:43 +0400 Subject: [PATCH] SubGhz: add `subghz tx_from_file` CLI cmd, major TX flow refactoring, various improvements and bug fixes (#3302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SubGhz: add cmd CLI "subghz tx_from_file" * SubGhz: add sending raw.sub files * SubGhz: add load custom preset * SubGhz: remove unnecessary files * SubGhz: change message * SubGhz: fix printf formatting * SubGhz: Cli refactoring code * FuriHal: add furi_hal_subghz Tx Rx IDLE state switching test * SubGhz: remove debug code, fix ext driver compilation * SubGhz: cleanup code, move wait status routine to cc1101 driver * SubGhz: proper pin mode transition in tx stop isr routine, proper DMA and ISR priorities, fix issue with async tx stuck * SubGhz: simplify async tx stop flow, fix ISR ARR check condition race * SubGhz: check ARR only when we transmitting * SubGhz: check ARR only when we transmitting for ext cc1101 * SubGhz: lower ISR priorities to safe level * SubGhz: proper gpio config, comments update Co-authored-by: あく --- .../drivers/subghz/cc1101_ext/cc1101_ext.c | 49 +-- .../protocol_support/mf_classic/mf_classic.c | 2 +- .../subghz_frequency_analyzer_worker.c | 13 +- applications/main/subghz/subghz_cli.c | 318 ++++++++++++++++-- lib/drivers/cc1101.c | 14 + lib/drivers/cc1101.h | 10 + targets/f7/furi_hal/furi_hal_interrupt.c | 3 +- targets/f7/furi_hal/furi_hal_subghz.c | 68 ++-- 8 files changed, 364 insertions(+), 113 deletions(-) diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c index 348f3891b..f8ce82bf3 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -45,7 +45,6 @@ typedef enum { SubGhzDeviceCC1101ExtStateIdle, /**< Idle, energy save mode */ SubGhzDeviceCC1101ExtStateAsyncRx, /**< Async RX started */ SubGhzDeviceCC1101ExtStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzDeviceCC1101ExtStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ } SubGhzDeviceCC1101ExtState; /** SubGhz regulation, receive transmission on the current frequency for the @@ -392,12 +391,18 @@ void subghz_device_cc1101_ext_reset() { void subghz_device_cc1101_ext_idle() { furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to IDLE mode + furi_check(cc1101_wait_status_state( + subghz_device_cc1101_ext->spi_bus_handle, CC1101StateIDLE, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); } void subghz_device_cc1101_ext_rx() { furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_rx(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to Rx mode + furi_check( + cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateRX, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); } @@ -405,6 +410,9 @@ bool subghz_device_cc1101_ext_tx() { if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false; furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_tx(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to Tx mode + furi_check( + cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateTX, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); return true; } @@ -653,7 +661,6 @@ static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t sa if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA); } - LL_TIM_EnableIT_UPDATE(TIM17); break; } else { // Lowest possible value is 4us @@ -689,22 +696,6 @@ static void subghz_device_cc1101_ext_async_tx_dma_isr() { #endif } -static void subghz_device_cc1101_ext_async_tx_timer_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { - if(LL_TIM_GetAutoReload(TIM17) == 0) { - if(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) { - LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); - subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - if(subghz_device_cc1101_ext->async_mirror_pin != NULL) - furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); - LL_TIM_DisableCounter(TIM17); - } - } - LL_TIM_ClearFlag_UPDATE(TIM17); - } -} - bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context) { furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); furi_assert(callback); @@ -733,7 +724,7 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | - LL_DMA_MODE_NORMAL); + LL_DMA_PRIORITY_VERYHIGH); LL_DMA_SetDataLength( SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMAMUX_REQ_TIM17_UP); @@ -756,9 +747,6 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_DisableARRPreload(TIM17); - furi_hal_interrupt_set_isr( - FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL); - subghz_device_cc1101_ext_async_tx_middleware_idle( &subghz_device_cc1101_ext->async_tx.middleware); subghz_device_cc1101_ext_async_tx_refill( @@ -816,22 +804,21 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb } bool subghz_device_cc1101_ext_is_async_tx_complete() { - return subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd; + return ( + (subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) && + (LL_TIM_GetAutoReload(TIM17) == 0)); } void subghz_device_cc1101_ext_stop_async_tx() { - furi_assert( - subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx || - subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd); - - // Deinitialize GPIO - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - furi_hal_gpio_init( - subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); // Shutdown radio subghz_device_cc1101_ext_idle(); + // Deinitialize GPIO + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + // Deinitialize Timer furi_hal_bus_disable(FuriHalBusTIM17); furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 4f4668ea7..7feeccf22 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -121,7 +121,7 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { } } -static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { +static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { //-V524 const NfcDevice* device = instance->nfc_device; const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 4a4445faa..995434631 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -72,7 +72,6 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { uint32_t frequency = 0; float rssi_temp = -127.0f; uint32_t frequency_temp = 0; - CC1101Status status; //Start CC1101 furi_hal_subghz_reset(); @@ -123,9 +122,9 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { subghz_setting_get_frequency(instance->setting, i)); cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); - do { - status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); - } while(status.STATE != CC1101StateIDLE); + + furi_check(cc1101_wait_status_state( + &furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); @@ -168,9 +167,9 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, i); cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); - do { - status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); - } while(status.STATE != CC1101StateIDLE); + + furi_check(cc1101_wait_status_state( + &furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index e1b5e8684..3400011ec 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -49,6 +49,28 @@ static void subghz_cli_radio_device_power_off() { if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); } +static SubGhzEnvironment* subghz_cli_environment_init(void) { + SubGhzEnvironment* environment = subghz_environment_alloc(); + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { + printf("Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); + } else { + printf("Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); + } + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { + printf("Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); + } else { + printf("Load_keystore keeloq_mfcodes_user \033[0;33mAbsent\033[0m\r\n"); + } + subghz_environment_set_came_atomo_rainbow_table_file_name( + environment, SUBGHZ_CAME_ATOMO_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); + subghz_environment_set_nice_flor_s_rainbow_table_file_name( + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + return environment; +} + void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -324,16 +346,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); furi_check(instance->stream); - SubGhzEnvironment* environment = subghz_environment_alloc(); - subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME); - subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); - subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, SUBGHZ_CAME_ATOMO_DIR_NAME); - subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); - subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + SubGhzEnvironment* environment = subghz_cli_environment_init(); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -517,25 +530,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { // Allocate context SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); - SubGhzEnvironment* environment = subghz_environment_alloc(); - if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); - } else { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); - } - if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); - } else { - printf( - "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n"); - } - subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, SUBGHZ_CAME_ATOMO_DIR_NAME); - subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); - subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + SubGhzEnvironment* environment = subghz_cli_environment_init(); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -580,6 +575,262 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { furi_string_free(file_name); } +static FuriHalSubGhzPreset subghz_cli_get_preset_name(const char* preset_name) { + FuriHalSubGhzPreset preset = FuriHalSubGhzPresetIDLE; + if(!strcmp(preset_name, "FuriHalSubGhzPresetOok270Async")) { + preset = FuriHalSubGhzPresetOok270Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPresetOok650Async")) { + preset = FuriHalSubGhzPresetOok650Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev238Async")) { + preset = FuriHalSubGhzPreset2FSKDev238Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev476Async")) { + preset = FuriHalSubGhzPreset2FSKDev476Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPresetCustom")) { + preset = FuriHalSubGhzPresetCustom; + } else { + printf("subghz tx_from_file: unknown preset"); + } + return preset; +} + +void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) { // -V524 + UNUSED(context); + FuriString* file_name; + file_name = furi_string_alloc(); + furi_string_set(file_name, ANY_PATH("subghz/test.sub")); + uint32_t repeat = 10; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + FlipperFormat* fff_data_raw = flipper_format_string_alloc(); + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint32_t temp_data32; + bool check_file = false; + const SubGhzDevice* device = NULL; + + uint32_t frequency = 0; + SubGhzTransmitter* transmitter = NULL; + + subghz_devices_init(); + + SubGhzEnvironment* environment = subghz_cli_environment_init(); + + do { + if(furi_string_size(args)) { + if(!args_read_string_and_trim(args, file_name)) { + cli_print_usage( + "subghz tx_from_file: ", + " ", + furi_string_get_cstr(args)); + break; + } + } + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &repeat, &device_ind); + if(ret != 2) { + printf("sscanf returned %d, repeat: %lu device: %lu\r\n", ret, repeat, device_ind); + cli_print_usage( + "subghz tx_from_file:", + " ", + furi_string_get_cstr(args)); + break; + } + } + + device = subghz_cli_command_get_device(&device_ind); + if(device == NULL) { + printf("subghz tx_from_file: \033[0;31mError device not found\033[0m\r\n"); + break; + } + + if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { + printf( + "subghz tx_from_file: \033[0;31mError open file\033[0m %s\r\n", + furi_string_get_cstr(file_name)); + break; + } + + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + printf("subghz tx_from_file: \033[0;31mMissing or incorrect header\033[0m\r\n"); + break; + } + + if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || + (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && + temp_data32 == SUBGHZ_KEY_FILE_VERSION) { + } else { + printf("subghz tx_from_file: \033[0;31mType or version mismatch\033[0m\r\n"); + break; + } + + //Load frequency + if(!flipper_format_read_uint32(fff_data_file, "Frequency", &frequency, 1)) { + printf("subghz tx_from_file: \033[0;31mMissing Frequency\033[0m\r\n"); + break; + } + + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf("subghz tx_from_file: \033[0;31mFrequency not supported\033[0m\r\n"); + break; + } + + //Load preset + if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { + printf("subghz tx_from_file: \033[0;31mMissing Preset\033[0m\r\n"); + break; + } + + subghz_devices_begin(device); + subghz_devices_reset(device); + + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + uint8_t* custom_preset_data; + uint32_t custom_preset_data_size; + if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32)) + break; + if(!temp_data32 || (temp_data32 % 2)) { + printf("subghz tx_from_file: \033[0;31mCustom_preset_data size error\033[0m\r\n"); + break; + } + custom_preset_data_size = sizeof(uint8_t) * temp_data32; + custom_preset_data = malloc(custom_preset_data_size); + if(!flipper_format_read_hex( + fff_data_file, + "Custom_preset_data", + custom_preset_data, + custom_preset_data_size)) { + printf("subghz tx_from_file: \033[0;31mCustom_preset_data read error\033[0m\r\n"); + break; + } + subghz_devices_load_preset( + device, + subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)), + custom_preset_data); + free(custom_preset_data); + } else { + subghz_devices_load_preset( + device, subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)), NULL); + } + + subghz_devices_set_frequency(device, frequency); + + //Load protocol + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + printf("subghz tx_from_file: \033[0;31mMissing protocol\033[0m\r\n"); + break; + } + + SubGhzProtocolStatus status; + bool is_init_protocol = true; + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { // if RAW protocol + subghz_protocol_raw_gen_fff_data( + fff_data_raw, furi_string_get_cstr(file_name), subghz_devices_get_name(device)); + + transmitter = + subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str)); + if(transmitter == NULL) { + printf("subghz tx_from_file: \033[0;31mError transmitter\033[0m\r\n"); + is_init_protocol = false; + } + + if(is_init_protocol) { + status = subghz_transmitter_deserialize(transmitter, fff_data_raw); + if(status != SubGhzProtocolStatusOk) { + printf( + "subghz tx_from_file: \033[0;31mError deserialize protocol\033[0m %d\r\n", + status); + is_init_protocol = false; + } + } + + } else { //if not RAW protocol + flipper_format_insert_or_update_uint32(fff_data_file, "Repeat", &repeat, 1); + + transmitter = + subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str)); + if(transmitter == NULL) { + printf("subghz tx_from_file: \033[0;31mError transmitter\033[0m\r\n"); + is_init_protocol = false; + } + if(is_init_protocol) { + status = subghz_transmitter_deserialize(transmitter, fff_data_file); + if(status != SubGhzProtocolStatusOk) { + printf( + "subghz tx_from_file: \033[0;31mError deserialize protocol\033[0m %d\r\n", + status); + is_init_protocol = false; + } + } + + flipper_format_delete_key(fff_data_file, "Repeat"); + } + + if(is_init_protocol) { + check_file = true; + } else { + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_transmitter_free(transmitter); + } + + } while(false); + + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); + + if(check_file) { + furi_hal_power_suppress_charge_enter(); + + printf( + "Listening at \033[0;33m%s\033[0m. Frequency=%lu, Protocol=%s\r\n\r\nPress CTRL+C to stop\r\n\r\n", + furi_string_get_cstr(file_name), + frequency, + furi_string_get_cstr(temp_str)); + do { + //delay in downloading files and other preparatory processes + furi_delay_ms(200); + if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) { + while( + !(subghz_devices_is_async_complete_tx(device) || + cli_cmd_interrupt_received(cli))) { + printf("."); + fflush(stdout); + furi_delay_ms(333); + } + subghz_devices_stop_async_tx(device); + + } else { + printf("Transmission on this frequency is restricted in your region\r\n"); + } + + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { + subghz_transmitter_stop(transmitter); + repeat--; + if(!cli_cmd_interrupt_received(cli) && repeat) + subghz_transmitter_deserialize(transmitter, fff_data_raw); + } + + } while(!cli_cmd_interrupt_received(cli) && + (repeat && !strcmp(furi_string_get_cstr(temp_str), "RAW"))); + + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_cli_radio_device_power_off(); + + furi_hal_power_suppress_charge_exit(); + + subghz_transmitter_free(transmitter); + } + flipper_format_free(fff_data_raw); + furi_string_free(file_name); + furi_string_free(temp_str); + subghz_devices_deinit(); + subghz_environment_free(environment); +} + static void subghz_cli_command_print_usage() { printf("Usage:\r\n"); printf("subghz \r\n"); @@ -592,11 +843,13 @@ static void subghz_cli_command_print_usage() { printf("\trx \t - Receive\r\n"); printf("\trx_raw \t - Receive RAW\r\n"); printf("\tdecode_raw \t - Testing\r\n"); + printf( + "\ttx_from_file \t - Transmitting from file\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\r\n"); printf(" debug cmd:\r\n"); - printf("\ttx_carrier \t - Transmit carrier\r\n"); + printf("\ttx_carrier \t - Transmitting carrier\r\n"); printf("\trx_carrier \t - Receive carrier\r\n"); printf( "\tencrypt_keeloq \t - Encrypt keeloq manufacture keys\r\n"); @@ -915,6 +1168,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "tx_from_file") == 0) { + subghz_cli_command_tx_from_file(cli, args, context); + break; + } + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_string_cmp_str(cmd, "encrypt_keeloq") == 0) { subghz_cli_command_encrypt_keeloq(cli, args); diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index 85d915acd..b71d78ff0 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -78,6 +78,20 @@ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SNOP); } +bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us) { + bool result = false; + CC1101Status status = {0}; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_us); + while(!furi_hal_cortex_timer_is_expired(timer)) { + status = cc1101_strobe(handle, CC1101_STROBE_SNOP); + if(status.STATE == state) { + result = true; + break; + } + } + return result; +} + CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SPWD); } diff --git a/lib/drivers/cc1101.h b/lib/drivers/cc1101.h index d8ee05d52..c8c552bec 100644 --- a/lib/drivers/cc1101.h +++ b/lib/drivers/cc1101.h @@ -59,6 +59,16 @@ CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle); */ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle); +/** Wait specific chip state + * + * @param handle The SPI bus handle + * @param[in] state The state to wait + * @param[in] timeout_us The timeout in microseconds + * + * @return true on success, false otherwise + */ +bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us); + /** Enable shutdown mode * * @param handle - pointer to FuriHalSpiHandle diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index c508dac72..889ddc56c 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -10,7 +11,7 @@ #define TAG "FuriHalInterrupt" -#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY 5 +#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) typedef struct { FuriHalInterruptISR isr; diff --git a/targets/f7/furi_hal/furi_hal_subghz.c b/targets/f7/furi_hal/furi_hal_subghz.c index 43164a096..e73a325aa 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.c +++ b/targets/f7/furi_hal/furi_hal_subghz.c @@ -36,7 +36,6 @@ typedef enum { SubGhzStateAsyncRx, /**< Async RX started */ SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ } SubGhzState; @@ -277,12 +276,16 @@ void furi_hal_subghz_reset() { void furi_hal_subghz_idle() { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to IDLE mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } void furi_hal_subghz_rx() { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to Rx mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateRX, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } @@ -290,6 +293,8 @@ bool furi_hal_subghz_tx() { if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_tx(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to Tx mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateTX, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); return true; } @@ -352,10 +357,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) { uint32_t real_frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, value); cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); - while(true) { - CC1101Status status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); - if(status.STATE == CC1101StateIDLE) break; - } + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); return real_frequency; @@ -624,7 +626,6 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); } - LL_TIM_EnableIT_UPDATE(TIM2); break; } else { // Lowest possible value is 2us @@ -666,21 +667,6 @@ static void furi_hal_subghz_async_tx_dma_isr() { #endif } -static void furi_hal_subghz_async_tx_timer_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) { - LL_TIM_ClearFlag_UPDATE(TIM2); - if(LL_TIM_GetAutoReload(TIM2) == 0) { - if(furi_hal_subghz.state == SubGhzStateAsyncTx) { - furi_hal_subghz.state = SubGhzStateAsyncTxEnd; - LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); - //forcibly pulls the pin to the ground so that there is no carrier - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - LL_TIM_DisableCounter(TIM2); - } - } - } -} - bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { furi_assert(furi_hal_subghz.state == SubGhzStateIdle); furi_assert(callback); @@ -701,7 +687,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* // Connect CC1101_GD0 to TIM2 as output furi_hal_gpio_init_ex( - &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2); + &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); // Configure DMA LL_DMA_InitTypeDef dma_config = {0}; @@ -715,7 +701,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; dma_config.NbData = FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; + dma_config.Priority = + LL_DMA_PRIORITY_VERYHIGH; // Ensure that ARR is updated before anyone else try to check it LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL); LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF); @@ -743,8 +730,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2); LL_TIM_DisableMasterSlaveMode(TIM2); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL); - furi_hal_subghz_async_tx_middleware_idle(&furi_hal_subghz_async_tx.middleware); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); @@ -752,15 +737,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_EnableDMAReq_UPDATE(TIM2); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); - // Start counter -#ifdef FURI_HAL_SUBGHZ_TX_GPIO - furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); -#endif - furi_hal_subghz_tx(); - - LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableCounter(TIM2); - // Start debug if(furi_hal_subghz_start_debug()) { const GpioPin* gpio = furi_hal_subghz.async_mirror_pin; @@ -777,30 +753,36 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; dma_config.NbData = 2; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + dma_config.Priority = LL_DMA_PRIORITY_HIGH; // Ensure that it's updated after ARR LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config); LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2); LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF); } + // Start counter +#ifdef FURI_HAL_SUBGHZ_TX_GPIO + furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); +#endif + furi_hal_subghz_tx(); + + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + return true; } bool furi_hal_subghz_is_async_tx_complete() { - return furi_hal_subghz.state == SubGhzStateAsyncTxEnd; + return (furi_hal_subghz.state == SubGhzStateAsyncTx) && (LL_TIM_GetAutoReload(TIM2) == 0); } void furi_hal_subghz_stop_async_tx() { - furi_assert( - furi_hal_subghz.state == SubGhzStateAsyncTx || - furi_hal_subghz.state == SubGhzStateAsyncTxEnd); - - // Deinitialize GPIO - // Keep in mind that cc1101 will try to pull it up in idle. - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); // Shutdown radio furi_hal_subghz_idle(); + + // Deinitialize GPIO + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); #ifdef FURI_HAL_SUBGHZ_TX_GPIO furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, false); #endif