From 55f770328c38240a7a2bfa66676e84ac136a58ef Mon Sep 17 00:00:00 2001 From: David Date: Wed, 11 Mar 2026 17:58:20 +0100 Subject: [PATCH 1/6] Update --- lib/subghz/protocols/kia_v3_v4.c | 684 +++++++++++++------------------ 1 file changed, 292 insertions(+), 392 deletions(-) diff --git a/lib/subghz/protocols/kia_v3_v4.c b/lib/subghz/protocols/kia_v3_v4.c index 7b8b869e..36d90274 100644 --- a/lib/subghz/protocols/kia_v3_v4.c +++ b/lib/subghz/protocols/kia_v3_v4.c @@ -1,38 +1,31 @@ #include "kia_v3_v4.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" #include "../blocks/custom_btn_i.h" -static uint8_t kia_v3v4_get_btn_code() { - uint8_t custom_btn = subghz_custom_btn_get(); - uint8_t original_btn = subghz_custom_btn_get_original(); - if(custom_btn == SUBGHZ_CUSTOM_BTN_OK) return original_btn; - if(custom_btn == SUBGHZ_CUSTOM_BTN_UP) return 0x01; // Lock - if(custom_btn == SUBGHZ_CUSTOM_BTN_DOWN) return 0x02; // Unlock - if(custom_btn == SUBGHZ_CUSTOM_BTN_LEFT) return 0x03; // Boot/Trunk - if(custom_btn == SUBGHZ_CUSTOM_BTN_RIGHT) return 0x03; // Boot/Trunk - return original_btn; -} +#define TAG "SubGhzProtocolKiaV3V4" - -#include "keys.h" - -#define TAG "KiaV3V4" - - -static const char* kia_version_names[] = {"Kia V4", "Kia V3"}; +#define KIA_MF_KEY 0xA8F5DFFC8DAA5CDBULL #define KIA_V3_V4_PREAMBLE_PAIRS 16 #define KIA_V3_V4_TOTAL_BURSTS 3 #define KIA_V3_V4_INTER_BURST_GAP_US 10000 #define KIA_V3_V4_SYNC_DURATION 1200 -static const SubGhzBlockConst kia_protocol_v3_v4_const = { +static const char* kia_version_names[] = {"KIA/HYU V4", "KIA/HYU V3"}; + +static const SubGhzBlockConst subghz_protocol_kia_v3_v4_const = { .te_short = 400, .te_long = 800, .te_delta = 150, .min_count_bit_for_found = 68, }; -typedef struct SubGhzProtocolDecoderKiaV3V4 { +struct SubGhzProtocolDecoderKiaV3V4 { SubGhzProtocolDecoderBase base; SubGhzBlockDecoder decoder; SubGhzBlockGeneric generic; @@ -46,9 +39,9 @@ typedef struct SubGhzProtocolDecoderKiaV3V4 { uint32_t decrypted; uint8_t crc; uint8_t version; -} SubGhzProtocolDecoderKiaV3V4; +}; -typedef struct SubGhzProtocolEncoderKiaV3V4 { +struct SubGhzProtocolEncoderKiaV3V4 { SubGhzProtocolEncoderBase base; SubGhzProtocolBlockEncoder encoder; SubGhzBlockGeneric generic; @@ -57,10 +50,11 @@ typedef struct SubGhzProtocolEncoderKiaV3V4 { uint8_t btn; uint16_t cnt; uint8_t version; + uint8_t crc; uint32_t encrypted; uint32_t decrypted; -} SubGhzProtocolEncoderKiaV3V4; +}; typedef enum { KiaV3V4DecoderStepReset = 0, @@ -68,7 +62,6 @@ typedef enum { KiaV3V4DecoderStepCollectRawBits, } KiaV3V4DecoderStep; -// KeeLoq decrypt static uint32_t keeloq_common_decrypt(uint32_t data, uint64_t key) { uint32_t block = data; uint64_t tkey = key; @@ -84,7 +77,6 @@ static uint32_t keeloq_common_decrypt(uint32_t data, uint64_t key) { return block; } -// KeeLoq encrypt static uint32_t keeloq_common_encrypt(uint32_t data, uint64_t key) { uint32_t block = data; uint64_t tkey = key; @@ -154,7 +146,7 @@ static bool kia_v3_v4_process_buffer(SubGhzProtocolDecoderKiaV3V4* instance) { uint8_t btn = (reverse8(b[7]) & 0xF0) >> 4; uint8_t our_serial_lsb = serial & 0xFF; - uint32_t decrypted = keeloq_common_decrypt(encrypted, get_kia_mf_key()); + uint32_t decrypted = keeloq_common_decrypt(encrypted, KIA_MF_KEY); uint8_t dec_btn = (decrypted >> 28) & 0x0F; uint8_t dec_serial_lsb = (decrypted >> 16) & 0xFF; @@ -175,44 +167,61 @@ static bool kia_v3_v4_process_buffer(SubGhzProtocolDecoderKiaV3V4* instance) { ((uint64_t)b[6] << 8) | (uint64_t)b[7]; instance->generic.data = key_data; instance->generic.data_count_bit = 68; + + instance->decoder.decode_data = key_data; + instance->decoder.decode_count_bit = 68; + + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->generic.btn); + } + subghz_custom_btn_set_max(5); return true; } -const SubGhzProtocolDecoder kia_protocol_v3_v4_decoder = { - .alloc = kia_protocol_decoder_v3_v4_alloc, - .free = kia_protocol_decoder_v3_v4_free, - .feed = kia_protocol_decoder_v3_v4_feed, - .reset = kia_protocol_decoder_v3_v4_reset, - .get_hash_data = kia_protocol_decoder_v3_v4_get_hash_data, - .serialize = kia_protocol_decoder_v3_v4_serialize, - .deserialize = kia_protocol_decoder_v3_v4_deserialize, - .get_string = kia_protocol_decoder_v3_v4_get_string, +const SubGhzProtocolDecoder subghz_protocol_kia_v3_v4_decoder = { + .alloc = subghz_protocol_decoder_kia_v3_v4_alloc, + .free = subghz_protocol_decoder_kia_v3_v4_free, + .feed = subghz_protocol_decoder_kia_v3_v4_feed, + .reset = subghz_protocol_decoder_kia_v3_v4_reset, + .get_hash_data = subghz_protocol_decoder_kia_v3_v4_get_hash_data, + .get_hash_data_long = NULL, + .serialize = subghz_protocol_decoder_kia_v3_v4_serialize, + .deserialize = subghz_protocol_decoder_kia_v3_v4_deserialize, + .get_string = subghz_protocol_decoder_kia_v3_v4_get_string, + .get_string_brief = NULL, }; -const SubGhzProtocolEncoder kia_protocol_v3_v4_encoder = { - .alloc = kia_protocol_encoder_v3_v4_alloc, - .free = kia_protocol_encoder_v3_v4_free, - .deserialize = kia_protocol_encoder_v3_v4_deserialize, - .stop = kia_protocol_encoder_v3_v4_stop, - .yield = kia_protocol_encoder_v3_v4_yield, +const SubGhzProtocolEncoder subghz_protocol_kia_v3_v4_encoder = { + .alloc = subghz_protocol_encoder_kia_v3_v4_alloc, + .free = subghz_protocol_encoder_kia_v3_v4_free, + .deserialize = subghz_protocol_encoder_kia_v3_v4_deserialize, + .stop = subghz_protocol_encoder_kia_v3_v4_stop, + .yield = subghz_protocol_encoder_kia_v3_v4_yield, }; const SubGhzProtocol subghz_protocol_kia_v3_v4 = { - .name = KIA_PROTOCOL_V3_V4_NAME, + .name = SUBGHZ_PROTOCOL_KIA_V3_V4_NAME, .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - .decoder = &kia_protocol_v3_v4_decoder, - .encoder = &kia_protocol_v3_v4_encoder, + .decoder = &subghz_protocol_kia_v3_v4_decoder, + .encoder = &subghz_protocol_kia_v3_v4_encoder, }; -// ============================================================================ -// ENCODER IMPLEMENTATION -// ============================================================================ +static const char* subghz_protocol_kia_v3_v4_get_name_button(uint8_t btn) { + switch(btn) { + case 0x1: return "Lock"; + case 0x2: return "Unlock"; + case 0x3: return "Trunk"; + case 0x4: return "Panic"; + case 0x8: return "Horn"; + default: return "Unknown"; + } +} -void* kia_protocol_encoder_v3_v4_alloc(SubGhzEnvironment* environment) { +void* subghz_protocol_encoder_kia_v3_v4_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolEncoderKiaV3V4* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV3V4)); @@ -226,16 +235,15 @@ void* kia_protocol_encoder_v3_v4_alloc(SubGhzEnvironment* environment) { instance->encoder.size_upload = 600; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.repeat = 40; + instance->encoder.repeat = 10; instance->encoder.front = 0; instance->encoder.is_running = false; - FURI_LOG_I(TAG, "Encoder allocated at %p", instance); return instance; } -void kia_protocol_encoder_v3_v4_free(void* context) { - furi_check(context); +void subghz_protocol_encoder_kia_v3_v4_free(void* context) { + furi_assert(context); SubGhzProtocolEncoderKiaV3V4* instance = context; if(instance->encoder.upload) { free(instance->encoder.upload); @@ -243,32 +251,22 @@ void kia_protocol_encoder_v3_v4_free(void* context) { free(instance); } -static void kia_protocol_encoder_v3_v4_build_packet( +static void subghz_protocol_encoder_kia_v3_v4_build_packet( SubGhzProtocolEncoderKiaV3V4* instance, uint8_t* raw_bytes) { - // Build plaintext for encryption: uint32_t plaintext = (instance->cnt & 0xFFFF) | ((instance->serial & 0xFF) << 16) | (0x1 << 24) | ((instance->btn & 0x0F) << 28); instance->decrypted = plaintext; - uint32_t encrypted = keeloq_common_encrypt(plaintext, get_kia_mf_key()); + uint32_t encrypted = keeloq_common_encrypt(plaintext, KIA_MF_KEY); instance->encrypted = encrypted; - FURI_LOG_I( - TAG, - "Encrypt: plain=0x%08lX -> enc=0x%08lX", - (unsigned long)plaintext, - (unsigned long)encrypted); - - // Decoder does: encrypted = (rev(b[3])<<24) | (rev(b[2])<<16) | (rev(b[1])<<8) | rev(b[0]) - // So b[0] must contain reverse8(LSB), b[3] must contain reverse8(MSB) - raw_bytes[0] = reverse8((encrypted >> 0) & 0xFF); // LSB + raw_bytes[0] = reverse8((encrypted >> 0) & 0xFF); raw_bytes[1] = reverse8((encrypted >> 8) & 0xFF); raw_bytes[2] = reverse8((encrypted >> 16) & 0xFF); - raw_bytes[3] = reverse8((encrypted >> 24) & 0xFF); // MSB + raw_bytes[3] = reverse8((encrypted >> 24) & 0xFF); - // Serial/button uint32_t serial_btn = (instance->serial & 0x0FFFFFFF) | ((uint32_t)(instance->btn & 0x0F) << 28); raw_bytes[4] = reverse8((serial_btn >> 0) & 0xFF); @@ -276,45 +274,22 @@ static void kia_protocol_encoder_v3_v4_build_packet( raw_bytes[6] = reverse8((serial_btn >> 16) & 0xFF); raw_bytes[7] = reverse8((serial_btn >> 24) & 0xFF); - // CRC uint8_t crc = kia_v3_v4_calculate_crc(raw_bytes); raw_bytes[8] = (crc << 4); + instance->crc = crc; - // DEBUG: Log the exact raw bytes we're generating - FURI_LOG_I( - TAG, - "TX raw: %02X %02X %02X %02X %02X %02X %02X %02X %02X", - raw_bytes[0], - raw_bytes[1], - raw_bytes[2], - raw_bytes[3], - raw_bytes[4], - raw_bytes[5], - raw_bytes[6], - raw_bytes[7], - raw_bytes[8]); - - // Store in generic.data for display instance->generic.data = ((uint64_t)raw_bytes[0] << 56) | ((uint64_t)raw_bytes[1] << 48) | ((uint64_t)raw_bytes[2] << 40) | ((uint64_t)raw_bytes[3] << 32) | ((uint64_t)raw_bytes[4] << 24) | ((uint64_t)raw_bytes[5] << 16) | ((uint64_t)raw_bytes[6] << 8) | (uint64_t)raw_bytes[7]; instance->generic.data_count_bit = 68; - - FURI_LOG_I( - TAG, - "Packet built: Serial=0x%07lX, Btn=0x%X, Cnt=0x%04X, CRC=0x%X", - (unsigned long)instance->serial, - instance->btn, - instance->cnt, - crc); } -static void kia_protocol_encoder_v3_v4_get_upload(SubGhzProtocolEncoderKiaV3V4* instance) { - furi_check(instance); +static void subghz_protocol_encoder_kia_v3_v4_get_upload(SubGhzProtocolEncoderKiaV3V4* instance) { + furi_assert(instance); uint8_t raw_bytes[9]; - kia_protocol_encoder_v3_v4_build_packet(instance, raw_bytes); + subghz_protocol_encoder_kia_v3_v4_build_packet(instance, raw_bytes); if(instance->version == 1) { for(int i = 0; i < 9; i++) { @@ -330,29 +305,24 @@ static void kia_protocol_encoder_v3_v4_get_upload(SubGhzProtocolEncoderKiaV3V4* level_duration_make(false, KIA_V3_V4_INTER_BURST_GAP_US); } - // Preamble: alternating short pulses for(int i = 0; i < KIA_V3_V4_PREAMBLE_PAIRS; i++) { instance->encoder.upload[index++] = - level_duration_make(true, kia_protocol_v3_v4_const.te_short); + level_duration_make(true, subghz_protocol_kia_v3_v4_const.te_short); instance->encoder.upload[index++] = - level_duration_make(false, kia_protocol_v3_v4_const.te_short); + level_duration_make(false, subghz_protocol_kia_v3_v4_const.te_short); } - // Sync pulse - different for V3 vs V4 if(instance->version == 0) { - // V4: long HIGH, short LOW instance->encoder.upload[index++] = level_duration_make(true, KIA_V3_V4_SYNC_DURATION); instance->encoder.upload[index++] = - level_duration_make(false, kia_protocol_v3_v4_const.te_short); + level_duration_make(false, subghz_protocol_kia_v3_v4_const.te_short); } else { - // V3: short HIGH, long LOW instance->encoder.upload[index++] = - level_duration_make(true, kia_protocol_v3_v4_const.te_short); + level_duration_make(true, subghz_protocol_kia_v3_v4_const.te_short); instance->encoder.upload[index++] = level_duration_make(false, KIA_V3_V4_SYNC_DURATION); } - // Data bits - PWM encoding with complementary durations for(int byte_idx = 0; byte_idx < 9; byte_idx++) { int bits_in_byte = (byte_idx == 8) ? 4 : 8; @@ -360,43 +330,31 @@ static void kia_protocol_encoder_v3_v4_get_upload(SubGhzProtocolEncoderKiaV3V4* bool bit = (raw_bytes[byte_idx] >> bit_idx) & 1; if(bit) { - // bit 1: long HIGH, short LOW (total ~1200µs) instance->encoder.upload[index++] = - level_duration_make(true, kia_protocol_v3_v4_const.te_long); // 800µs + level_duration_make(true, subghz_protocol_kia_v3_v4_const.te_long); instance->encoder.upload[index++] = - level_duration_make(false, kia_protocol_v3_v4_const.te_short); // 400µs + level_duration_make(false, subghz_protocol_kia_v3_v4_const.te_short); } else { - // bit 0: short HIGH, long LOW (total ~1200µs) instance->encoder.upload[index++] = - level_duration_make(true, kia_protocol_v3_v4_const.te_short); // 400µs + level_duration_make(true, subghz_protocol_kia_v3_v4_const.te_short); instance->encoder.upload[index++] = - level_duration_make(false, kia_protocol_v3_v4_const.te_long); // 800µs + level_duration_make(false, subghz_protocol_kia_v3_v4_const.te_long); } } } } - //instance->encoder.upload[index++] = level_duration_make(false, KIA_V3_V4_INTER_BURST_GAP_US); - instance->encoder.size_upload = index; instance->encoder.front = 0; - - FURI_LOG_I( - TAG, - "Upload built: %d bursts, size_upload=%zu, version=%s", - KIA_V3_V4_TOTAL_BURSTS, - instance->encoder.size_upload, - instance->version == 0 ? "V4" : "V3"); } SubGhzProtocolStatus - kia_protocol_encoder_v3_v4_deserialize(void* context, FlipperFormat* flipper_format) { - furi_check(context); + subghz_protocol_encoder_kia_v3_v4_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); SubGhzProtocolEncoderKiaV3V4* instance = context; instance->encoder.is_running = false; instance->encoder.front = 0; - //instance->encoder.repeat = 40; SubGhzProtocolStatus ret = SubGhzProtocolStatusError; @@ -405,49 +363,39 @@ SubGhzProtocolStatus do { FuriString* temp_str = furi_string_alloc(); if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) { - FURI_LOG_E(TAG, "Missing Protocol"); furi_string_free(temp_str); break; } - // Accept "Kia V3/V4", "Kia V3", or "Kia V4" const char* proto_str = furi_string_get_cstr(temp_str); if(!furi_string_equal(temp_str, instance->base.protocol->name) && - strcmp(proto_str, "Kia V3") != 0 && strcmp(proto_str, "Kia V4") != 0) { - FURI_LOG_E(TAG, "Wrong protocol %s", proto_str); + strcmp(proto_str, "KIA/HYU V3") != 0 && strcmp(proto_str, "KIA/HYU V4") != 0) { furi_string_free(temp_str); break; } - // Set version based on protocol name if specific bool version_from_protocol_name = false; - if(strcmp(proto_str, "Kia V3") == 0) { + if(strcmp(proto_str, "KIA/HYU V3") == 0) { instance->version = 1; version_from_protocol_name = true; - FURI_LOG_I(TAG, "Protocol name indicates V3"); - } else if(strcmp(proto_str, "Kia V4") == 0) { + } else if(strcmp(proto_str, "KIA/HYU V4") == 0) { instance->version = 0; version_from_protocol_name = true; - FURI_LOG_I(TAG, "Protocol name indicates V4"); } furi_string_free(temp_str); - // Read bit count flipper_format_rewind(flipper_format); uint32_t bit_count_temp; if(!flipper_format_read_uint32(flipper_format, "Bit", &bit_count_temp, 1)) { - FURI_LOG_E(TAG, "Missing Bit"); break; } instance->generic.data_count_bit = 68; - // Read key data flipper_format_rewind(flipper_format); temp_str = furi_string_alloc(); if(!flipper_format_read_string(flipper_format, "Key", temp_str)) { - FURI_LOG_E(TAG, "Missing Key"); furi_string_free(temp_str); break; } @@ -469,7 +417,6 @@ SubGhzProtocolStatus } else if(c >= 'a' && c <= 'f') { nibble = c - 'a' + 10; } else { - FURI_LOG_E(TAG, "Invalid hex character: %c", c); break; } @@ -480,9 +427,7 @@ SubGhzProtocolStatus furi_string_free(temp_str); instance->generic.data = key; - FURI_LOG_I(TAG, "Parsed key: 0x%016llX", (unsigned long long)instance->generic.data); - // Read serial flipper_format_rewind(flipper_format); if(!flipper_format_read_uint32(flipper_format, "Serial", &instance->serial, 1)) { uint8_t b[8]; @@ -498,129 +443,154 @@ SubGhzProtocolStatus instance->serial = ((uint32_t)reverse8(b[7] & 0xF0) << 24) | ((uint32_t)reverse8(b[6]) << 16) | ((uint32_t)reverse8(b[5]) << 8) | (uint32_t)reverse8(b[4]); - FURI_LOG_I(TAG, "Extracted serial: 0x%08lX", (unsigned long)instance->serial); } else { - FURI_LOG_I(TAG, "Read serial: 0x%08lX", (unsigned long)instance->serial); } instance->generic.serial = instance->serial; - // Read button flipper_format_rewind(flipper_format); uint32_t btn_temp; if(flipper_format_read_uint32(flipper_format, "Btn", &btn_temp, 1)) { instance->btn = (uint8_t)btn_temp; - FURI_LOG_I(TAG, "Read button: 0x%X", instance->btn); } else { uint8_t b7 = instance->generic.data & 0xFF; instance->btn = (reverse8(b7) & 0xF0) >> 4; - FURI_LOG_I(TAG, "Extracted button: 0x%X", instance->btn); } - instance->generic.btn = instance->btn; - if(subghz_custom_btn_get_original() == 0) - subghz_custom_btn_set_original(instance->generic.btn); - subghz_custom_btn_set_max(4); - instance->generic.btn = kia_v3v4_get_btn_code(); - instance->btn = instance->generic.btn; - // Read counter flipper_format_rewind(flipper_format); uint32_t cnt_temp; if(flipper_format_read_uint32(flipper_format, "Cnt", &cnt_temp, 1)) { instance->cnt = (uint16_t)cnt_temp; - FURI_LOG_I(TAG, "Read counter: 0x%04X", instance->cnt); } else { - // Try Decrypted field flipper_format_rewind(flipper_format); uint32_t decrypted_temp; if(flipper_format_read_uint32(flipper_format, "Decrypted", &decrypted_temp, 1)) { instance->cnt = decrypted_temp & 0xFFFF; - FURI_LOG_I(TAG, "Extracted counter from Decrypted: 0x%04X", instance->cnt); } else { instance->cnt = 0; - FURI_LOG_W(TAG, "Counter not found, using 0"); } } - instance->generic.cnt = instance->cnt; - // Read version - ONLY use file version if protocol name didn't specify one flipper_format_rewind(flipper_format); uint32_t version_temp; - if(flipper_format_read_uint32(flipper_format, "KIAVersion", &version_temp, 1)) { + if(flipper_format_read_uint32(flipper_format, "Version", &version_temp, 1)) { if(!version_from_protocol_name) { - // Generic "Kia V3/V4" protocol - use file's version instance->version = (uint8_t)version_temp; - FURI_LOG_I( - TAG, "Read version from file: %s", instance->version == 0 ? "V4" : "V3"); - } else { - // Protocol name was specific - trust that, ignore file version - FURI_LOG_I( - TAG, - "Using version from protocol name: %s (file had Version=%lu)", - instance->version == 0 ? "V4" : "V3", - (unsigned long)version_temp); } } else if(!version_from_protocol_name) { instance->version = 0; - FURI_LOG_I(TAG, "Version not found, defaulting to V4"); } - // Read repeat flipper_format_rewind(flipper_format); if(!flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1)) { - instance->encoder.repeat = 40; - FURI_LOG_D(TAG, "Repeat not found, using default 40"); + instance->encoder.repeat = 10; } - // Build the upload - kia_protocol_encoder_v3_v4_get_upload(instance); + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->btn); + } + subghz_custom_btn_set_max(5); + + uint8_t selected_btn; + if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK) { + selected_btn = subghz_custom_btn_get_original(); + } else { + selected_btn = subghz_custom_btn_get(); + } + + if(selected_btn == 5) { + instance->btn = 0x8; + } else if(selected_btn >= 1 && selected_btn <= 4) { + instance->btn = selected_btn; + } + + uint32_t mult = furi_hal_subghz_get_rolling_counter_mult(); + instance->cnt = (instance->cnt + mult) & 0xFFFF; + + instance->generic.btn = instance->btn; + instance->generic.cnt = instance->cnt; + + subghz_protocol_encoder_kia_v3_v4_get_upload(instance); + + if(!flipper_format_rewind(flipper_format)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + } + + if(!flipper_format_rewind(flipper_format)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + uint32_t cnt_to_write = instance->cnt; + if(!flipper_format_update_uint32(flipper_format, "Cnt", &cnt_to_write, 1)) { + } + + if(!flipper_format_rewind(flipper_format)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + uint32_t btn_to_write = instance->btn; + if(!flipper_format_update_uint32(flipper_format, "Btn", &btn_to_write, 1)) { + } + + if(!flipper_format_rewind(flipper_format)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + uint32_t decrypted_to_write = instance->decrypted; + if(!flipper_format_update_uint32(flipper_format, "Decrypted", &decrypted_to_write, 1)) { + } + + if(!flipper_format_rewind(flipper_format)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + uint32_t encrypted_to_write = instance->encrypted; + if(!flipper_format_update_uint32(flipper_format, "Encrypted", &encrypted_to_write, 1)) { + } + + if(!flipper_format_rewind(flipper_format)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + uint32_t crc_to_write = instance->crc; + if(!flipper_format_update_uint32(flipper_format, "CRC", &crc_to_write, 1)) { + } instance->encoder.is_running = true; instance->encoder.front = 0; - FURI_LOG_I( - TAG, - "Encoder initialized: Serial=0x%07lX, Btn=0x%X, Cnt=0x%04X, Version=%s", - (unsigned long)instance->serial, - instance->btn, - instance->cnt, - instance->version == 0 ? "V4" : "V3"); - ret = SubGhzProtocolStatusOk; } while(false); return ret; } -void kia_protocol_encoder_v3_v4_stop(void* context) { +void subghz_protocol_encoder_kia_v3_v4_stop(void* context) { if(!context) return; SubGhzProtocolEncoderKiaV3V4* instance = context; instance->encoder.is_running = false; instance->encoder.front = 0; } -LevelDuration kia_protocol_encoder_v3_v4_yield(void* context) { +LevelDuration subghz_protocol_encoder_kia_v3_v4_yield(void* context) { SubGhzProtocolEncoderKiaV3V4* instance = context; if(!instance || !instance->encoder.upload || instance->encoder.repeat == 0 || !instance->encoder.is_running) { if(instance) { - FURI_LOG_D( - TAG, - "Encoder yield stopped: repeat=%u, is_running=%d", - instance->encoder.repeat, - instance->encoder.is_running); instance->encoder.is_running = false; } return level_duration_reset(); } if(instance->encoder.front >= instance->encoder.size_upload) { - FURI_LOG_E( - TAG, - "Encoder front out of bounds: %zu >= %zu", - instance->encoder.front, - instance->encoder.size_upload); instance->encoder.is_running = false; instance->encoder.front = 0; return level_duration_reset(); @@ -628,101 +598,48 @@ LevelDuration kia_protocol_encoder_v3_v4_yield(void* context) { LevelDuration ret = instance->encoder.upload[instance->encoder.front]; - if(instance->encoder.front < 5) { - FURI_LOG_D( - TAG, - "Encoder yield[%zu]: repeat=%u, level=%d, duration=%lu", - instance->encoder.front, - instance->encoder.repeat, - level_duration_get_level(ret), - level_duration_get_duration(ret)); - } - if(++instance->encoder.front == instance->encoder.size_upload) { instance->encoder.repeat--; instance->encoder.front = 0; - FURI_LOG_I( - TAG, "Encoder completed one cycle, remaining repeat=%u", instance->encoder.repeat); } return ret; } -void kia_protocol_encoder_v3_v4_set_button(void* context, uint8_t button) { - furi_check(context); - SubGhzProtocolEncoderKiaV3V4* instance = context; - instance->btn = button & 0x0F; - instance->generic.btn = instance->btn; - kia_protocol_encoder_v3_v4_get_upload(instance); - FURI_LOG_I(TAG, "Button set to 0x%X", instance->btn); -} - -void kia_protocol_encoder_v3_v4_set_counter(void* context, uint16_t counter) { - furi_check(context); - SubGhzProtocolEncoderKiaV3V4* instance = context; - instance->cnt = counter; - instance->generic.cnt = instance->cnt; - kia_protocol_encoder_v3_v4_get_upload(instance); - FURI_LOG_I(TAG, "Counter set to 0x%04X", instance->cnt); -} - -void kia_protocol_encoder_v3_v4_increment_counter(void* context) { - furi_check(context); - SubGhzProtocolEncoderKiaV3V4* instance = context; - instance->cnt++; - instance->generic.cnt = instance->cnt; - kia_protocol_encoder_v3_v4_get_upload(instance); - FURI_LOG_I(TAG, "Counter incremented to 0x%04X", instance->cnt); -} - -uint16_t kia_protocol_encoder_v3_v4_get_counter(void* context) { - furi_check(context); - SubGhzProtocolEncoderKiaV3V4* instance = context; - return instance->cnt; -} - -uint8_t kia_protocol_encoder_v3_v4_get_button(void* context) { - furi_check(context); - SubGhzProtocolEncoderKiaV3V4* instance = context; - return instance->btn; -} - -// ============================================================================ -// DECODER IMPLEMENTATION -// ============================================================================ - -void* kia_protocol_decoder_v3_v4_alloc(SubGhzEnvironment* environment) { +void* subghz_protocol_decoder_kia_v3_v4_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolDecoderKiaV3V4* instance = malloc(sizeof(SubGhzProtocolDecoderKiaV3V4)); instance->base.protocol = &subghz_protocol_kia_v3_v4; instance->generic.protocol_name = instance->base.protocol->name; + instance->version = 0; + instance->is_v3_sync = false; return instance; } - -void kia_protocol_decoder_v3_v4_free(void* context) { - furi_check(context); +void subghz_protocol_decoder_kia_v3_v4_free(void* context) { + furi_assert(context); SubGhzProtocolDecoderKiaV3V4* instance = context; free(instance); } -void kia_protocol_decoder_v3_v4_reset(void* context) { - furi_check(context); +void subghz_protocol_decoder_kia_v3_v4_reset(void* context) { + furi_assert(context); SubGhzProtocolDecoderKiaV3V4* instance = context; instance->decoder.parser_step = KiaV3V4DecoderStepReset; instance->header_count = 0; instance->raw_bit_count = 0; + instance->is_v3_sync = false; instance->crc = 0; memset(instance->raw_bits, 0, sizeof(instance->raw_bits)); } -void kia_protocol_decoder_v3_v4_feed(void* context, bool level, uint32_t duration) { - furi_check(context); +void subghz_protocol_decoder_kia_v3_v4_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); SubGhzProtocolDecoderKiaV3V4* instance = context; switch(instance->decoder.parser_step) { - case KiaV3V4DecoderStepReset: - if(level && (DURATION_DIFF(duration, kia_protocol_v3_v4_const.te_short) < - kia_protocol_v3_v4_const.te_delta)) { + case KiaV3V4DecoderStepReset: + if(level && DURATION_DIFF(duration, subghz_protocol_kia_v3_v4_const.te_short) < + subghz_protocol_kia_v3_v4_const.te_delta) { instance->decoder.parser_step = KiaV3V4DecoderStepCheckPreamble; instance->decoder.te_last = duration; instance->header_count = 1; @@ -731,9 +648,10 @@ void kia_protocol_decoder_v3_v4_feed(void* context, bool level, uint32_t duratio case KiaV3V4DecoderStepCheckPreamble: if(level) { - if(DURATION_DIFF(duration, kia_protocol_v3_v4_const.te_short) < - kia_protocol_v3_v4_const.te_delta) { + if(DURATION_DIFF(duration, subghz_protocol_kia_v3_v4_const.te_short) < + subghz_protocol_kia_v3_v4_const.te_delta) { instance->decoder.te_last = duration; + instance->header_count++; } else if(duration > 1000 && duration < 1500) { if(instance->header_count >= 8) { instance->decoder.parser_step = KiaV3V4DecoderStepCollectRawBits; @@ -757,10 +675,10 @@ void kia_protocol_decoder_v3_v4_feed(void* context, bool level, uint32_t duratio instance->decoder.parser_step = KiaV3V4DecoderStepReset; } } else if( - (DURATION_DIFF(duration, kia_protocol_v3_v4_const.te_short) < - kia_protocol_v3_v4_const.te_delta) && - (DURATION_DIFF(instance->decoder.te_last, kia_protocol_v3_v4_const.te_short) < - kia_protocol_v3_v4_const.te_delta)) { + DURATION_DIFF(duration, subghz_protocol_kia_v3_v4_const.te_short) < + subghz_protocol_kia_v3_v4_const.te_delta && + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_v3_v4_const.te_short) < + subghz_protocol_kia_v3_v4_const.te_delta) { instance->header_count++; } else if(duration > 1500) { instance->decoder.parser_step = KiaV3V4DecoderStepReset; @@ -777,12 +695,12 @@ void kia_protocol_decoder_v3_v4_feed(void* context, bool level, uint32_t duratio } instance->decoder.parser_step = KiaV3V4DecoderStepReset; } else if( - DURATION_DIFF(duration, kia_protocol_v3_v4_const.te_short) < - kia_protocol_v3_v4_const.te_delta) { + DURATION_DIFF(duration, subghz_protocol_kia_v3_v4_const.te_short) < + subghz_protocol_kia_v3_v4_const.te_delta) { kia_v3_v4_add_raw_bit(instance, false); } else if( - DURATION_DIFF(duration, kia_protocol_v3_v4_const.te_long) < - kia_protocol_v3_v4_const.te_delta) { + DURATION_DIFF(duration, subghz_protocol_kia_v3_v4_const.te_long) < + subghz_protocol_kia_v3_v4_const.te_delta) { kia_v3_v4_add_raw_bit(instance, true); } else { instance->decoder.parser_step = KiaV3V4DecoderStepReset; @@ -806,119 +724,110 @@ void kia_protocol_decoder_v3_v4_feed(void* context, bool level, uint32_t duratio } } -uint8_t kia_protocol_decoder_v3_v4_get_hash_data(void* context) { - furi_check(context); +uint8_t subghz_protocol_decoder_kia_v3_v4_get_hash_data(void* context) { + furi_assert(context); SubGhzProtocolDecoderKiaV3V4* instance = context; return subghz_protocol_blocks_get_hash_data( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -SubGhzProtocolStatus kia_protocol_decoder_v3_v4_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_kia_v3_v4_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { - furi_check(context); + furi_assert(context); SubGhzProtocolDecoderKiaV3V4* instance = context; - SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + SubGhzProtocolStatus ret = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - do { - // Write frequency - if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { - break; - } - - // Write preset - if(!flipper_format_write_string_cstr( - flipper_format, "Preset", furi_string_get_cstr(preset->name))) { - break; - } - - // Write version-specific protocol name instead of generic "Kia V3/V4" - const char* version_name = (instance->version == 0) ? "Kia V4" : "Kia V3"; - if(!flipper_format_write_string_cstr(flipper_format, "Protocol", version_name)) { - break; - } - - // Write bit count - uint32_t bits = instance->generic.data_count_bit; - if(!flipper_format_write_uint32(flipper_format, "Bit", &bits, 1)) { - break; - } - - // Write key - uint8_t key_data[8]; - uint64_t kd = instance->generic.data; - for(int i = 7; i >= 0; i--) { key_data[i] = kd & 0xFF; kd >>= 8; } - if(!flipper_format_write_hex(flipper_format, "Key", key_data, 8)) { - break; - } - - // Write all fields needed by encoder - if(!flipper_format_write_uint32(flipper_format, "Serial", &instance->generic.serial, 1)) { - break; - } - - uint32_t temp = instance->generic.btn; - if(!flipper_format_write_uint32(flipper_format, "Btn", &temp, 1)) { - break; - } - - temp = instance->generic.cnt; - if(!flipper_format_write_uint32(flipper_format, "Cnt", &temp, 1)) { - break; - } - - // Write protocol-specific fields + if(ret == SubGhzProtocolStatusOk) { if(!flipper_format_write_uint32(flipper_format, "Encrypted", &instance->encrypted, 1)) { - break; + ret = SubGhzProtocolStatusErrorParserOthers; } + } + if(ret == SubGhzProtocolStatusOk) { if(!flipper_format_write_uint32(flipper_format, "Decrypted", &instance->decrypted, 1)) { - break; + ret = SubGhzProtocolStatusErrorParserOthers; } + } - temp = instance->version; - if(!flipper_format_write_uint32(flipper_format, "KIAVersion", &temp, 1)) { - break; + if(ret == SubGhzProtocolStatusOk) { + uint32_t temp = instance->version; + if(!flipper_format_write_uint32(flipper_format, "Version", &temp, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; } + } - temp = instance->crc; + if(ret == SubGhzProtocolStatusOk) { + uint32_t temp = instance->crc; if(!flipper_format_write_uint32(flipper_format, "CRC", &temp, 1)) { - break; + ret = SubGhzProtocolStatusErrorParserOthers; } - - ret = SubGhzProtocolStatusOk; - } while(false); + } return ret; } SubGhzProtocolStatus - kia_protocol_decoder_v3_v4_deserialize(void* context, FlipperFormat* flipper_format) { - furi_check(context); + subghz_protocol_decoder_kia_v3_v4_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); SubGhzProtocolDecoderKiaV3V4* instance = context; - - SubGhzProtocolStatus ret = - subghz_block_generic_deserialize_check_count_bit(&instance->generic, flipper_format, 64); + + SubGhzProtocolStatus ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); if(ret == SubGhzProtocolStatusOk) { - uint32_t temp = 0; - - if(flipper_format_read_uint32(flipper_format, "Encrypted", &temp, 1)) { - instance->encrypted = temp; - } - if(flipper_format_read_uint32(flipper_format, "Decrypted", &temp, 1)) { - instance->decrypted = temp; - } - if(flipper_format_read_uint32(flipper_format, "KIAVersion", &temp, 1)) { - instance->version = (uint8_t)temp; - } - if(flipper_format_read_uint32(flipper_format, "CRC", &temp, 1)) { - instance->crc = (uint8_t)temp; + if(instance->generic.data_count_bit < 64) { + ret = SubGhzProtocolStatusErrorParserBitCount; } } + + if(ret == SubGhzProtocolStatusOk) { + if(!flipper_format_read_uint32(flipper_format, "Encrypted", &instance->encrypted, 1)) { + instance->encrypted = 0; + } + + if(!flipper_format_read_uint32(flipper_format, "Decrypted", &instance->decrypted, 1)) { + instance->decrypted = 0; + } + + uint32_t temp_version = 0; + if(flipper_format_read_uint32(flipper_format, "Version", &temp_version, 1)) { + instance->version = temp_version; + } else { + instance->version = 0; + } + + uint32_t temp_crc = 0; + if(flipper_format_read_uint32(flipper_format, "CRC", &temp_crc, 1)) { + instance->crc = temp_crc; + } else { + instance->crc = 0; + } + + if(instance->decrypted != 0) { + instance->generic.btn = (instance->decrypted >> 28) & 0x0F; + instance->generic.cnt = instance->decrypted & 0xFFFF; + } + + if(instance->generic.data != 0) { + uint8_t b[8]; + for(int i = 0; i < 8; i++) { + b[i] = (instance->generic.data >> ((7-i) * 8)) & 0xFF; + } + + instance->generic.serial = ((uint32_t)reverse8(b[7] & 0xF0) << 24) | + ((uint32_t)reverse8(b[6]) << 16) | + ((uint32_t)reverse8(b[5]) << 8) | + (uint32_t)reverse8(b[4]); + } + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->generic.btn); + } + subghz_custom_btn_set_max(5); + } + return ret; } @@ -930,12 +839,18 @@ static uint64_t compute_yek(uint64_t key) { return yek; } -void kia_protocol_decoder_v3_v4_get_string(void* context, FuriString* output) { - furi_check(context); +static bool kia_v3_v4_verify_crc_from_data(uint64_t data, uint8_t received_crc) { + uint8_t bytes[8]; + for(int i = 0; i < 8; i++) { + bytes[i] = (data >> ((7-i) * 8)) & 0xFF; + } + uint8_t calculated_crc = kia_v3_v4_calculate_crc(bytes); + return (calculated_crc == received_crc); +} + +void subghz_protocol_decoder_kia_v3_v4_get_string(void* context, FuriString* output) { + furi_assert(context); SubGhzProtocolDecoderKiaV3V4* instance = context; - if(subghz_custom_btn_get_original() == 0) - subghz_custom_btn_set_original(instance->generic.btn); - subghz_custom_btn_set_max(4); uint64_t yek = compute_yek(instance->generic.data); uint32_t key_hi = (uint32_t)(instance->generic.data >> 32); @@ -943,42 +858,27 @@ void kia_protocol_decoder_v3_v4_get_string(void* context, FuriString* output) { uint32_t yek_hi = (uint32_t)(yek >> 32); uint32_t yek_lo = (uint32_t)(yek & 0xFFFFFFFF); - if(instance->version == 0) { - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%08lX%08lX\r\n" - "Yek:%08lX%08lX\r\n" - "Serial:%07lX Btn:%01X CRC:%01X\r\n" - "Decr:%08lX Cnt:%04lX\r\n", - kia_version_names[instance->version], - instance->generic.data_count_bit, - key_hi, - key_lo, - yek_hi, - yek_lo, - instance->generic.serial, - instance->generic.btn, - instance->crc, - instance->decrypted, - instance->generic.cnt); - } else { - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%08lX%08lX\r\n" - "Yek:%08lX%08lX\r\n" - "Serial:%07lX Btn:%01X\r\n" - "Decr:%08lX Cnt:%04lX\r\n", - kia_version_names[instance->version], - instance->generic.data_count_bit, - key_hi, - key_lo, - yek_hi, - yek_lo, - instance->generic.serial, - instance->generic.btn, - instance->decrypted, - instance->generic.cnt); - } + bool crc_valid = kia_v3_v4_verify_crc_from_data(instance->generic.data, instance->crc); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Yek:%08lX%08lX\r\n" + "Sn:%07lX Btn:%X [%s]\r\n" + "Dec:%08lX Cnt:%04lX\r\n" + "CRC:%X %s", + kia_version_names[instance->version], + instance->generic.data_count_bit, + key_hi, + key_lo, + yek_hi, + yek_lo, + instance->generic.serial, + instance->generic.btn, + subghz_protocol_kia_v3_v4_get_name_button(instance->generic.btn), + instance->decrypted, + instance->generic.cnt, + instance->crc, + crc_valid ? "(OK)" : "(FAIL)"); } From ddb85d034f499e8566aa4c876cfa70b10f23a112 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 11 Mar 2026 17:58:44 +0100 Subject: [PATCH 2/6] Refactor Kia V3/V4 protocol functions and types --- lib/subghz/protocols/kia_v3_v4.h | 42 ++++++++++++++------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/lib/subghz/protocols/kia_v3_v4.h b/lib/subghz/protocols/kia_v3_v4.h index 960d540b..880fd920 100644 --- a/lib/subghz/protocols/kia_v3_v4.h +++ b/lib/subghz/protocols/kia_v3_v4.h @@ -1,37 +1,31 @@ #pragma once -#include "kia_generic.h" +#include "base.h" +#include "../blocks/math.h" +#define SUBGHZ_PROTOCOL_KIA_V3_V4_NAME "KIA/HYU V3/V4" -#define KIA_PROTOCOL_V3_V4_NAME "Kia V3/V4" +typedef struct SubGhzProtocolDecoderKiaV3V4 SubGhzProtocolDecoderKiaV3V4; +typedef struct SubGhzProtocolEncoderKiaV3V4 SubGhzProtocolEncoderKiaV3V4; extern const SubGhzProtocol subghz_protocol_kia_v3_v4; -// Decoder functions -void* kia_protocol_decoder_v3_v4_alloc(SubGhzEnvironment* environment); -void kia_protocol_decoder_v3_v4_free(void* context); -void kia_protocol_decoder_v3_v4_reset(void* context); -void kia_protocol_decoder_v3_v4_feed(void* context, bool level, uint32_t duration); -uint8_t kia_protocol_decoder_v3_v4_get_hash_data(void* context); -SubGhzProtocolStatus kia_protocol_decoder_v3_v4_serialize( +void* subghz_protocol_decoder_kia_v3_v4_alloc(SubGhzEnvironment* environment); +void subghz_protocol_decoder_kia_v3_v4_free(void* context); +void subghz_protocol_decoder_kia_v3_v4_reset(void* context); +void subghz_protocol_decoder_kia_v3_v4_feed(void* context, bool level, uint32_t duration); +uint8_t subghz_protocol_decoder_kia_v3_v4_get_hash_data(void* context); +SubGhzProtocolStatus subghz_protocol_decoder_kia_v3_v4_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); SubGhzProtocolStatus - kia_protocol_decoder_v3_v4_deserialize(void* context, FlipperFormat* flipper_format); -void kia_protocol_decoder_v3_v4_get_string(void* context, FuriString* output); + subghz_protocol_decoder_kia_v3_v4_deserialize(void* context, FlipperFormat* flipper_format); +void subghz_protocol_decoder_kia_v3_v4_get_string(void* context, FuriString* output); -// Encoder functions -void* kia_protocol_encoder_v3_v4_alloc(SubGhzEnvironment* environment); -void kia_protocol_encoder_v3_v4_free(void* context); +void* subghz_protocol_encoder_kia_v3_v4_alloc(SubGhzEnvironment* environment); +void subghz_protocol_encoder_kia_v3_v4_free(void* context); SubGhzProtocolStatus - kia_protocol_encoder_v3_v4_deserialize(void* context, FlipperFormat* flipper_format); -void kia_protocol_encoder_v3_v4_stop(void* context); -LevelDuration kia_protocol_encoder_v3_v4_yield(void* context); - -// Encoder helper functions for UI -void kia_protocol_encoder_v3_v4_set_button(void* context, uint8_t button); -void kia_protocol_encoder_v3_v4_set_counter(void* context, uint16_t counter); -void kia_protocol_encoder_v3_v4_increment_counter(void* context); -uint16_t kia_protocol_encoder_v3_v4_get_counter(void* context); -uint8_t kia_protocol_encoder_v3_v4_get_button(void* context); + subghz_protocol_encoder_kia_v3_v4_deserialize(void* context, FlipperFormat* flipper_format); +void subghz_protocol_encoder_kia_v3_v4_stop(void* context); +LevelDuration subghz_protocol_encoder_kia_v3_v4_yield(void* context); From cffd268950157a4846e5bf36c64f4920eeeb77fb Mon Sep 17 00:00:00 2001 From: David Date: Wed, 11 Mar 2026 17:59:14 +0100 Subject: [PATCH 3/6] Update --- lib/subghz/protocols/kia_v5.c | 955 +++++++++++++++------------------- 1 file changed, 410 insertions(+), 545 deletions(-) diff --git a/lib/subghz/protocols/kia_v5.c b/lib/subghz/protocols/kia_v5.c index 8046753d..6d6ee38d 100644 --- a/lib/subghz/protocols/kia_v5.c +++ b/lib/subghz/protocols/kia_v5.c @@ -1,37 +1,24 @@ #include "kia_v5.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" #include "../blocks/custom_btn_i.h" +#include +#include -// Force logging for this file only - will remove after done testing -#include -#define KV5_LOG(fmt, ...) furi_log_print_format(FuriLogLevelInfo, "KiaV5", fmt, ##__VA_ARGS__) +#define TAG "SubGhzProtocolKiaV5" - -#include "keys.h" - - -static const SubGhzBlockConst kia_protocol_v5_const = { +static const SubGhzBlockConst subghz_protocol_kia_v5_const = { .te_short = 400, .te_long = 800, .te_delta = 150, .min_count_bit_for_found = 64, }; -// ============================================================================= -// KEY DERIVATION -// ============================================================================= - -static uint8_t keystore_bytes[8] = {0}; - -static void build_keystore_from_mfkey(uint8_t* result) { - uint64_t ky = get_kia_v5_key(); - for(int i = 0; i < 8; i++) { - result[i] = (ky >> ((7 - i) * 8)) & 0xFF; - } -} - -// ============================================================================= -// BIT REVERSAL HELPERS -// ============================================================================= +static const uint8_t keystore_bytes[] = {0x53, 0x54, 0x46, 0x52, 0x4b, 0x45, 0x30, 0x30}; static uint8_t reverse_byte(uint8_t b) { uint8_t r = 0; @@ -51,11 +38,6 @@ static uint64_t bit_reverse_64(uint64_t input) { return output; } -// ============================================================================= -// CRC CALCULATION -// ============================================================================= - -#ifdef ENABLE_EMULATE_FEATURE static uint8_t kia_v5_calculate_crc(uint64_t yek) { uint8_t crc = 0; for(int i = 0; i < 16; i++) { @@ -63,11 +45,6 @@ static uint8_t kia_v5_calculate_crc(uint64_t yek) { } return crc & 0x07; } -#endif - -// ============================================================================= -// DECRYPTION -// ============================================================================= static uint16_t mixer_decode(uint32_t encrypted) { uint8_t s0 = (encrypted & 0xFF); @@ -75,8 +52,6 @@ static uint16_t mixer_decode(uint32_t encrypted) { uint8_t s2 = (encrypted >> 16) & 0xFF; uint8_t s3 = (encrypted >> 24) & 0xFF; - build_keystore_from_mfkey(keystore_bytes); - int round_index = 1; for(size_t i = 0; i < 18; i++) { uint8_t r = keystore_bytes[round_index] & 0xFF; @@ -88,7 +63,6 @@ static uint16_t mixer_decode(uint32_t encrypted) { } else { base = (s3 & 0x02) == 0 ? 0x3A : 0x5C; } - if(s2 & 0x08) { base = (((base >> 4) & 0x0F) | ((base & 0x0F) << 4)) & 0xFF; } @@ -98,26 +72,16 @@ static uint16_t mixer_decode(uint32_t encrypted) { if(s0 & 0x01) { base = (base << 1) & 0xFF; } - uint8_t temp = (s3 ^ s1) & 0xFF; s3 = ((s3 & 0x7F) << 1) & 0xFF; - if(s2 & 0x80) { - s3 |= 0x01; - } + if(s2 & 0x80) s3 |= 0x01; s2 = ((s2 & 0x7F) << 1) & 0xFF; - if(s1 & 0x80) { - s2 |= 0x01; - } + if(s1 & 0x80) s2 |= 0x01; s1 = ((s1 & 0x7F) << 1) & 0xFF; - if(s0 & 0x80) { - s1 |= 0x01; - } + if(s0 & 0x80) s1 |= 0x01; s0 = ((s0 & 0x7F) << 1) & 0xFF; - uint8_t chk = (base ^ (r ^ temp)) & 0xFF; - if(chk & 0x80) { - s0 |= 0x01; - } + if(chk & 0x80) s0 |= 0x01; r = ((r & 0x7F) << 1) & 0xFF; steps--; } @@ -126,74 +90,51 @@ static uint16_t mixer_decode(uint32_t encrypted) { return (s0 + (s1 << 8)) & 0xFFFF; } -// ============================================================================= -// ENCRYPTION -// ============================================================================= - -#ifdef ENABLE_EMULATE_FEATURE static uint32_t mixer_encode(uint16_t counter, uint16_t seed) { uint8_t s0 = counter & 0xFF; uint8_t s1 = (counter >> 8) & 0xFF; uint8_t s2 = seed & 0xFF; uint8_t s3 = (seed >> 8) & 0xFF; - build_keystore_from_mfkey(keystore_bytes); + int dec_rounds[18]; + int ri = 1; + for(int i = 0; i < 18; i++) { dec_rounds[i] = ri; ri = (ri - 1) & 0x7; } - int round_index = 0; - for(size_t i = 0; i < 18; i++) { - uint8_t r = keystore_bytes[round_index] & 0xFF; - uint8_t r_reversed = reverse_byte(r); + for(int i = 17; i >= 0; i--) { + uint8_t r_orig = keystore_bytes[dec_rounds[i]] & 0xFF; + uint8_t r_steps[8]; + r_steps[0] = r_orig; + for(int k = 1; k < 8; k++) + r_steps[k] = ((r_steps[k-1] & 0x7F) << 1) & 0xFF; for(int step = 7; step >= 0; step--) { - uint8_t out_bit = s0 & 0x01; - - s0 = ((s0 >> 1) | ((s1 & 0x01) << 7)) & 0xFF; - s1 = ((s1 >> 1) | ((s2 & 0x01) << 7)) & 0xFF; - s2 = ((s2 >> 1) | ((s3 & 0x01) << 7)) & 0xFF; - s3 = (s3 >> 1) & 0xFF; - - uint8_t feedback; - if((s3 & 0x20) == 0) { - feedback = (s3 & 0x01) ? 0x2E : 0x74; - } else { - feedback = (s3 & 0x01) ? 0x5C : 0x3A; - } - - if(s2 & 0x04) { - feedback = ((feedback >> 4) | (feedback << 4)) & 0xFF; - } - if(s1 & 0x80) { - feedback = (feedback << 2) & 0xFF; - } - if(s0 & 0x80) { - feedback = (feedback << 1) & 0xFF; - } - - feedback ^= (s3 ^ s1); - feedback ^= ((r_reversed >> (7 - step)) & 0x01) << 7; - - if(((feedback >> 7) & 1) != out_bit) { - s3 |= 0x80; - } + uint8_t r = r_steps[step]; + uint8_t feedback_bit = s0 & 0x01; + uint8_t s3b = s3 >> 1; + uint8_t s2b = (s2 >> 1) | ((s3 & 0x01) << 7); + uint8_t s1b = (s1 >> 1) | ((s2 & 0x01) << 7); + uint8_t s0b = (s0 >> 1) | ((s1 & 0x01) << 7); + uint8_t base, temp, chk; + if((s3b & 0x40) == 0) base = (s3b & 0x02) == 0 ? 0x74 : 0x2E; + else base = (s3b & 0x02) == 0 ? 0x3A : 0x5C; + if(s2b & 0x08) base = (((base >> 4) & 0x0F) | ((base & 0x0F) << 4)) & 0xFF; + if(s1b & 0x01) base = ((base & 0x3F) << 2) & 0xFF; + if(s0b & 0x01) base = (base << 1) & 0xFF; + temp = (s3b ^ s1b) & 0xFF; + chk = (base ^ (r ^ temp)) & 0xFF; + if(((chk >> 7) & 1) == feedback_bit) s3 = s3b; + else s3 = s3b | 0x80; + s2 = s2b; s1 = s1b; s0 = s0b; } - - round_index = (round_index + 1) & 0x07; } - return ((uint32_t)s3 << 24) | ((uint32_t)s2 << 16) | ((uint32_t)s1 << 8) | s0; } -#endif - -// ============================================================================= -// STRUCT DEFINITIONS -// ============================================================================= struct SubGhzProtocolDecoderKiaV5 { SubGhzProtocolDecoderBase base; SubGhzBlockDecoder decoder; SubGhzBlockGeneric generic; uint16_t header_count; - ManchesterState manchester_state; uint64_t decoded_data; uint64_t saved_key; @@ -202,26 +143,11 @@ struct SubGhzProtocolDecoderKiaV5 { uint8_t crc; }; -#ifdef ENABLE_EMULATE_FEATURE -struct SubGhzProtocolEncoderKiaV5 { - SubGhzProtocolEncoderBase base; - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - - uint32_t serial; - uint8_t button; - uint16_t counter; - uint64_t yek; - uint64_t key; - uint8_t crc; -}; -#else struct SubGhzProtocolEncoderKiaV5 { SubGhzProtocolEncoderBase base; SubGhzProtocolBlockEncoder encoder; SubGhzBlockGeneric generic; }; -#endif typedef enum { KiaV5DecoderStepReset = 0, @@ -229,447 +155,289 @@ typedef enum { KiaV5DecoderStepData, } KiaV5DecoderStep; -// ============================================================================= -// PROTOCOL INTERFACE DEFINITIONS -// ============================================================================= - -const SubGhzProtocolDecoder kia_protocol_v5_decoder = { - .alloc = kia_protocol_decoder_v5_alloc, - .free = kia_protocol_decoder_v5_free, - .feed = kia_protocol_decoder_v5_feed, - .reset = kia_protocol_decoder_v5_reset, - .get_hash_data = kia_protocol_decoder_v5_get_hash_data, - .serialize = kia_protocol_decoder_v5_serialize, - .deserialize = kia_protocol_decoder_v5_deserialize, - .get_string = kia_protocol_decoder_v5_get_string, +const SubGhzProtocolDecoder subghz_protocol_kia_v5_decoder = { + .alloc = subghz_protocol_decoder_kia_v5_alloc, + .free = subghz_protocol_decoder_kia_v5_free, + .feed = subghz_protocol_decoder_kia_v5_feed, + .reset = subghz_protocol_decoder_kia_v5_reset, + .get_hash_data = subghz_protocol_decoder_kia_v5_get_hash_data, + .get_hash_data_long = NULL, + .serialize = subghz_protocol_decoder_kia_v5_serialize, + .deserialize = subghz_protocol_decoder_kia_v5_deserialize, + .get_string = subghz_protocol_decoder_kia_v5_get_string, + .get_string_brief = NULL, }; -#ifdef ENABLE_EMULATE_FEATURE -const SubGhzProtocolEncoder kia_protocol_v5_encoder = { - .alloc = kia_protocol_encoder_v5_alloc, - .free = kia_protocol_encoder_v5_free, - .deserialize = kia_protocol_encoder_v5_deserialize, - .stop = kia_protocol_encoder_v5_stop, - .yield = kia_protocol_encoder_v5_yield, +const SubGhzProtocolEncoder subghz_protocol_kia_v5_encoder = { + .alloc = subghz_protocol_encoder_kia_v5_alloc, + .free = subghz_protocol_encoder_kia_v5_free, + .deserialize = subghz_protocol_encoder_kia_v5_deserialize, + .stop = subghz_protocol_encoder_kia_v5_stop, + .yield = subghz_protocol_encoder_kia_v5_yield, }; -#else -const SubGhzProtocolEncoder kia_protocol_v5_encoder = { - .alloc = NULL, - .free = NULL, - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; -#endif const SubGhzProtocol subghz_protocol_kia_v5 = { - .name = KIA_PROTOCOL_V5_NAME, + .name = SUBGHZ_PROTOCOL_KIA_V5_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - .decoder = &kia_protocol_v5_decoder, - .encoder = &kia_protocol_v5_encoder, + .decoder = &subghz_protocol_kia_v5_decoder, + .encoder = &subghz_protocol_kia_v5_encoder, }; -// ============================================================================= -// ENCODER IMPLEMENTATION -// ============================================================================= - -#ifdef ENABLE_EMULATE_FEATURE - -static uint8_t kia_v5_get_btn_code() { - uint8_t custom_btn = subghz_custom_btn_get(); - uint8_t original_btn = subghz_custom_btn_get_original(); - if(custom_btn == SUBGHZ_CUSTOM_BTN_OK) return original_btn; - if(custom_btn == SUBGHZ_CUSTOM_BTN_UP) return 0x01; // Lock - if(custom_btn == SUBGHZ_CUSTOM_BTN_DOWN) return 0x02; // Unlock - if(custom_btn == SUBGHZ_CUSTOM_BTN_LEFT) return 0x03; // Boot/Trunk - if(custom_btn == SUBGHZ_CUSTOM_BTN_RIGHT) return 0x03; // Boot/Trunk - return original_btn; -} - -#define KIA_V5_PREAMBLE_PULSES 50 -#define KIA_V5_TOTAL_BURSTS 3 -#define KIA_V5_INTER_BURST_GAP_US 10000 - -// Static state — persists across encoder alloc/free cycles -// for the lifetime of the app session -static uint16_t kia_v5_last_counter = 0; -static bool kia_v5_counter_loaded = false; -static bool kia_v5_tx_done = false; - -void* kia_protocol_encoder_v5_alloc(SubGhzEnvironment* environment) { +void* subghz_protocol_encoder_kia_v5_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolEncoderKiaV5* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV5)); - instance->base.protocol = &subghz_protocol_kia_v5; instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 1; - instance->encoder.size_upload = 1024; + instance->encoder.size_upload = 400; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.repeat = 10; instance->encoder.is_running = false; - instance->encoder.front = 0; - - instance->serial = 0; - instance->button = 0; - instance->counter = 0; - instance->yek = 0; - instance->key = 0; - instance->crc = 0; - - kia_v5_tx_done = false; - return instance; } -void kia_protocol_encoder_v5_free(void* context) { - furi_check(context); +void subghz_protocol_encoder_kia_v5_free(void* context) { + furi_assert(context); SubGhzProtocolEncoderKiaV5* instance = context; - if(instance->encoder.upload) { - free(instance->encoder.upload); - } + free(instance->encoder.upload); free(instance); } -static void kia_protocol_encoder_v5_update_data(SubGhzProtocolEncoderKiaV5* instance) { - uint16_t seed = instance->serial & 0xFFFF; - uint32_t encrypted = mixer_encode(instance->counter, seed); - - instance->yek = ((uint64_t)(instance->button & 0x0F) << 60) | - ((uint64_t)(instance->serial & 0x0FFFFFFF) << 32) | (uint64_t)encrypted; - - instance->crc = kia_v5_calculate_crc(instance->yek); - instance->key = bit_reverse_64(instance->yek); - - instance->generic.data = instance->key; - instance->generic.data_count_bit = 64; - instance->generic.serial = instance->serial; - instance->generic.btn = instance->button; - instance->generic.cnt = instance->counter; - - KV5_LOG( - "TX: Key=%08lX%08lX Yek=%08lX%08lX Sn=%07lX Btn=%X Cnt=%04X CRC=%X", - (uint32_t)(instance->key >> 32), - (uint32_t)(instance->key & 0xFFFFFFFF), - (uint32_t)(instance->yek >> 32), - (uint32_t)(instance->yek & 0xFFFFFFFF), - (unsigned long)instance->serial, - instance->button, - instance->counter, - instance->crc); +void subghz_protocol_encoder_kia_v5_stop(void* context) { + SubGhzProtocolEncoderKiaV5* instance = context; + instance->encoder.is_running = false; } -static void kia_protocol_encoder_v5_get_upload(SubGhzProtocolEncoderKiaV5* instance) { - furi_check(instance); +static bool subghz_protocol_encoder_kia_v5_get_upload(SubGhzProtocolEncoderKiaV5* instance); + +LevelDuration subghz_protocol_encoder_kia_v5_yield(void* context) { + SubGhzProtocolEncoderKiaV5* instance = context; + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + return ret; +} + +static bool subghz_protocol_encoder_kia_v5_get_upload(SubGhzProtocolEncoderKiaV5* instance) { + furi_assert(instance); + + uint8_t btn_index = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ? + subghz_custom_btn_get_original() : + subghz_custom_btn_get(); + + uint8_t btn; + const uint8_t btn_map[5] = {0xFF, 0x02, 0x01, 0x04, 0x08}; + if(btn_index == 0) { + btn = subghz_custom_btn_get_original(); + } else { + btn = btn_map[btn_index]; + } + + instance->generic.btn = btn; + + uint64_t yek = 0; + yek |= ((uint64_t)(btn & 0x0F) << 60); + yek |= ((uint64_t)(instance->generic.serial & 0x0FFFFFFF) << 32); + + uint16_t seed = ((uint16_t)(btn & 0x0F) << 12) | (instance->generic.serial & 0x0FFF); + uint32_t encrypted = mixer_encode(instance->generic.cnt, seed); + yek |= (uint64_t)encrypted; + + uint8_t crc = kia_v5_calculate_crc(yek); + uint64_t key = bit_reverse_64(yek); + + instance->generic.data = key; + instance->generic.data_count_bit = 64; + size_t index = 0; - uint32_t te_short = kia_protocol_v5_const.te_short; - uint32_t te_long = kia_protocol_v5_const.te_long; + for(size_t i = 0; i < 80; i++) { + instance->encoder.upload[index++] = level_duration_make(true, subghz_protocol_kia_v5_const.te_short); + instance->encoder.upload[index++] = level_duration_make(false, subghz_protocol_kia_v5_const.te_short); + } + + instance->encoder.upload[index++] = level_duration_make(true, subghz_protocol_kia_v5_const.te_long); + instance->encoder.upload[index++] = level_duration_make(false, subghz_protocol_kia_v5_const.te_short); bool bits[67]; for(int i = 0; i < 64; i++) { - bits[i] = (instance->key >> (63 - i)) & 1; + bits[i] = (key >> (63 - i)) & 1; } for(int i = 0; i < 3; i++) { - bits[64 + i] = (instance->crc >> (2 - i)) & 1; + bits[64 + i] = (crc >> (2 - i)) & 1; } -#define KV5_ADD(lvl, dur) \ - do { \ - if(index > 0 && level_duration_get_level(instance->encoder.upload[index - 1]) == (lvl)) { \ - uint32_t prev = level_duration_get_duration(instance->encoder.upload[index - 1]); \ - instance->encoder.upload[index - 1] = level_duration_make((lvl), prev + (dur)); \ - } else { \ - instance->encoder.upload[index++] = level_duration_make((lvl), (dur)); \ - } \ - } while(0) + bool prev_bit = true; - for(uint8_t burst = 0; burst < KIA_V5_TOTAL_BURSTS; burst++) { - if(burst > 0) { - KV5_ADD(false, KIA_V5_INTER_BURST_GAP_US); + for(int i = 0; i < 67; i++) { + bool curr_bit = bits[i]; + if(prev_bit && curr_bit) { + instance->encoder.upload[index++] = level_duration_make(true, subghz_protocol_kia_v5_const.te_short); + instance->encoder.upload[index++] = level_duration_make(false, subghz_protocol_kia_v5_const.te_short); + } else if(prev_bit && !curr_bit) { + instance->encoder.upload[index++] = level_duration_make(true, subghz_protocol_kia_v5_const.te_long); + } else if(!prev_bit && curr_bit) { + instance->encoder.upload[index++] = level_duration_make(false, subghz_protocol_kia_v5_const.te_long); + } else { + instance->encoder.upload[index++] = level_duration_make(false, subghz_protocol_kia_v5_const.te_short); + instance->encoder.upload[index++] = level_duration_make(true, subghz_protocol_kia_v5_const.te_short); } - - // Preamble: alternating SHORT HIGH / SHORT LOW pairs - for(int i = 0; i < KIA_V5_PREAMBLE_PULSES; i++) { - KV5_ADD(true, te_short); - KV5_ADD(false, te_short); - } - - // Sync: LONG HIGH (consumed by preamble detector, triggers data mode) - KV5_ADD(true, te_long); - - // Alignment: SHORT LOW (ensures level transition; absorbed by Manchester reset) - KV5_ADD(false, te_short); - - // Manchester data encoding using prev_bit transitions. - // After alignment LOW, line state matches "after bit=1" (both end LOW), - // so prev_bit starts as true. - // - // Kia V5 Manchester polarity (level=true -> ShortHigh): - // prev=1, curr=1: HIGH short, LOW short (ends LOW) - // prev=1, curr=0: HIGH long (ends HIGH) - // prev=0, curr=0: LOW short, HIGH short (ends HIGH) - // prev=0, curr=1: LOW long (ends LOW) - bool prev_bit = true; - - for(int i = 0; i < 67; i++) { - bool curr_bit = bits[i]; - - if(prev_bit && curr_bit) { - KV5_ADD(true, te_short); - KV5_ADD(false, te_short); - } else if(prev_bit && !curr_bit) { - KV5_ADD(true, te_long); - } else if(!prev_bit && curr_bit) { - KV5_ADD(false, te_long); - } else { - KV5_ADD(false, te_short); - KV5_ADD(true, te_short); - } - - prev_bit = curr_bit; - } - - // Termination: create a level transition after the last data bit - KV5_ADD(prev_bit, te_short); + prev_bit = curr_bit; } -#undef KV5_ADD - + instance->encoder.upload[index++] = level_duration_make(prev_bit, subghz_protocol_kia_v5_const.te_short); + instance->encoder.upload[index++] = level_duration_make(false, subghz_protocol_kia_v5_const.te_long * 10); instance->encoder.size_upload = index; - instance->encoder.front = 0; - KV5_LOG( - "Upload: %d bursts, size=%zu, bits=%u", - KIA_V5_TOTAL_BURSTS, - instance->encoder.size_upload, - instance->generic.data_count_bit); + return true; } SubGhzProtocolStatus - kia_protocol_encoder_v5_deserialize(void* context, FlipperFormat* flipper_format) { - furi_check(context); + subghz_protocol_encoder_kia_v5_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); SubGhzProtocolEncoderKiaV5* instance = context; SubGhzProtocolStatus ret = SubGhzProtocolStatusError; - instance->encoder.is_running = false; - instance->encoder.front = 0; - instance->encoder.repeat = 1; + static uint32_t call_count = 0; + call_count++; + FURI_LOG_I(TAG, "deserialize #%lu, cnt before=%04lX", call_count, (uint32_t)instance->generic.cnt); do { - // Maybe loop is from here idk - if(kia_v5_tx_done) { - KV5_LOG("TX already completed, blocking re-deserialize"); + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) break; + + if(instance->generic.data_count_bit < subghz_protocol_kia_v5_const.min_count_bit_for_found) { + ret = SubGhzProtocolStatusErrorParserBitCount; break; } - FuriString* temp_str = furi_string_alloc(); - if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) { - KV5_LOG("Missing Protocol"); - furi_string_free(temp_str); - break; - } - - if(!furi_string_equal(temp_str, instance->base.protocol->name)) { - KV5_LOG("Wrong protocol %s", furi_string_get_cstr(temp_str)); - furi_string_free(temp_str); - break; - } - furi_string_free(temp_str); - - uint32_t bit_count_temp; - if(!flipper_format_read_uint32(flipper_format, "Bit", &bit_count_temp, 1)) { - KV5_LOG("Missing Bit"); - break; - } - - instance->generic.data_count_bit = 64; - - temp_str = furi_string_alloc(); - if(!flipper_format_read_string(flipper_format, "Key", temp_str)) { - KV5_LOG("Missing Key"); - furi_string_free(temp_str); - break; - } - - const char* key_str = furi_string_get_cstr(temp_str); - uint64_t key = 0; - size_t str_len = strlen(key_str); - size_t hex_pos = 0; - - for(size_t i = 0; i < str_len && hex_pos < 16; i++) { - char c = key_str[i]; - if(c == ' ') continue; - - uint8_t nibble; - if(c >= '0' && c <= '9') { - nibble = c - '0'; - } else if(c >= 'A' && c <= 'F') { - nibble = c - 'A' + 10; - } else if(c >= 'a' && c <= 'f') { - nibble = c - 'a' + 10; - } else { - KV5_LOG("Invalid hex character: %c", c); - furi_string_free(temp_str); - break; - } - - key = (key << 4) | nibble; - hex_pos++; - } - - furi_string_free(temp_str); - - if(hex_pos != 16) { - KV5_LOG("Invalid key length: %zu nibbles", hex_pos); - break; - } - - instance->generic.data = key; - - if(instance->generic.data == 0) { - KV5_LOG("Key is zero after parsing"); - break; - } - - KV5_LOG("Parsed key: %08lX%08lX", (uint32_t)(key >> 32), (uint32_t)(key & 0xFFFFFFFF)); - - // Read serial (always from file — serial doesn't change) - if(!flipper_format_read_uint32(flipper_format, "Serial", &instance->serial, 1)) { - uint64_t yek = bit_reverse_64(key); - instance->serial = (uint32_t)((yek >> 32) & 0x0FFFFFFF); - KV5_LOG("Extracted serial: 0x%07lX", (unsigned long)instance->serial); + uint32_t yek_high = 0, yek_low = 0; + uint64_t yek = 0; + if(flipper_format_read_uint32(flipper_format, "YekHi", &yek_high, 1) && + flipper_format_read_uint32(flipper_format, "YekLo", &yek_low, 1)) { + yek = ((uint64_t)yek_high << 32) | yek_low; } else { - KV5_LOG("Read serial: 0x%07lX", (unsigned long)instance->serial); + yek = bit_reverse_64(instance->generic.data); } - instance->generic.serial = instance->serial; - // Read button (always from file — button doesn't change) - uint32_t btn_temp; - if(flipper_format_read_uint32(flipper_format, "Btn", &btn_temp, 1)) { - instance->button = (uint8_t)btn_temp; - KV5_LOG("Read button: 0x%X", instance->button); - } else { - uint64_t yek = bit_reverse_64(key); - instance->button = (uint8_t)((yek >> 60) & 0x0F); - KV5_LOG("Extracted button: 0x%X", instance->button); - } - instance->generic.btn = instance->button; - if(subghz_custom_btn_get_original() == 0) + instance->generic.serial = (uint32_t)((yek >> 32) & 0x0FFFFFFF); + instance->generic.btn = (uint8_t)((yek >> 60) & 0x0F); + + uint32_t encrypted = (uint32_t)(yek & 0xFFFFFFFF); + instance->generic.cnt = mixer_decode(encrypted); + + uint32_t mult = furi_hal_subghz_get_rolling_counter_mult(); + instance->generic.cnt = (instance->generic.cnt + mult) & 0xFFFF; + FURI_LOG_I(TAG, "deserialize #%lu, cnt after=%04lX", call_count, (uint32_t)instance->generic.cnt); + + if(subghz_custom_btn_get_original() == 0) { subghz_custom_btn_set_original(instance->generic.btn); - subghz_custom_btn_set_max(4); - instance->generic.btn = kia_v5_get_btn_code(); - instance->button = instance->generic.btn; - - // Read counter: use static state to persist across encoder - // alloc/free cycles within the same app session. - if(!kia_v5_counter_loaded) { - uint32_t cnt_temp; - if(flipper_format_read_uint32(flipper_format, "Cnt", &cnt_temp, 1)) { - instance->counter = (uint16_t)cnt_temp; - KV5_LOG("Read counter from file: 0x%04X", instance->counter); - } else { - uint64_t yek = bit_reverse_64(key); - uint32_t encrypted = (uint32_t)(yek & 0xFFFFFFFF); - instance->counter = mixer_decode(encrypted); - KV5_LOG("Extracted counter: 0x%04X", instance->counter); - } - kia_v5_counter_loaded = true; - } else { - instance->counter = kia_v5_last_counter; - KV5_LOG("Using static counter: 0x%04X", instance->counter); } + subghz_custom_btn_set_max(4); - // Increment counter for rolling code - uint16_t old_counter = instance->counter; - instance->counter = (instance->counter + 1) & 0xFFFF; - instance->generic.cnt = instance->counter; - kia_v5_last_counter = instance->counter; - KV5_LOG("Counter incremented: 0x%04X -> 0x%04X", old_counter, instance->counter); - - // Build the signal with new counter - kia_protocol_encoder_v5_update_data(instance); - kia_protocol_encoder_v5_get_upload(instance); - - if(instance->encoder.size_upload == 0) { - KV5_LOG("Upload build failed"); + if(!subghz_protocol_encoder_kia_v5_get_upload(instance)) { ret = SubGhzProtocolStatusErrorEncoderGetUpload; break; } - // Try to persist updated counter back to file for next app session - flipper_format_rewind(flipper_format); - uint32_t cnt32 = instance->counter; - if(!flipper_format_update_uint32(flipper_format, "Cnt", &cnt32, 1)) { - flipper_format_rewind(flipper_format); - flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &cnt32, 1); + if(!flipper_format_rewind(flipper_format)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + ret = SubGhzProtocolStatusErrorParserKey; + break; + } + + uint32_t temp_btn = instance->generic.btn; + if(!flipper_format_update_uint32(flipper_format, "Btn", &temp_btn, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + + if(!flipper_format_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + + yek = bit_reverse_64(instance->generic.data); + yek_high = (uint32_t)(yek >> 32); + yek_low = (uint32_t)(yek & 0xFFFFFFFF); + if(!flipper_format_update_uint32(flipper_format, "YekHi", &yek_high, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + if(!flipper_format_update_uint32(flipper_format, "YekLo", &yek_low, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + + uint8_t crc = kia_v5_calculate_crc(yek); + uint32_t crc_temp = crc; + if(!flipper_format_update_uint32(flipper_format, "CRC", &crc_temp, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + break; } instance->encoder.is_running = true; - KV5_LOG( - "Encoder ready: repeat=%u, size=%zu, Sn=%07lX Btn=%X Cnt=%04X CRC=%X", - instance->encoder.repeat, - instance->encoder.size_upload, - (unsigned long)instance->serial, - instance->button, - instance->counter, - instance->crc); - - ret = SubGhzProtocolStatusOk; } while(false); return ret; } -void kia_protocol_encoder_v5_stop(void* context) { - furi_check(context); +void subghz_protocol_encoder_kia_v5_set_button(void* context, uint8_t button) { + furi_assert(context); SubGhzProtocolEncoderKiaV5* instance = context; - instance->encoder.is_running = false; - kia_v5_tx_done = false; + instance->generic.btn = button; } -LevelDuration kia_protocol_encoder_v5_yield(void* context) { - furi_check(context); +void subghz_protocol_encoder_kia_v5_set_counter(void* context, uint16_t counter) { + furi_assert(context); SubGhzProtocolEncoderKiaV5* instance = context; - - if(!instance->encoder.is_running || instance->encoder.repeat == 0) { - instance->encoder.is_running = false; - kia_v5_tx_done = true; - return level_duration_reset(); - } - - LevelDuration ret = instance->encoder.upload[instance->encoder.front]; - - if(++instance->encoder.front == instance->encoder.size_upload) { - instance->encoder.repeat--; - instance->encoder.front = 0; - } - - return ret; + instance->generic.cnt = counter; } -#endif // ENABLE_EMULATE_FEATURE +void subghz_protocol_encoder_kia_v5_increment_counter(void* context) { + furi_assert(context); + SubGhzProtocolEncoderKiaV5* instance = context; + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else { + instance->generic.cnt = 0; + } +} -// ============================================================================= -// DECODER IMPLEMENTATION -// ============================================================================= +uint16_t subghz_protocol_encoder_kia_v5_get_counter(void* context) { + furi_assert(context); + SubGhzProtocolEncoderKiaV5* instance = context; + return instance->generic.cnt; +} + +uint8_t subghz_protocol_encoder_kia_v5_get_button(void* context) { + furi_assert(context); + SubGhzProtocolEncoderKiaV5* instance = context; + return instance->generic.btn; +} static void kia_v5_add_bit(SubGhzProtocolDecoderKiaV5* instance, bool bit) { instance->decoded_data = (instance->decoded_data << 1) | (bit ? 1 : 0); instance->bit_count++; - if(instance->bit_count <= 8 || instance->bit_count == 32 || instance->bit_count == 64) { - KV5_LOG( - "Bit[%d]=%d data=%08lX%08lX", - instance->bit_count - 1, - bit ? 1 : 0, - (uint32_t)(instance->decoded_data >> 32), - (uint32_t)(instance->decoded_data & 0xFFFFFFFF)); - } } -void* kia_protocol_decoder_v5_alloc(SubGhzEnvironment* environment) { +void* subghz_protocol_decoder_kia_v5_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolDecoderKiaV5* instance = malloc(sizeof(SubGhzProtocolDecoderKiaV5)); instance->base.protocol = &subghz_protocol_kia_v5; @@ -677,14 +445,14 @@ void* kia_protocol_decoder_v5_alloc(SubGhzEnvironment* environment) { return instance; } -void kia_protocol_decoder_v5_free(void* context) { - furi_check(context); +void subghz_protocol_decoder_kia_v5_free(void* context) { + furi_assert(context); SubGhzProtocolDecoderKiaV5* instance = context; free(instance); } -void kia_protocol_decoder_v5_reset(void* context) { - furi_check(context); +void subghz_protocol_decoder_kia_v5_reset(void* context) { + furi_assert(context); SubGhzProtocolDecoderKiaV5* instance = context; instance->decoder.parser_step = KiaV5DecoderStepReset; instance->header_count = 0; @@ -696,31 +464,27 @@ void kia_protocol_decoder_v5_reset(void* context) { instance->manchester_state = ManchesterStateMid1; } -void kia_protocol_decoder_v5_feed(void* context, bool level, uint32_t duration) { - furi_check(context); +void subghz_protocol_decoder_kia_v5_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); SubGhzProtocolDecoderKiaV5* instance = context; switch(instance->decoder.parser_step) { case KiaV5DecoderStepReset: - if((level) && (DURATION_DIFF(duration, kia_protocol_v5_const.te_short) < - kia_protocol_v5_const.te_delta)) { + if((level) && (DURATION_DIFF(duration, subghz_protocol_kia_v5_const.te_short) < + subghz_protocol_kia_v5_const.te_delta)) { instance->decoder.parser_step = KiaV5DecoderStepCheckPreamble; instance->decoder.te_last = duration; instance->header_count = 1; instance->bit_count = 0; instance->decoded_data = 0; - manchester_advance( - instance->manchester_state, - ManchesterEventReset, - &instance->manchester_state, - NULL); + manchester_advance(instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL); } break; case KiaV5DecoderStepCheckPreamble: if(level) { - if(DURATION_DIFF(duration, kia_protocol_v5_const.te_long) < - kia_protocol_v5_const.te_delta) { + if(DURATION_DIFF(duration, subghz_protocol_kia_v5_const.te_long) < + subghz_protocol_kia_v5_const.te_delta) { if(instance->header_count > 40) { instance->decoder.parser_step = KiaV5DecoderStepData; instance->bit_count = 0; @@ -731,27 +495,27 @@ void kia_protocol_decoder_v5_feed(void* context, bool level, uint32_t duration) instance->decoder.te_last = duration; } } else if( - DURATION_DIFF(duration, kia_protocol_v5_const.te_short) < - kia_protocol_v5_const.te_delta) { + DURATION_DIFF(duration, subghz_protocol_kia_v5_const.te_short) < + subghz_protocol_kia_v5_const.te_delta) { instance->decoder.te_last = duration; } else { instance->decoder.parser_step = KiaV5DecoderStepReset; } } else { - if((DURATION_DIFF(duration, kia_protocol_v5_const.te_short) < - kia_protocol_v5_const.te_delta) && - (DURATION_DIFF(instance->decoder.te_last, kia_protocol_v5_const.te_short) < - kia_protocol_v5_const.te_delta)) { + if((DURATION_DIFF(duration, subghz_protocol_kia_v5_const.te_short) < + subghz_protocol_kia_v5_const.te_delta) && + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_v5_const.te_short) < + subghz_protocol_kia_v5_const.te_delta)) { instance->header_count++; } else if( - (DURATION_DIFF(duration, kia_protocol_v5_const.te_long) < - kia_protocol_v5_const.te_delta) && - (DURATION_DIFF(instance->decoder.te_last, kia_protocol_v5_const.te_short) < - kia_protocol_v5_const.te_delta)) { + (DURATION_DIFF(duration, subghz_protocol_kia_v5_const.te_long) < + subghz_protocol_kia_v5_const.te_delta) && + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_v5_const.te_short) < + subghz_protocol_kia_v5_const.te_delta)) { instance->header_count++; } else if( - DURATION_DIFF(instance->decoder.te_last, kia_protocol_v5_const.te_long) < - kia_protocol_v5_const.te_delta) { + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_v5_const.te_long) < + subghz_protocol_kia_v5_const.te_delta) { instance->header_count++; } else { instance->decoder.parser_step = KiaV5DecoderStepReset; @@ -763,21 +527,30 @@ void kia_protocol_decoder_v5_feed(void* context, bool level, uint32_t duration) case KiaV5DecoderStepData: { ManchesterEvent event; - if(DURATION_DIFF(duration, kia_protocol_v5_const.te_short) < - kia_protocol_v5_const.te_delta) { + if(DURATION_DIFF(duration, subghz_protocol_kia_v5_const.te_short) < + subghz_protocol_kia_v5_const.te_delta) { event = level ? ManchesterEventShortHigh : ManchesterEventShortLow; } else if( - DURATION_DIFF(duration, kia_protocol_v5_const.te_long) < - kia_protocol_v5_const.te_delta) { + DURATION_DIFF(duration, subghz_protocol_kia_v5_const.te_long) < + subghz_protocol_kia_v5_const.te_delta) { event = level ? ManchesterEventLongHigh : ManchesterEventLongLow; } else { - if(instance->bit_count >= kia_protocol_v5_const.min_count_bit_for_found) { + if(instance->bit_count >= subghz_protocol_kia_v5_const.min_count_bit_for_found) { instance->generic.data = instance->saved_key; - instance->generic.data_count_bit = 64; + instance->generic.data_count_bit = (instance->bit_count > 67) ? 67 : instance->bit_count; instance->crc = (uint8_t)(instance->decoded_data & 0x07); - instance->yek = bit_reverse_64(instance->generic.data); + instance->yek = 0; + for(int i = 0; i < 8; i++) { + uint8_t byte = (instance->generic.data >> (i * 8)) & 0xFF; + uint8_t reversed = 0; + for(int b = 0; b < 8; b++) { + if(byte & (1 << b)) + reversed |= (1 << (7 - b)); + } + instance->yek |= ((uint64_t)reversed << ((7 - i) * 8)); + } instance->generic.serial = (uint32_t)((instance->yek >> 32) & 0x0FFFFFFF); instance->generic.btn = (uint8_t)((instance->yek >> 60) & 0x0F); @@ -785,16 +558,13 @@ void kia_protocol_decoder_v5_feed(void* context, bool level, uint32_t duration) uint32_t encrypted = (uint32_t)(instance->yek & 0xFFFFFFFF); instance->generic.cnt = mixer_decode(encrypted); - KV5_LOG( - "RX: Key=%08lX%08lX Yek=%08lX%08lX Sn=%07lX Btn=%X Cnt=%04lX CRC=%X", - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data & 0xFFFFFFFF), - (uint32_t)(instance->yek >> 32), - (uint32_t)(instance->yek & 0xFFFFFFFF), - instance->generic.serial, - instance->generic.btn, - instance->generic.cnt, - instance->crc); + instance->decoder.decode_data = instance->generic.data; + instance->decoder.decode_count_bit = instance->generic.data_count_bit; + + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->generic.btn); + } + subghz_custom_btn_set_max(4); if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); @@ -806,10 +576,8 @@ void kia_protocol_decoder_v5_feed(void* context, bool level, uint32_t duration) bool data_bit; if(instance->bit_count <= 66 && - manchester_advance( - instance->manchester_state, event, &instance->manchester_state, &data_bit)) { + manchester_advance(instance->manchester_state, event, &instance->manchester_state, &data_bit)) { kia_v5_add_bit(instance, data_bit); - if(instance->bit_count == 64) { instance->saved_key = instance->decoded_data; instance->decoded_data = 0; @@ -822,63 +590,157 @@ void kia_protocol_decoder_v5_feed(void* context, bool level, uint32_t duration) } } -uint8_t kia_protocol_decoder_v5_get_hash_data(void* context) { - furi_check(context); +uint8_t subghz_protocol_decoder_kia_v5_get_hash_data(void* context) { + furi_assert(context); SubGhzProtocolDecoderKiaV5* instance = context; return subghz_protocol_blocks_get_hash_data( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -SubGhzProtocolStatus kia_protocol_decoder_v5_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_kia_v5_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { - furi_check(context); + furi_assert(context); SubGhzProtocolDecoderKiaV5* instance = context; - instance->generic.data_count_bit = 64; - SubGhzProtocolStatus ret = - subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + SubGhzProtocolStatus ret = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); if(ret == SubGhzProtocolStatusOk) { - flipper_format_write_uint32(flipper_format, "Serial", &instance->generic.serial, 1); + if(!flipper_format_write_uint32(flipper_format, "Serial", &instance->generic.serial, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + } + } + if(ret == SubGhzProtocolStatusOk) { uint32_t temp = instance->generic.btn; - flipper_format_write_uint32(flipper_format, "Btn", &temp, 1); - flipper_format_write_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1); + if(!flipper_format_write_uint32(flipper_format, "Btn", &temp, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + } + } + if(ret == SubGhzProtocolStatusOk) { + if(!flipper_format_write_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + } + } + if(ret == SubGhzProtocolStatusOk) { uint32_t crc_temp = instance->crc; - flipper_format_write_uint32(flipper_format, "CRC", &crc_temp, 1); + if(!flipper_format_write_uint32(flipper_format, "CRC", &crc_temp, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + } + } + if(ret == SubGhzProtocolStatusOk) { + uint32_t raw_high = (uint32_t)(instance->generic.data >> 32); + uint32_t raw_low = (uint32_t)(instance->generic.data & 0xFFFFFFFF); + if(!flipper_format_write_uint32(flipper_format, "DataHi", &raw_high, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + } + if(ret == SubGhzProtocolStatusOk) { + if(!flipper_format_write_uint32(flipper_format, "DataLo", &raw_low, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + } + } + } + if(ret == SubGhzProtocolStatusOk) { + uint32_t yek_high = (uint32_t)(instance->yek >> 32); + uint32_t yek_low = (uint32_t)(instance->yek & 0xFFFFFFFF); + if(!flipper_format_write_uint32(flipper_format, "YekHi", &yek_high, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + } + if(ret == SubGhzProtocolStatusOk) { + if(!flipper_format_write_uint32(flipper_format, "YekLo", &yek_low, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + } + } } return ret; } SubGhzProtocolStatus - kia_protocol_decoder_v5_deserialize(void* context, FlipperFormat* flipper_format) { - furi_check(context); + subghz_protocol_decoder_kia_v5_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); SubGhzProtocolDecoderKiaV5* instance = context; - return subghz_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, 64); + + SubGhzProtocolStatus ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + + if(ret == SubGhzProtocolStatusOk) { + if(instance->generic.data_count_bit < subghz_protocol_kia_v5_const.min_count_bit_for_found) { + ret = SubGhzProtocolStatusErrorParserBitCount; + } + } + + if(ret == SubGhzProtocolStatusOk) { + uint32_t temp_crc = 0; + if(flipper_format_read_uint32(flipper_format, "CRC", &temp_crc, 1)) { + instance->crc = temp_crc; + } + + uint32_t yek_high = 0, yek_low = 0; + if(flipper_format_read_uint32(flipper_format, "YekHi", &yek_high, 1) && + flipper_format_read_uint32(flipper_format, "YekLo", &yek_low, 1)) { + instance->yek = ((uint64_t)yek_high << 32) | yek_low; + } else { + instance->yek = 0; + for(int i = 0; i < 8; i++) { + uint8_t byte = (instance->generic.data >> (i * 8)) & 0xFF; + uint8_t reversed = 0; + for(int j = 0; j < 8; j++) { + if(byte & (1 << j)) { + reversed |= (1 << (7 - j)); + } + } + instance->yek |= ((uint64_t)reversed << ((7 - i) * 8)); + } + } + + instance->generic.serial = (uint32_t)((instance->yek >> 32) & 0x0FFFFFFF); + instance->generic.btn = (uint8_t)((instance->yek >> 60) & 0x0F); + + uint32_t encrypted = (uint32_t)(instance->yek & 0xFFFFFFFF); + instance->generic.cnt = mixer_decode(encrypted); + + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->generic.btn); + } + subghz_custom_btn_set_max(4); + } + + return ret; } -void kia_protocol_decoder_v5_get_string(void* context, FuriString* output) { - furi_check(context); +static const char* subghz_protocol_kia_v5_get_name_button(uint8_t btn) { + switch(btn) { + case 0x01: return "Unlock"; + case 0x02: return "Lock"; + case 0x04: return "Trunk"; + case 0x08: return "Horn"; + default: return "Unknown"; + } +} + +void subghz_protocol_decoder_kia_v5_get_string(void* context, FuriString* output) { + furi_assert(context); SubGhzProtocolDecoderKiaV5* instance = context; - if(subghz_custom_btn_get_original() == 0) - subghz_custom_btn_set_original(instance->generic.btn); - subghz_custom_btn_set_max(4); uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + uint32_t code_found_lo = instance->generic.data & 0xFFFFFFFF; uint32_t yek_hi = (uint32_t)(instance->yek >> 32); uint32_t yek_lo = (uint32_t)(instance->yek & 0xFFFFFFFF); + uint8_t calculated_crc = kia_v5_calculate_crc(instance->yek); + bool crc_valid = (instance->crc == calculated_crc); + + uint16_t seed = ((uint16_t)(instance->generic.btn & 0x0F) << 12) | + (instance->generic.serial & 0x0FFF); + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:%08lX%08lX\r\n" "Yek:%08lX%08lX\r\n" - "Sn:%07lX Btn:%X Cnt:%04lX\r\n" - "CRC:%X\r\n", + "Sn:%07lX Cnt:%04lX\r\n" + "Btn:%02X:[%s] Seed:%04X\r\n" + "CRC:%X %s", instance->generic.protocol_name, instance->generic.data_count_bit, code_found_hi, @@ -886,7 +748,10 @@ void kia_protocol_decoder_v5_get_string(void* context, FuriString* output) { yek_hi, yek_lo, instance->generic.serial, - instance->generic.btn, instance->generic.cnt, - instance->crc); + instance->generic.btn, + subghz_protocol_kia_v5_get_name_button(instance->generic.btn), + seed, + instance->crc, + crc_valid ? "(OK)" : "(FAIL)"); } From 2e5648f3f40bde412ff1e7ee3c80f0a3f5bb7507 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 11 Mar 2026 17:59:35 +0100 Subject: [PATCH 4/6] Refactor Kia V5 protocol functions and includes --- lib/subghz/protocols/kia_v5.h | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/subghz/protocols/kia_v5.h b/lib/subghz/protocols/kia_v5.h index 065a5f55..b0026619 100644 --- a/lib/subghz/protocols/kia_v5.h +++ b/lib/subghz/protocols/kia_v5.h @@ -1,36 +1,36 @@ #pragma once -#include "kia_generic.h" -#include +#include "base.h" +#include "../blocks/math.h" - -#define KIA_PROTOCOL_V5_NAME "Kia V5" +#define SUBGHZ_PROTOCOL_KIA_V5_NAME "KIA/HYU V5" typedef struct SubGhzProtocolDecoderKiaV5 SubGhzProtocolDecoderKiaV5; typedef struct SubGhzProtocolEncoderKiaV5 SubGhzProtocolEncoderKiaV5; -extern const SubGhzProtocolDecoder kia_protocol_v5_decoder; -extern const SubGhzProtocolEncoder kia_protocol_v5_encoder; extern const SubGhzProtocol subghz_protocol_kia_v5; -// Decoder functions -void* kia_protocol_decoder_v5_alloc(SubGhzEnvironment* environment); -void kia_protocol_decoder_v5_free(void* context); -void kia_protocol_decoder_v5_reset(void* context); -void kia_protocol_decoder_v5_feed(void* context, bool level, uint32_t duration); -uint8_t kia_protocol_decoder_v5_get_hash_data(void* context); -SubGhzProtocolStatus kia_protocol_decoder_v5_serialize( +void* subghz_protocol_decoder_kia_v5_alloc(SubGhzEnvironment* environment); +void subghz_protocol_decoder_kia_v5_free(void* context); +void subghz_protocol_decoder_kia_v5_reset(void* context); +void subghz_protocol_decoder_kia_v5_feed(void* context, bool level, uint32_t duration); +uint8_t subghz_protocol_decoder_kia_v5_get_hash_data(void* context); +SubGhzProtocolStatus subghz_protocol_decoder_kia_v5_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); SubGhzProtocolStatus - kia_protocol_decoder_v5_deserialize(void* context, FlipperFormat* flipper_format); -void kia_protocol_decoder_v5_get_string(void* context, FuriString* output); + subghz_protocol_decoder_kia_v5_deserialize(void* context, FlipperFormat* flipper_format); +void subghz_protocol_decoder_kia_v5_get_string(void* context, FuriString* output); -// Encoder functions -void* kia_protocol_encoder_v5_alloc(SubGhzEnvironment* environment); -void kia_protocol_encoder_v5_free(void* context); +void* subghz_protocol_encoder_kia_v5_alloc(SubGhzEnvironment* environment); +void subghz_protocol_encoder_kia_v5_free(void* context); SubGhzProtocolStatus - kia_protocol_encoder_v5_deserialize(void* context, FlipperFormat* flipper_format); -void kia_protocol_encoder_v5_stop(void* context); -LevelDuration kia_protocol_encoder_v5_yield(void* context); \ No newline at end of file + subghz_protocol_encoder_kia_v5_deserialize(void* context, FlipperFormat* flipper_format); +void subghz_protocol_encoder_kia_v5_stop(void* context); +LevelDuration subghz_protocol_encoder_kia_v5_yield(void* context); +void subghz_protocol_encoder_kia_v5_set_button(void* context, uint8_t button); +void subghz_protocol_encoder_kia_v5_set_counter(void* context, uint16_t counter); +void subghz_protocol_encoder_kia_v5_increment_counter(void* context); +uint16_t subghz_protocol_encoder_kia_v5_get_counter(void* context); +uint8_t subghz_protocol_encoder_kia_v5_get_button(void* context); From 0290f601a0a6ca65a2788167dc5d05407b3c7a3b Mon Sep 17 00:00:00 2001 From: David Date: Wed, 11 Mar 2026 18:47:07 +0100 Subject: [PATCH 5/6] Remove NULL function pointers from Kia V3/V4 protocol Removed unused function pointers from the Kia V3/V4 protocol structure. --- lib/subghz/protocols/kia_v3_v4.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/subghz/protocols/kia_v3_v4.c b/lib/subghz/protocols/kia_v3_v4.c index 36d90274..c0625794 100644 --- a/lib/subghz/protocols/kia_v3_v4.c +++ b/lib/subghz/protocols/kia_v3_v4.c @@ -185,11 +185,9 @@ const SubGhzProtocolDecoder subghz_protocol_kia_v3_v4_decoder = { .feed = subghz_protocol_decoder_kia_v3_v4_feed, .reset = subghz_protocol_decoder_kia_v3_v4_reset, .get_hash_data = subghz_protocol_decoder_kia_v3_v4_get_hash_data, - .get_hash_data_long = NULL, .serialize = subghz_protocol_decoder_kia_v3_v4_serialize, .deserialize = subghz_protocol_decoder_kia_v3_v4_deserialize, .get_string = subghz_protocol_decoder_kia_v3_v4_get_string, - .get_string_brief = NULL, }; const SubGhzProtocolEncoder subghz_protocol_kia_v3_v4_encoder = { From bfdf60944feb3795e493454dd486f07268914bae Mon Sep 17 00:00:00 2001 From: David Date: Wed, 11 Mar 2026 18:48:03 +0100 Subject: [PATCH 6/6] Remove unused fields from Kia V5 protocol --- lib/subghz/protocols/kia_v5.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/subghz/protocols/kia_v5.c b/lib/subghz/protocols/kia_v5.c index 6d6ee38d..0b931004 100644 --- a/lib/subghz/protocols/kia_v5.c +++ b/lib/subghz/protocols/kia_v5.c @@ -161,11 +161,9 @@ const SubGhzProtocolDecoder subghz_protocol_kia_v5_decoder = { .feed = subghz_protocol_decoder_kia_v5_feed, .reset = subghz_protocol_decoder_kia_v5_reset, .get_hash_data = subghz_protocol_decoder_kia_v5_get_hash_data, - .get_hash_data_long = NULL, .serialize = subghz_protocol_decoder_kia_v5_serialize, .deserialize = subghz_protocol_decoder_kia_v5_deserialize, .get_string = subghz_protocol_decoder_kia_v5_get_string, - .get_string_brief = NULL, }; const SubGhzProtocolEncoder subghz_protocol_kia_v5_encoder = {