From c461d40f94357f5f25e7ae5cc7229a015bdb10a0 Mon Sep 17 00:00:00 2001 From: ESurge Date: Fri, 4 Nov 2022 15:23:54 -0700 Subject: [PATCH] Fixed Infrared FAP when fixing Infrared CLI --- applications/main/infrared/application.fam | 8 - .../main/infrared/infrared_brute_force.c | 158 +++++++++ .../main/infrared/infrared_brute_force.h | 23 ++ applications/main/infrared/infrared_i.h | 2 +- .../main/infrared/infrared_remote_button.h | 2 +- applications/main/infrared/infrared_signal.c | 300 ++++++++++++++++++ applications/main/infrared/infrared_signal.h | 45 +++ .../{infraredsrv_cli.c => infrared_cli.c} | 0 8 files changed, 528 insertions(+), 10 deletions(-) create mode 100644 applications/main/infrared/infrared_brute_force.c create mode 100644 applications/main/infrared/infrared_brute_force.h create mode 100644 applications/main/infrared/infrared_signal.c create mode 100644 applications/main/infrared/infrared_signal.h rename applications/services/infraredsrv/{infraredsrv_cli.c => infrared_cli.c} (100%) diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index dd2bec757..1270c5846 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -16,11 +16,3 @@ App( fap_icon="ir_10px.png", fap_icon_assets="images", ) - -App( - appid="infrared_start", - apptype=FlipperAppType.STARTUP, - entry_point="infrared_on_system_start", - requires=["infrared"], - order=20, -) diff --git a/applications/main/infrared/infrared_brute_force.c b/applications/main/infrared/infrared_brute_force.c new file mode 100644 index 000000000..31bcabd1d --- /dev/null +++ b/applications/main/infrared/infrared_brute_force.c @@ -0,0 +1,158 @@ +#include "infrared_brute_force.h" + +#include +#include +#include + +#include "infrared_signal.h" + +typedef struct { + uint32_t index; + uint32_t count; +} InfraredBruteForceRecord; + +DICT_DEF2( + InfraredBruteForceRecordDict, + FuriString*, + FURI_STRING_OPLIST, + InfraredBruteForceRecord, + M_POD_OPLIST); + +struct InfraredBruteForce { + FlipperFormat* ff; + const char* db_filename; + FuriString* current_record_name; + InfraredSignal* current_signal; + InfraredBruteForceRecordDict_t records; + bool is_started; +}; + +InfraredBruteForce* infrared_brute_force_alloc() { + InfraredBruteForce* brute_force = malloc(sizeof(InfraredBruteForce)); + brute_force->ff = NULL; + brute_force->db_filename = NULL; + brute_force->current_signal = NULL; + brute_force->is_started = false; + brute_force->current_record_name = furi_string_alloc(); + InfraredBruteForceRecordDict_init(brute_force->records); + return brute_force; +} + +void infrared_brute_force_clear_records(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + InfraredBruteForceRecordDict_reset(brute_force->records); +} + +void infrared_brute_force_free(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + InfraredBruteForceRecordDict_clear(brute_force->records); + furi_string_free(brute_force->current_record_name); + free(brute_force); +} + +void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename) { + furi_assert(!brute_force->is_started); + brute_force->db_filename = db_filename; +} + +bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + furi_assert(brute_force->db_filename); + bool success = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + success = flipper_format_buffered_file_open_existing(ff, brute_force->db_filename); + if(success) { + FuriString* signal_name; + signal_name = furi_string_alloc(); + while(flipper_format_read_string(ff, "name", signal_name)) { + InfraredBruteForceRecord* record = + InfraredBruteForceRecordDict_get(brute_force->records, signal_name); + if(record) { + ++(record->count); + } + } + furi_string_free(signal_name); + } + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + return success; +} + +bool infrared_brute_force_start( + InfraredBruteForce* brute_force, + uint32_t index, + uint32_t* record_count) { + furi_assert(!brute_force->is_started); + bool success = false; + *record_count = 0; + + InfraredBruteForceRecordDict_it_t it; + for(InfraredBruteForceRecordDict_it(it, brute_force->records); + !InfraredBruteForceRecordDict_end_p(it); + InfraredBruteForceRecordDict_next(it)) { + const InfraredBruteForceRecordDict_itref_t* record = InfraredBruteForceRecordDict_cref(it); + if(record->value.index == index) { + *record_count = record->value.count; + if(*record_count) { + furi_string_set(brute_force->current_record_name, record->key); + } + break; + } + } + + if(*record_count) { + Storage* storage = furi_record_open(RECORD_STORAGE); + brute_force->ff = flipper_format_buffered_file_alloc(storage); + brute_force->current_signal = infrared_signal_alloc(); + brute_force->is_started = true; + success = + flipper_format_buffered_file_open_existing(brute_force->ff, brute_force->db_filename); + if(!success) infrared_brute_force_stop(brute_force); + } + return success; +} + +bool infrared_brute_force_is_started(InfraredBruteForce* brute_force) { + return brute_force->is_started; +} + +void infrared_brute_force_stop(InfraredBruteForce* brute_force) { + furi_assert(brute_force->is_started); + furi_string_reset(brute_force->current_record_name); + infrared_signal_free(brute_force->current_signal); + flipper_format_free(brute_force->ff); + brute_force->current_signal = NULL; + brute_force->ff = NULL; + brute_force->is_started = false; + furi_record_close(RECORD_STORAGE); +} + +bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) { + furi_assert(brute_force->is_started); + const bool success = infrared_signal_search_and_read( + brute_force->current_signal, brute_force->ff, brute_force->current_record_name); + if(success) { + infrared_signal_transmit(brute_force->current_signal); + } + return success; +} + +void infrared_brute_force_add_record( + InfraredBruteForce* brute_force, + uint32_t index, + const char* name) { + InfraredBruteForceRecord value = {.index = index, .count = 0}; + FuriString* key; + key = furi_string_alloc_set(name); + InfraredBruteForceRecordDict_set_at(brute_force->records, key, value); + furi_string_free(key); +} + +void infrared_brute_force_reset(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + InfraredBruteForceRecordDict_reset(brute_force->records); +} diff --git a/applications/main/infrared/infrared_brute_force.h b/applications/main/infrared/infrared_brute_force.h new file mode 100644 index 000000000..fff472e79 --- /dev/null +++ b/applications/main/infrared/infrared_brute_force.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +typedef struct InfraredBruteForce InfraredBruteForce; + +InfraredBruteForce* infrared_brute_force_alloc(); +void infrared_brute_force_free(InfraredBruteForce* brute_force); +void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename); +bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); +bool infrared_brute_force_start( + InfraredBruteForce* brute_force, + uint32_t index, + uint32_t* record_count); +bool infrared_brute_force_is_started(InfraredBruteForce* brute_force); +void infrared_brute_force_stop(InfraredBruteForce* brute_force); +bool infrared_brute_force_send_next(InfraredBruteForce* brute_force); +void infrared_brute_force_clear_records(InfraredBruteForce* brute_force); +void infrared_brute_force_add_record( + InfraredBruteForce* brute_force, + uint32_t index, + const char* name); diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h index 075721475..4bf0631f5 100644 --- a/applications/main/infrared/infrared_i.h +++ b/applications/main/infrared/infrared_i.h @@ -23,7 +23,7 @@ #include "infrared.h" #include "infrared_remote.h" -#include "../../services/infraredsrv/infrared_brute_force.h" +#include "infrared_brute_force.h" #include "infrared_custom_event.h" #include "scenes/infrared_scene.h" diff --git a/applications/main/infrared/infrared_remote_button.h b/applications/main/infrared/infrared_remote_button.h index 918b6085f..f25b759b5 100644 --- a/applications/main/infrared/infrared_remote_button.h +++ b/applications/main/infrared/infrared_remote_button.h @@ -1,6 +1,6 @@ #pragma once -#include "../../services/infraredsrv/infrared_signal.h" +#include "infrared_signal.h" typedef struct InfraredRemoteButton InfraredRemoteButton; diff --git a/applications/main/infrared/infrared_signal.c b/applications/main/infrared/infrared_signal.c new file mode 100644 index 000000000..d399b9587 --- /dev/null +++ b/applications/main/infrared/infrared_signal.c @@ -0,0 +1,300 @@ +#include "infrared_signal.h" + +#include +#include +#include +#include +#include + +#define TAG "InfraredSignal" + +struct InfraredSignal { + bool is_raw; + union { + InfraredMessage message; + InfraredRawSignal raw; + } payload; +}; + +static void infrared_signal_clear_timings(InfraredSignal* signal) { + if(signal->is_raw) { + free(signal->payload.raw.timings); + signal->payload.raw.timings_size = 0; + signal->payload.raw.timings = NULL; + } +} + +static bool infrared_signal_is_message_valid(InfraredMessage* message) { + if(!infrared_is_protocol_valid(message->protocol)) { + FURI_LOG_E(TAG, "Unknown protocol"); + return false; + } + + uint32_t address_length = infrared_get_protocol_address_length(message->protocol); + uint32_t address_mask = (1UL << address_length) - 1; + + if(message->address != (message->address & address_mask)) { + FURI_LOG_E( + TAG, + "Address is out of range (mask 0x%08lX): 0x%lX\r\n", + address_mask, + message->address); + return false; + } + + uint32_t command_length = infrared_get_protocol_command_length(message->protocol); + uint32_t command_mask = (1UL << command_length) - 1; + + if(message->command != (message->command & command_mask)) { + FURI_LOG_E( + TAG, + "Command is out of range (mask 0x%08lX): 0x%lX\r\n", + command_mask, + message->command); + return false; + } + + return true; +} + +static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) { + if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) { + FURI_LOG_E( + TAG, + "Frequency is out of range (%X - %X): %lX", + INFRARED_MIN_FREQUENCY, + INFRARED_MAX_FREQUENCY, + raw->frequency); + return false; + + } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) { + FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle); + return false; + + } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) { + FURI_LOG_E( + TAG, + "Timings amount is out of range (0 - %X): %X", + MAX_TIMINGS_AMOUNT, + raw->timings_size); + return false; + } + + return true; +} + +static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) { + const char* protocol_name = infrared_get_protocol_name(message->protocol); + return flipper_format_write_string_cstr(ff, "type", "parsed") && + flipper_format_write_string_cstr(ff, "protocol", protocol_name) && + flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) && + flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4); +} + +static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) { + furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); + return flipper_format_write_string_cstr(ff, "type", "raw") && + flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) && + flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) && + flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size); +} + +static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { + FuriString* buf; + buf = furi_string_alloc(); + bool success = false; + + do { + if(!flipper_format_read_string(ff, "protocol", buf)) break; + + InfraredMessage message; + message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf)); + + success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) && + flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) && + infrared_signal_is_message_valid(&message); + + if(!success) break; + + infrared_signal_set_message(signal, &message); + } while(0); + + furi_string_free(buf); + return success; +} + +static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { + uint32_t timings_size, frequency; + float duty_cycle; + + bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) && + flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) && + flipper_format_get_value_count(ff, "data", &timings_size); + + if(!success || timings_size > MAX_TIMINGS_AMOUNT) { + return false; + } + + uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); + success = flipper_format_read_uint32(ff, "data", timings, timings_size); + + if(success) { + infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); + } + + free(timings); + return success; +} + +static bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) { + FuriString* tmp = furi_string_alloc(); + + bool success = false; + + do { + if(!flipper_format_read_string(ff, "type", tmp)) break; + if(furi_string_equal(tmp, "raw")) { + success = infrared_signal_read_raw(signal, ff); + } else if(furi_string_equal(tmp, "parsed")) { + success = infrared_signal_read_message(signal, ff); + } else { + FURI_LOG_E(TAG, "Unknown signal type"); + } + } while(false); + + furi_string_free(tmp); + return success; +} + +InfraredSignal* infrared_signal_alloc() { + InfraredSignal* signal = malloc(sizeof(InfraredSignal)); + + signal->is_raw = false; + signal->payload.message.protocol = InfraredProtocolUnknown; + + return signal; +} + +void infrared_signal_free(InfraredSignal* signal) { + infrared_signal_clear_timings(signal); + free(signal); +} + +bool infrared_signal_is_raw(InfraredSignal* signal) { + return signal->is_raw; +} + +bool infrared_signal_is_valid(InfraredSignal* signal) { + return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) : + infrared_signal_is_message_valid(&signal->payload.message); +} + +void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) { + if(other->is_raw) { + const InfraredRawSignal* raw = &other->payload.raw; + infrared_signal_set_raw_signal( + signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle); + } else { + const InfraredMessage* message = &other->payload.message; + infrared_signal_set_message(signal, message); + } +} + +void infrared_signal_set_raw_signal( + InfraredSignal* signal, + const uint32_t* timings, + size_t timings_size, + uint32_t frequency, + float duty_cycle) { + infrared_signal_clear_timings(signal); + + signal->is_raw = true; + + signal->payload.raw.timings_size = timings_size; + signal->payload.raw.frequency = frequency; + signal->payload.raw.duty_cycle = duty_cycle; + + signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t)); + memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t)); +} + +InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) { + furi_assert(signal->is_raw); + return &signal->payload.raw; +} + +void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) { + infrared_signal_clear_timings(signal); + + signal->is_raw = false; + signal->payload.message = *message; +} + +InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) { + furi_assert(!signal->is_raw); + return &signal->payload.message; +} + +bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) { + if(!flipper_format_write_comment_cstr(ff, "") || + !flipper_format_write_string_cstr(ff, "name", name)) { + return false; + } else if(signal->is_raw) { + return infrared_signal_save_raw(&signal->payload.raw, ff); + } else { + return infrared_signal_save_message(&signal->payload.message, ff); + } +} + +bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) { + FuriString* tmp = furi_string_alloc(); + + bool success = false; + + do { + if(!flipper_format_read_string(ff, "name", tmp)) break; + furi_string_set(name, tmp); + if(!infrared_signal_read_body(signal, ff)) break; + success = true; + } while(0); + + furi_string_free(tmp); + return success; +} + +bool infrared_signal_search_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + const FuriString* name) { + bool success = false; + FuriString* tmp = furi_string_alloc(); + + do { + bool is_name_found = false; + while(flipper_format_read_string(ff, "name", tmp)) { + is_name_found = furi_string_equal(name, tmp); + if(is_name_found) break; + } + if(!is_name_found) break; + if(!infrared_signal_read_body(signal, ff)) break; + success = true; + } while(false); + + furi_string_free(tmp); + return success; +} + +void infrared_signal_transmit(InfraredSignal* signal) { + if(signal->is_raw) { + InfraredRawSignal* raw_signal = &signal->payload.raw; + infrared_send_raw_ext( + raw_signal->timings, + raw_signal->timings_size, + true, + raw_signal->frequency, + raw_signal->duty_cycle); + } else { + InfraredMessage* message = &signal->payload.message; + infrared_send(message, 1); + } +} diff --git a/applications/main/infrared/infrared_signal.h b/applications/main/infrared/infrared_signal.h new file mode 100644 index 000000000..29c661938 --- /dev/null +++ b/applications/main/infrared/infrared_signal.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include +#include + +typedef struct InfraredSignal InfraredSignal; + +typedef struct { + size_t timings_size; + uint32_t* timings; + uint32_t frequency; + float duty_cycle; +} InfraredRawSignal; + +InfraredSignal* infrared_signal_alloc(); +void infrared_signal_free(InfraredSignal* signal); + +bool infrared_signal_is_raw(InfraredSignal* signal); +bool infrared_signal_is_valid(InfraredSignal* signal); + +void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other); + +void infrared_signal_set_raw_signal( + InfraredSignal* signal, + const uint32_t* timings, + size_t timings_size, + uint32_t frequency, + float duty_cycle); +InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal); + +void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message); +InfraredMessage* infrared_signal_get_message(InfraredSignal* signal); + +bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name); +bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name); +bool infrared_signal_search_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + const FuriString* name); + +void infrared_signal_transmit(InfraredSignal* signal); diff --git a/applications/services/infraredsrv/infraredsrv_cli.c b/applications/services/infraredsrv/infrared_cli.c similarity index 100% rename from applications/services/infraredsrv/infraredsrv_cli.c rename to applications/services/infraredsrv/infrared_cli.c