Compare commits

...

1 Commits

Author SHA1 Message Date
d4rks1d33 4478f99dfe fixes
Build Dev Firmware / build (push) Failing after 14m50s
2026-06-05 20:46:07 -03:00
15 changed files with 1370 additions and 276 deletions
+2
View File
@@ -90,3 +90,5 @@ lib/subghz/protocols/honda_rolling.c
lib/subghz/protocols/honda_rolling.h
lib/subghz/protocols/honda_pandora.c
lib/subghz/protocols/honda_pandora.h
lib/subghz/protocols/toyota.c
lib/subghz/protocols/toyota.h
@@ -31,6 +31,7 @@ Protocols are split into **AM** and **FM** registries. The active registry is ch
| Fiat V0 | ✅ | ✅ | Manchester | AM650 | Rolling Code (static emu only) | ❌ | 315.00 / 433.92 |
| Fiat V1 | ✅ | ❌ | Manchester | AM650 | Rolling Code | CRC8 | 315.00 / 433.92 |
| Ford V0 | ✅ | ✅ | Manchester | AM650 | Rolling Code | ✅ + Checksum | 315.00 / 433.92 |
| Honda V1 | ✅ | ✅ | Manchester | AM650 | Rolling Code | CRC4 | 315.00 / 433.92 |
| Kia V1 | ✅ | ✅ | Manchester | AM650 | Rolling Code | CRC4 | 315.00 / 433.92 |
| Porsche Touareg | ✅ | ❌ | PWM | AM650 | Rolling Code | ❌ | 315.00 / 433.92 |
| PSA (Peugeot/Citroen) | ✅ | ✅ | Manchester | AM650 | XTEA/XOR | CRC8 | 315.00 / 433.92 |
@@ -6,7 +6,6 @@ App(
entry_point="protopirate_app",
requires=["gui"],
stack_size=4 * 1024,
sources=["*.c*"],
fap_description="Decode car key fob signals from Sub-GHz",
fap_version="3.0",
fap_icon="images/protopirate_10px.png",
@@ -28,7 +27,9 @@ App(
"protocols/fiat_v0.c",
"protocols/fiat_v1.c",
"protocols/ford_v0.c",
"protocols/honda_v1.c",
"protocols/kia_v1.c",
"protocols/kia_v2.c",
"protocols/porsche_touareg.c",
"protocols/psa.c",
"protocols/psa_crypto.c",
@@ -50,7 +51,6 @@ App(
"protocols/keys.c",
"protocols/scher_khan.c",
"protocols/kia_v0.c",
"protocols/kia_v2.c",
"protocols/kia_v3_v4.c",
"protocols/kia_v5.c",
"protocols/kia_v6.c",
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
//#define ENABLE_TIMING_TUNER_SCENE
#define ENABLE_TIMING_TUNER_SCENE
#define ENABLE_SUB_DECODE_SCENE
#define ENABLE_EMULATE_FEATURE
@@ -0,0 +1,697 @@
#include "honda_v1.h"
#include "protocols_common.h"
#include <string.h>
#define HONDA_V1_BIT_COUNT 68
#define HONDA_V1_TE_SHORT 1000
#define HONDA_V1_TE_LONG 2000
#define HONDA_V1_TE_DELTA 400
#define HONDA_V1_TE_SHORT_MIN 600
#define HONDA_V1_TE_END 3500
#define HONDA_V1_VALID_MAX 0x4B
#define HONDA_V1_NIBBLE_MASK 0x0FU
#define HONDA_V1_SERIAL_MASK 0x0FFFFFFFU
#define HONDA_V1_COUNTER_MASK 0xFFFFU
#define HONDA_V1_LOW32_MASK 0xFFFFFFFFULL
#define HONDA_V1_BUTTON_MAX 10U
#define HONDA_V1_BUTTON_VALID_MASK 0x701U
#define HONDA_V1_BUTTON_FALLBACK_CODE 0x00088888U
#define HONDA_V1_UPLOAD_CAPACITY 2048U
#define HONDA_V1_PREAMBLE_UPLOAD_COUNT 180U
#define HONDA_V1_FRAME_SYMBOLS 80U
#define HONDA_V1_FRAME_START 12U
#define HONDA_V1_FRAME_SYNC_DROP 2U
#define HONDA_V1_FRAME_REPEAT_PER_CRC 2U
#define HONDA_V1_FRAME_BYTES 9U
#define HONDA_V1_FRAME_CRC_INDEX 8U
#define HONDA_V1_FRAME_GAP_US 5000U
#define HONDA_V1_FRAME_GENERATED_MAX (HONDA_V1_FRAME_SYMBOLS * 2U)
#define HONDA_V1_FRAME_TAIL_MAX 3U
#define HONDA_V1_DECODE_BUFFER_BYTES 12U
#define HONDA_V1_KEY_BYTES 8U
#define HONDA_V1_CRC_FIELD "Crc"
#define HONDA_V1_KEY_2_FIELD "Key_2"
_Static_assert(
HONDA_V1_UPLOAD_CAPACITY <= PP_SHARED_UPLOAD_CAPACITY,
"HONDA_V1_UPLOAD_CAPACITY exceeds shared upload slab");
_Static_assert(
HONDA_V1_PREAMBLE_UPLOAD_COUNT < HONDA_V1_UPLOAD_CAPACITY,
"HONDA_V1 preamble exceeds upload slab");
_Static_assert(
HONDA_V1_FRAME_SYNC_DROP < HONDA_V1_FRAME_GENERATED_MAX,
"HONDA_V1 frame sync drop exceeds generated frame");
typedef enum {
HondaV1DecoderStepReset = 0,
HondaV1DecoderStepPreamble,
HondaV1DecoderStepData,
} HondaV1DecoderStep;
typedef enum {
HondaV1ButtonUnlock = 0,
HondaV1ButtonLock = 8,
HondaV1ButtonTrunk = 9,
HondaV1ButtonPanic = 10,
} HondaV1Button;
static const char* const honda_v1_button_names[HONDA_V1_BUTTON_MAX + 1U] = {
[HondaV1ButtonUnlock] = "Unlock",
[HondaV1ButtonLock] = "Lock",
[HondaV1ButtonTrunk] = "Trunk",
[HondaV1ButtonPanic] = "Panic",
};
static const uint32_t honda_v1_button_codes[HONDA_V1_BUTTON_MAX + 1U] = {
[HondaV1ButtonUnlock] = 0x00080808,
[HondaV1ButtonLock] = 0x00088888,
[HondaV1ButtonTrunk] = 0x00099190,
[HondaV1ButtonPanic] = 0x000FA7A0,
};
struct SubGhzProtocolDecoderHondaV1 {
SubGhzProtocolDecoderBase base;
SubGhzBlockGeneric generic;
uint8_t step;
uint8_t preamble_count;
bool preamble_has_long;
bool data_pending;
bool last_level;
uint8_t bits[HONDA_V1_DECODE_BUFFER_BYTES];
uint8_t bit_count;
uint32_t pending;
bool pending_valid;
uint8_t k2;
};
#ifdef ENABLE_EMULATE_FEATURE
struct SubGhzProtocolEncoderHondaV1 {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
uint8_t k2;
};
#endif
static bool honda_v1_button_valid(uint8_t b) {
if(b > HONDA_V1_BUTTON_MAX) return false;
return ((HONDA_V1_BUTTON_VALID_MASK >> b) & 1U) != 0U;
}
static const char* honda_v1_button_name(uint8_t b) {
if((b < COUNT_OF(honda_v1_button_names)) && (honda_v1_button_names[b] != NULL)) {
return honda_v1_button_names[b];
}
return "Unknown";
}
static uint32_t honda_v1_button_code(uint8_t button) {
if(!honda_v1_button_valid(button)) {
return HONDA_V1_BUTTON_FALLBACK_CODE;
}
return honda_v1_button_codes[button];
}
static bool honda_v1_duration_is(uint32_t d, uint32_t t) {
return (d >= t) ? ((d - t) <= HONDA_V1_TE_DELTA) : ((t - d) <= HONDA_V1_TE_DELTA);
}
static uint8_t honda_v1_crc_fold(uint16_t v) {
const uint8_t lo = (uint8_t)(v & HONDA_V1_NIBBLE_MASK);
const uint16_t hi = (uint16_t)(v >> 4U);
int32_t s = (hi & 1U) ? (int32_t)lo : -(int32_t)lo;
uint8_t out = (uint8_t)((s - (int32_t)hi) & 7);
out |= (uint8_t)(((v >> 3U) & 1U) << 3U);
if(((v >> 1U) & 1U) && (((v >> 4U) ^ (v >> 5U)) & 1U)) {
out ^= 0x04U;
}
return (uint8_t)(out & HONDA_V1_NIBBLE_MASK);
}
static uint8_t honda_v1_checksum_base(uint64_t data) {
const uint8_t a = honda_v1_crc_fold((uint16_t)(data & HONDA_V1_COUNTER_MASK));
const uint8_t b = honda_v1_crc_fold((uint8_t)((data >> 40U) & 0xFFU));
return (uint8_t)((a ^ b ^ 1U) & HONDA_V1_NIBBLE_MASK);
}
static uint8_t honda_v1_checksum_alternate(uint8_t checksum) {
uint8_t mask = 0x09U;
if((checksum & 1U) == 0U) {
mask = (checksum & 2U) ? 0x0BU : HONDA_V1_NIBBLE_MASK;
}
return (uint8_t)((checksum ^ mask) & HONDA_V1_NIBBLE_MASK);
}
static void honda_v1_checksum_wire_order(uint64_t data, uint8_t* first, uint8_t* second) {
const uint8_t checksum = honda_v1_checksum_base(data);
const uint8_t other = honda_v1_checksum_alternate(checksum);
if((checksum & 0x08U) != 0U) {
*first = other;
*second = checksum;
} else {
*first = checksum;
*second = other;
}
}
static bool honda_v1_crc_valid(uint64_t data, uint8_t crc) {
uint8_t first = 0U;
uint8_t second = 0U;
honda_v1_checksum_wire_order(data, &first, &second);
crc &= HONDA_V1_NIBBLE_MASK;
return (crc == first) || (crc == second);
}
static void honda_v1_decode_fields(SubGhzBlockGeneric* generic) {
const uint32_t low = (uint32_t)(generic->data & HONDA_V1_LOW32_MASK);
generic->serial = (uint32_t)((generic->data >> 36U) & HONDA_V1_SERIAL_MASK);
generic->btn = (uint8_t)((low >> 28U) & HONDA_V1_NIBBLE_MASK);
generic->cnt = low & HONDA_V1_COUNTER_MASK;
generic->data_count_bit = HONDA_V1_BIT_COUNT;
}
static uint64_t honda_v1_build_key(uint32_t serial, uint8_t button, uint16_t counter) {
const uint32_t table = honda_v1_button_code(button);
const uint32_t low = ((table & HONDA_V1_COUNTER_MASK) << 16U) | counter;
const uint32_t high = ((serial & HONDA_V1_SERIAL_MASK) << 4U) | (table >> 16U);
return ((uint64_t)high << 32U) | low;
}
static void honda_v1_state_reset(SubGhzProtocolDecoderHondaV1* instance) {
instance->step = HondaV1DecoderStepReset;
instance->preamble_count = 0U;
instance->preamble_has_long = false;
instance->data_pending = false;
instance->last_level = false;
instance->bit_count = 0U;
memset(instance->bits, 0, sizeof(instance->bits));
}
static void honda_v1_add_bit(SubGhzProtocolDecoderHondaV1* instance, bool bit) {
if(instance->bit_count > HONDA_V1_VALID_MAX) return;
if(bit) {
instance->bits[instance->bit_count >> 3U] |=
(uint8_t)(1U << (((uint8_t)~instance->bit_count) & 0x07U));
}
instance->bit_count++;
}
static bool honda_v1_commit(SubGhzProtocolDecoderHondaV1* instance) {
if(instance->bit_count < HONDA_V1_BIT_COUNT) return false;
uint8_t aligned[sizeof(instance->bits)];
memcpy(aligned, instance->bits, sizeof(aligned));
uint8_t shift_count = instance->bit_count - HONDA_V1_BIT_COUNT;
if(shift_count < 1U) shift_count = 1U;
for(uint8_t shift = 0U; shift < shift_count; shift++) {
for(size_t i = 0; i < sizeof(aligned) - 1U; i++) {
aligned[i] = (uint8_t)((aligned[i] << 1U) | (aligned[i + 1U] >> 7U));
}
aligned[sizeof(aligned) - 1U] <<= 1U;
}
const uint8_t button = (uint8_t)(aligned[4] >> 4U);
if(!honda_v1_button_valid(button)) return false;
instance->generic.data = pp_bytes_to_u64_be(aligned);
instance->k2 = (uint8_t)(aligned[8] >> 4U);
honda_v1_decode_fields(&instance->generic);
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
return true;
}
static void honda_v1_symbol(SubGhzProtocolDecoderHondaV1* instance, bool level, uint32_t duration) {
const bool sh = honda_v1_duration_is(duration, HONDA_V1_TE_SHORT);
const bool lg = honda_v1_duration_is(duration, HONDA_V1_TE_LONG);
if(!sh && !lg) {
if(!level && (duration > HONDA_V1_TE_END) &&
(instance->step == HondaV1DecoderStepData)) {
honda_v1_commit(instance);
}
honda_v1_state_reset(instance);
return;
}
if(instance->step == HondaV1DecoderStepReset) {
if(level) {
instance->step = HondaV1DecoderStepPreamble;
instance->preamble_count = 1U;
instance->last_level = level;
}
return;
}
if(instance->step == HondaV1DecoderStepPreamble) {
if(lg) {
if(instance->preamble_count < 0xFFU) instance->preamble_count++;
instance->preamble_has_long = true;
instance->last_level = level;
return;
}
if(sh) {
if(instance->preamble_has_long && (instance->preamble_count > 5U)) {
instance->step = HondaV1DecoderStepData;
instance->bit_count = 0U;
memset(instance->bits, 0, sizeof(instance->bits));
instance->data_pending = true;
instance->last_level = level;
return;
}
if(instance->preamble_count < 0xFFU) instance->preamble_count++;
instance->last_level = level;
return;
}
honda_v1_state_reset(instance);
return;
}
if(sh) {
if(instance->data_pending) {
honda_v1_add_bit(instance, level);
instance->data_pending = false;
instance->last_level = level;
return;
}
instance->data_pending = true;
instance->last_level = level;
} else {
if(instance->data_pending) {
honda_v1_add_bit(instance, level);
} else {
honda_v1_add_bit(instance, instance->last_level);
}
instance->last_level = level;
}
}
#ifdef ENABLE_EMULATE_FEATURE
static bool honda_v1_append_frame(
SubGhzProtocolEncoderHondaV1* instance,
size_t* index,
const uint8_t frame[HONDA_V1_FRAME_BYTES]) {
LevelDuration* upload = instance->encoder.upload;
LevelDuration generated[HONDA_V1_FRAME_GENERATED_MAX];
size_t generated_count = 0U;
for(uint32_t bit_index = 0U; bit_index < HONDA_V1_FRAME_SYMBOLS; bit_index++) {
uint32_t bit;
if(bit_index >= HONDA_V1_FRAME_START) {
const uint32_t data_index = (bit_index - HONDA_V1_FRAME_START) >> 3U;
const uint8_t shift = (uint8_t)((11U - bit_index) & 0x07U);
bit = (frame[data_index] >> shift) & 0x01U;
} else {
bit = ((uint32_t)~bit_index) & 0x01U;
}
generated_count = pp_emit_merge(
generated,
generated_count,
COUNT_OF(generated),
bit != 0U,
HONDA_V1_TE_SHORT);
generated_count = pp_emit_merge(
generated,
generated_count,
COUNT_OF(generated),
bit == 0U,
HONDA_V1_TE_SHORT);
}
if(generated_count <= HONDA_V1_FRAME_SYNC_DROP) {
return false;
}
const size_t copy_count = generated_count - HONDA_V1_FRAME_SYNC_DROP;
if((*index + copy_count + HONDA_V1_FRAME_TAIL_MAX) > HONDA_V1_UPLOAD_CAPACITY) {
return false;
}
memcpy(
&upload[*index],
&generated[HONDA_V1_FRAME_SYNC_DROP],
copy_count * sizeof(LevelDuration));
*index += copy_count;
const bool tail_level = !level_duration_get_level(upload[*index - 1U]);
*index = pp_emit(upload, *index, HONDA_V1_UPLOAD_CAPACITY, tail_level, HONDA_V1_TE_SHORT);
if(!tail_level) {
*index = pp_emit(upload, *index, HONDA_V1_UPLOAD_CAPACITY, true, HONDA_V1_TE_SHORT);
}
*index = pp_emit(upload, *index, HONDA_V1_UPLOAD_CAPACITY, false, HONDA_V1_FRAME_GAP_US);
return true;
}
static bool honda_v1_build_upload(SubGhzProtocolEncoderHondaV1* instance) {
furi_check(instance);
LevelDuration* upload = instance->encoder.upload;
if(upload == NULL) return false;
uint8_t frame[HONDA_V1_FRAME_BYTES] = {0};
uint8_t first = 0U;
uint8_t second = 0U;
size_t index = 0U;
index = pp_emit_short_pairs(
upload,
index,
HONDA_V1_UPLOAD_CAPACITY,
HONDA_V1_TE_SHORT,
HONDA_V1_PREAMBLE_UPLOAD_COUNT / 2U);
if(index != HONDA_V1_PREAMBLE_UPLOAD_COUNT) {
return false;
}
upload[index - 1U] = level_duration_make(false, HONDA_V1_FRAME_GAP_US);
pp_u64_to_bytes_be(instance->generic.data, frame);
honda_v1_checksum_wire_order(instance->generic.data, &first, &second);
const uint8_t crc_order[] = {first, second};
for(size_t crc_index = 0U; crc_index < COUNT_OF(crc_order); crc_index++) {
frame[HONDA_V1_FRAME_CRC_INDEX] = (uint8_t)(crc_order[crc_index] << 4U);
for(size_t repeat = 0U; repeat < HONDA_V1_FRAME_REPEAT_PER_CRC; repeat++) {
if(!honda_v1_append_frame(instance, &index, frame)) {
return false;
}
}
}
instance->k2 = second;
instance->encoder.front = 0U;
instance->encoder.size_upload = index;
return true;
}
#endif
const SubGhzProtocolDecoder subghz_protocol_honda_v1_decoder = {
.alloc = subghz_protocol_decoder_honda_v1_alloc,
.free = pp_decoder_free_default,
.feed = subghz_protocol_decoder_honda_v1_feed,
.reset = subghz_protocol_decoder_honda_v1_reset,
.get_hash_data = subghz_protocol_decoder_honda_v1_get_hash_data,
.serialize = subghz_protocol_decoder_honda_v1_serialize,
.deserialize = subghz_protocol_decoder_honda_v1_deserialize,
.get_string = subghz_protocol_decoder_honda_v1_get_string,
};
#ifdef ENABLE_EMULATE_FEATURE
const SubGhzProtocolEncoder subghz_protocol_honda_v1_encoder = {
.alloc = subghz_protocol_encoder_honda_v1_alloc,
.free = pp_encoder_free,
.deserialize = subghz_protocol_encoder_honda_v1_deserialize,
.stop = pp_encoder_stop,
.yield = pp_encoder_yield,
};
#else
const SubGhzProtocolEncoder subghz_protocol_honda_v1_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
#endif
const SubGhzProtocol honda_v1_protocol = {
.name = HONDA_V1_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Load
#ifdef ENABLE_EMULATE_FEATURE
| SubGhzProtocolFlag_Send
#endif
,
.decoder = &subghz_protocol_honda_v1_decoder,
.encoder = &subghz_protocol_honda_v1_encoder,
};
#ifdef ENABLE_EMULATE_FEATURE
void* subghz_protocol_encoder_honda_v1_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderHondaV1* instance = malloc(sizeof(SubGhzProtocolEncoderHondaV1));
furi_check(instance);
memset(instance, 0, sizeof(*instance));
instance->base.protocol = &honda_v1_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 1U;
pp_encoder_buffer_ensure(instance, HONDA_V1_UPLOAD_CAPACITY);
return instance;
}
SubGhzProtocolStatus
subghz_protocol_encoder_honda_v1_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolEncoderHondaV1* instance = context;
instance->encoder.is_running = false;
instance->encoder.front = 0U;
if(pp_verify_protocol_name(flipper_format, instance->base.protocol->name) !=
SubGhzProtocolStatusOk) {
return SubGhzProtocolStatusError;
}
SubGhzProtocolStatus status = subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, HONDA_V1_BIT_COUNT);
if(status != SubGhzProtocolStatusOk) {
return status;
}
instance->generic.protocol_name = instance->base.protocol->name;
honda_v1_decode_fields(&instance->generic);
uint32_t serial = instance->generic.serial;
uint32_t btn = instance->generic.btn;
uint32_t cnt = instance->generic.cnt;
pp_encoder_read_fields(flipper_format, &serial, &btn, &cnt, NULL);
serial &= HONDA_V1_SERIAL_MASK;
uint8_t button = (uint8_t)(btn & HONDA_V1_NIBBLE_MASK);
if(!honda_v1_button_valid(button)) {
button = (uint8_t)instance->generic.btn;
}
if(!honda_v1_button_valid(button)) {
button = HondaV1ButtonUnlock;
}
instance->generic.serial = serial;
instance->generic.btn = button;
instance->generic.cnt = cnt & HONDA_V1_COUNTER_MASK;
instance->generic.data_count_bit = HONDA_V1_BIT_COUNT;
instance->generic.data =
honda_v1_build_key(instance->generic.serial, instance->generic.btn, instance->generic.cnt);
uint8_t first = 0U;
uint8_t second = 0U;
honda_v1_checksum_wire_order(instance->generic.data, &first, &second);
instance->k2 = second;
uint8_t key_data[HONDA_V1_KEY_BYTES];
pp_u64_to_bytes_be(instance->generic.data, key_data);
flipper_format_rewind(flipper_format);
bool key_written = flipper_format_update_hex(flipper_format, FF_KEY, key_data, sizeof(key_data));
if(!key_written) {
flipper_format_rewind(flipper_format);
key_written =
flipper_format_insert_or_update_hex(flipper_format, FF_KEY, key_data, sizeof(key_data));
}
if(!key_written) {
return SubGhzProtocolStatusErrorParserKey;
}
pp_flipper_update_or_insert_u32(flipper_format, FF_SERIAL, instance->generic.serial);
pp_flipper_update_or_insert_u32(flipper_format, FF_BTN, instance->generic.btn);
pp_flipper_update_or_insert_u32(flipper_format, FF_CNT, instance->generic.cnt);
pp_flipper_update_or_insert_u32(flipper_format, HONDA_V1_CRC_FIELD, instance->k2);
pp_flipper_update_or_insert_u32(flipper_format, HONDA_V1_KEY_2_FIELD, instance->k2);
instance->encoder.repeat = pp_encoder_read_repeat(flipper_format, 1U);
if(!honda_v1_build_upload(instance)) {
return SubGhzProtocolStatusErrorEncoderGetUpload;
}
instance->encoder.is_running = true;
return SubGhzProtocolStatusOk;
}
#endif
void* subghz_protocol_decoder_honda_v1_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderHondaV1* instance = malloc(sizeof(SubGhzProtocolDecoderHondaV1));
furi_check(instance);
memset(instance, 0, sizeof(*instance));
instance->base.protocol = &honda_v1_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_honda_v1_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderHondaV1* instance = context;
instance->pending = 0U;
instance->pending_valid = false;
honda_v1_state_reset(instance);
}
void subghz_protocol_decoder_honda_v1_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderHondaV1* instance = context;
if(duration < HONDA_V1_TE_DELTA) {
instance->pending += duration;
instance->pending_valid = true;
return;
}
if(instance->pending_valid) {
const uint32_t p = instance->pending;
if(level) {
instance->pending = p + duration;
instance->pending_valid = true;
return;
}
if(p >= HONDA_V1_TE_SHORT_MIN) honda_v1_symbol(instance, true, p);
instance->pending = 0U;
instance->pending_valid = false;
}
if(level) {
instance->pending = duration;
instance->pending_valid = true;
return;
}
honda_v1_symbol(instance, false, duration);
}
uint8_t subghz_protocol_decoder_honda_v1_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderHondaV1* instance = context;
const uint64_t data = instance->generic.data;
return (uint8_t)(data ^ (data >> 8U) ^ (data >> 16U) ^ (data >> 24U) ^ (data >> 32U) ^
(data >> 40U) ^ (data >> 48U) ^ (data >> 56U));
}
SubGhzProtocolStatus subghz_protocol_decoder_honda_v1_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderHondaV1* instance = context;
honda_v1_decode_fields(&instance->generic);
SubGhzProtocolStatus status =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(status != SubGhzProtocolStatusOk) {
return status;
}
status = pp_serialize_fields(
flipper_format,
PP_FIELD_SERIAL | PP_FIELD_BTN | PP_FIELD_CNT,
instance->generic.serial,
instance->generic.btn,
instance->generic.cnt,
0);
if(status != SubGhzProtocolStatusOk) {
return status;
}
uint32_t crc = instance->k2 & HONDA_V1_NIBBLE_MASK;
if(!flipper_format_write_uint32(flipper_format, HONDA_V1_CRC_FIELD, &crc, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
if(!flipper_format_write_uint32(flipper_format, HONDA_V1_KEY_2_FIELD, &crc, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
return pp_write_display(
flipper_format, instance->generic.protocol_name, honda_v1_button_name(instance->generic.btn));
}
SubGhzProtocolStatus
subghz_protocol_decoder_honda_v1_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderHondaV1* instance = context;
SubGhzProtocolStatus status = subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, HONDA_V1_BIT_COUNT);
if(status != SubGhzProtocolStatusOk) {
return status;
}
flipper_format_rewind(flipper_format);
uint32_t crc = 0U;
bool crc_found = flipper_format_read_uint32(flipper_format, HONDA_V1_KEY_2_FIELD, &crc, 1);
if(!crc_found) {
flipper_format_rewind(flipper_format);
crc_found = flipper_format_read_uint32(flipper_format, HONDA_V1_CRC_FIELD, &crc, 1);
}
if(crc_found) {
instance->k2 = (uint8_t)(crc & HONDA_V1_NIBBLE_MASK);
} else {
uint8_t first = 0U;
uint8_t second = 0U;
honda_v1_checksum_wire_order(instance->generic.data, &first, &second);
instance->k2 = first;
}
instance->generic.protocol_name = instance->base.protocol->name;
honda_v1_decode_fields(&instance->generic);
return status;
}
void subghz_protocol_decoder_honda_v1_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderHondaV1* instance = context;
honda_v1_decode_fields(&instance->generic);
const uint8_t k2 = instance->k2 & HONDA_V1_NIBBLE_MASK;
const bool crc_ok = honda_v1_crc_valid(instance->generic.data, k2);
furi_string_printf(
output,
"%s %dbit\r\n"
"Key:%016llX\r\n"
"Btn:%s\r\n"
"Sn:%07lX Cnt:%04lX\r\n"
"Crc:%X [%s]",
instance->generic.protocol_name,
(int)instance->generic.data_count_bit,
(unsigned long long)instance->generic.data,
honda_v1_button_name((uint8_t)instance->generic.btn),
(unsigned long)instance->generic.serial,
(unsigned long)instance->generic.cnt,
k2,
crc_ok ? "OK" : "ERR");
}
@@ -0,0 +1,36 @@
#pragma once
#include <furi.h>
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/types.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include <lib/subghz/blocks/generic.h>
#include <flipper_format/flipper_format.h>
#include "../defines.h"
#define HONDA_V1_PROTOCOL_NAME "Honda V1"
typedef struct SubGhzProtocolDecoderHondaV1 SubGhzProtocolDecoderHondaV1;
typedef struct SubGhzProtocolEncoderHondaV1 SubGhzProtocolEncoderHondaV1;
extern const SubGhzProtocol honda_v1_protocol;
void* subghz_protocol_decoder_honda_v1_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_honda_v1_reset(void* context);
void subghz_protocol_decoder_honda_v1_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_honda_v1_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_honda_v1_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_honda_v1_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_honda_v1_get_string(void* context, FuriString* output);
#ifdef ENABLE_EMULATE_FEATURE
void* subghz_protocol_encoder_honda_v1_alloc(SubGhzEnvironment* environment);
SubGhzProtocolStatus
subghz_protocol_encoder_honda_v1_deserialize(void* context, FlipperFormat* flipper_format);
#endif
@@ -73,7 +73,7 @@ const SubGhzProtocolEncoder kia_protocol_v2_encoder = {
const SubGhzProtocol kia_protocol_v2 = {
.name = KIA_PROTOCOL_V2_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
SubGhzProtocolFlag_Send,
.decoder = &kia_protocol_v2_decoder,
@@ -4,18 +4,22 @@
#include "../fiat_v1.h"
#include "../ford_v0.h"
#include "../kia_v1.h"
#include "../kia_v2.h"
#include "../porsche_touareg.h"
#include "../psa.h"
#include "../subaru.h"
#include "../vag.h"
#include "../star_line.h"
#include "../honda_v1.h"
static const SubGhzProtocol* const protopirate_protocol_registry_am_items[] = {
&chrysler_protocol_v0,
&fiat_protocol_v0,
&fiat_v1_protocol,
&ford_protocol_v0,
&honda_v1_protocol,
&kia_protocol_v1,
&kia_protocol_v2,
&porsche_touareg_protocol,
&psa_protocol,
&subaru_protocol,
@@ -1,7 +1,6 @@
#include "../protopirate_protocol_plugins.h"
#include "../scher_khan.h"
#include "../kia_v0.h"
#include "../kia_v2.h"
#include "../kia_v3_v4.h"
#include "../kia_v5.h"
#include "../kia_v6.h"
@@ -18,7 +17,6 @@
static const SubGhzProtocol* const protopirate_protocol_registry_fm_items[] = {
&subghz_protocol_scher_khan,
&kia_protocol_v0,
&kia_protocol_v2,
&kia_protocol_v3_v4,
&kia_protocol_v5,
&kia_protocol_v6,
@@ -83,6 +83,14 @@ static const ProtoPirateProtocolTiming protocol_timings[] = {
.te_delta = 120,
.min_count_bit = 64,
},
// Honda V1: Manchester 1000/2000µs
{
.name = HONDA_V1_PROTOCOL_NAME,
.te_short = 1000,
.te_long = 2000,
.te_delta = 400,
.min_count_bit = 64,
},
// Kia V0: PWM 250/500µs — Kia 61bit, Suzuki 64bit, Honda V0 72bit
{
.name = "Kia V0",
@@ -27,6 +27,7 @@
#include "star_line.h"
#include "psa.h"
#include "honda_static.h"
#include "honda_v1.h"
typedef enum {
ProtoPirateProtocolRegistryFilterAM = 0,
@@ -473,6 +473,14 @@ static uint8_t emu_button_for_protocol(
case InputKeyRight: return 0x3; // Un+Lk combo
default: return original;
}
} else if(strstr(protocol, "Honda V1")) {
switch(key) {
case InputKeyUp: return 0x08; // Lock
case InputKeyOk: return 0x00; // Unlock
case InputKeyDown: return 0x09; // Trunk
case InputKeyLeft: return 0x0A; // Panic
default: return original;
}
} else if(strstr(protocol, "Honda Static")) {
switch(key) {
case InputKeyUp: return 0x1; // Lock
+1
View File
@@ -85,6 +85,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
&ford_protocol_v2,
&ford_protocol_v3,
&subghz_protocol_land_rover_v0,
//&subghz_protocol_toyota,
};
+1
View File
@@ -86,3 +86,4 @@
#include "ford_v2.h"
#include "ford_v3.h"
#include "land_rover_v0.h"
//#include "toyota.h"
+607 -270
View File
@@ -12,297 +12,50 @@
static const SubGhzBlockConst subghz_protocol_subaru_const = {
.te_short = 800,
.te_long = 1600,
.te_delta = 200,
.te_delta = 250,
.min_count_bit_for_found = 64,
};
#define SUBARU_PREAMBLE_PAIRS 75
#define SUBARU_GAP_US 2800
#define SUBARU_SYNC_US 2800
#define SUBARU_UPLOAD_CAPACITY 400
/* ============================================================
* STRUCT DEFINITIONS (NO typedef matches subaru.h forward)
* ============================================================ */
struct SubGhzProtocolDecoderSubaru {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint16_t header_count;
uint16_t bit_count;
uint8_t data[8];
uint64_t key;
uint32_t serial;
uint8_t btn;
uint16_t cnt;
uint8_t button;
uint16_t count;
};
struct SubGhzProtocolEncoderSubaru {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
uint64_t key;
uint32_t serial;
uint8_t btn;
uint16_t cnt;
uint8_t button;
uint16_t count;
};
/* ============================================================
* HELPERS
* ============================================================ */
typedef enum {
SubaruDecoderStepReset = 0,
SubaruDecoderStepCheckPreamble,
SubaruDecoderStepFoundGap,
SubaruDecoderStepFoundSync,
SubaruDecoderStepSaveDuration,
SubaruDecoderStepCheckDuration,
} SubaruDecoderStep;
static const char* subaru_get_button_name(uint8_t btn) {
switch(btn & 0x03) {
case 0: return "Lock";
case 1: return "Unlock";
case 2: return "Trunk";
case 3: return "Panic";
default: return "??";
}
}
static void subaru_rotate_left_3(uint8_t* a, uint8_t* b, uint8_t* c, uint8_t count) {
for(uint8_t i = 0; i < count; i++) {
uint8_t t = *a;
*a = (*a << 1) | (*b >> 7);
*b = (*b << 1) | (*c >> 7);
*c = (*c << 1) | (t >> 7);
}
}
static void subaru_decode_fields(
const uint8_t* kb,
uint32_t* serial,
uint8_t* btn,
uint16_t* cnt) {
*btn = kb[0] & 0x0F;
*serial = ((uint32_t)kb[1] << 16) |
((uint32_t)kb[2] << 8) |
kb[3];
uint8_t lo = 0;
if(!(kb[4] & 0x40)) lo |= 0x01;
if(!(kb[4] & 0x80)) lo |= 0x02;
if(!(kb[5] & 0x01)) lo |= 0x04;
if(!(kb[5] & 0x02)) lo |= 0x08;
if(!(kb[6] & 0x01)) lo |= 0x10;
if(!(kb[6] & 0x02)) lo |= 0x20;
if(!(kb[5] & 0x40)) lo |= 0x40;
if(!(kb[5] & 0x80)) lo |= 0x80;
uint8_t reg1 =
((kb[7] & 0x0F) << 4) |
(kb[5] & 0x0C) |
((kb[6] >> 6) & 0x03);
uint8_t reg2 =
((kb[6] & 0x3C) << 2) |
((kb[7] >> 4) & 0x0F);
uint8_t s0 = kb[3];
uint8_t s1 = kb[1];
uint8_t s2 = kb[2];
subaru_rotate_left_3(&s0, &s1, &s2, 4 + lo);
uint8_t t1 = s1 ^ reg1;
uint8_t t2 = s2 ^ reg2;
uint8_t hi = 0;
if(!(t1 & 0x10)) hi |= 0x04;
if(!(t1 & 0x20)) hi |= 0x08;
if(!(t2 & 0x80)) hi |= 0x02;
if(!(t2 & 0x40)) hi |= 0x01;
if(!(t1 & 0x01)) hi |= 0x40;
if(!(t1 & 0x02)) hi |= 0x80;
if(!(t2 & 0x08)) hi |= 0x20;
if(!(t2 & 0x04)) hi |= 0x10;
*cnt = ((uint16_t)hi << 8) | lo;
}
/* ============================================================
* PROTOCOL TABLE
* ============================================================ */
const SubGhzProtocolDecoder subghz_protocol_subaru_decoder;
const SubGhzProtocolEncoder subghz_protocol_subaru_encoder;
const SubGhzProtocol subghz_protocol_subaru = {
.name = SUBGHZ_PROTOCOL_SUBARU_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 |
SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load |
SubGhzProtocolFlag_Save |
SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_subaru_decoder,
.encoder = &subghz_protocol_subaru_encoder,
};
/* ============================================================
* DECODER IMPLEMENTATION
* ============================================================ */
void* subghz_protocol_decoder_subaru_alloc(SubGhzEnvironment* env) {
UNUSED(env);
SubGhzProtocolDecoderSubaru* i = malloc(sizeof(SubGhzProtocolDecoderSubaru));
i->base.protocol = &subghz_protocol_subaru;
i->generic.protocol_name = i->base.protocol->name;
return i;
}
void subghz_protocol_decoder_subaru_free(void* ctx) {
free(ctx);
}
void subghz_protocol_decoder_subaru_reset(void* ctx) {
SubGhzProtocolDecoderSubaru* i = ctx;
i->decoder.parser_step = 0;
i->decoder.decode_data = 0;
i->decoder.decode_count_bit = 0;
i->header_count = 0;
}
void subghz_protocol_decoder_subaru_feed(void* ctx, bool level, uint32_t dur) {
SubGhzProtocolDecoderSubaru* i = ctx;
const uint32_t te_short = subghz_protocol_subaru_const.te_short;
const uint32_t te_long = subghz_protocol_subaru_const.te_long;
const uint32_t delta = subghz_protocol_subaru_const.te_delta;
switch(i->decoder.parser_step) {
case 0:
if(level && DURATION_DIFF(dur, te_long) < delta) {
i->decoder.decode_data = 0;
i->decoder.decode_count_bit = 0;
i->header_count = 0;
i->decoder.parser_step = 1;
}
break;
case 1:
if(!level && DURATION_DIFF(dur, te_long) < delta) {
i->header_count++;
} else if(!level && DURATION_DIFF(dur, SUBARU_GAP_US) < 800) {
if(i->header_count > 20)
i->decoder.parser_step = 2;
else
i->decoder.parser_step = 0;
} else {
i->decoder.parser_step = 0;
}
break;
case 2:
if(level && DURATION_DIFF(dur, SUBARU_SYNC_US) < 800) {
i->decoder.parser_step = 3;
} else {
i->decoder.parser_step = 0;
}
break;
case 3:
if(!level) {
i->decoder.te_last = dur;
i->decoder.parser_step = 4;
} else {
i->decoder.parser_step = 0;
}
break;
case 4:
if(level) {
bool bit;
if(DURATION_DIFF(dur, te_long) < delta &&
DURATION_DIFF(i->decoder.te_last, te_short) < delta) {
bit = false;
} else if(DURATION_DIFF(dur, te_short) < delta &&
DURATION_DIFF(i->decoder.te_last, te_long) < delta) {
bit = true;
} else {
i->decoder.parser_step = 0;
break;
}
subghz_protocol_blocks_add_bit(&i->decoder, bit);
if(i->decoder.decode_count_bit >= 64) {
i->generic.data = i->decoder.decode_data;
i->generic.data_count_bit = 64;
uint8_t b[8];
for(int k = 0; k < 8; k++)
b[k] = (i->generic.data >> (56 - 8*k)) & 0xFF;
subaru_decode_fields(b, &i->serial, &i->btn, &i->cnt);
i->generic.serial = i->serial;
i->generic.btn = i->btn;
i->generic.cnt = i->cnt;
if(i->base.callback)
i->base.callback(&i->base, i->base.context);
i->decoder.parser_step = 0;
}
}
break;
}
}
uint8_t subghz_protocol_decoder_subaru_get_hash_data(void* ctx) {
SubGhzProtocolDecoderSubaru* i = ctx;
return subghz_protocol_blocks_get_hash_data(
&i->decoder,
(i->decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_subaru_serialize(
void* ctx,
FlipperFormat* ff,
SubGhzRadioPreset* preset) {
SubGhzProtocolDecoderSubaru* i = ctx;
return subghz_block_generic_serialize(&i->generic, ff, preset);
}
SubGhzProtocolStatus subghz_protocol_decoder_subaru_deserialize(
void* ctx,
FlipperFormat* ff) {
SubGhzProtocolDecoderSubaru* i = ctx;
return subghz_block_generic_deserialize_check_count_bit(
&i->generic,
ff,
subghz_protocol_subaru_const.min_count_bit_for_found);
}
void subghz_protocol_decoder_subaru_get_string(void* ctx, FuriString* out) {
SubGhzProtocolDecoderSubaru* i = ctx;
furi_string_cat_printf(
out,
"%s %dbit\r\n"
"Key:%016llX\r\n"
"Sn:%06lX Btn:%01X [%s]\r\n"
"Cnt:%04X",
i->generic.protocol_name,
i->generic.data_count_bit,
i->generic.data,
i->serial,
i->btn,
subaru_get_button_name(i->btn),
i->cnt);
}
static void subaru_decode_count(const uint8_t* KB, uint16_t* count);
static void subaru_encode_count(uint8_t* KB, uint16_t count);
static void subaru_add_bit(SubGhzProtocolDecoderSubaru* instance, bool bit);
static bool subaru_process_data(SubGhzProtocolDecoderSubaru* instance);
static void subghz_protocol_encoder_subaru_get_upload(SubGhzProtocolEncoderSubaru* instance);
const SubGhzProtocolDecoder subghz_protocol_subaru_decoder = {
.alloc = subghz_protocol_decoder_subaru_alloc,
@@ -314,3 +67,587 @@ const SubGhzProtocolDecoder subghz_protocol_subaru_decoder = {
.deserialize = subghz_protocol_decoder_subaru_deserialize,
.get_string = subghz_protocol_decoder_subaru_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_subaru_encoder = {
.alloc = subghz_protocol_encoder_subaru_alloc,
.free = subghz_protocol_encoder_subaru_free,
.deserialize = subghz_protocol_encoder_subaru_deserialize,
.stop = subghz_protocol_encoder_subaru_stop,
.yield = subghz_protocol_encoder_subaru_yield,
};
const SubGhzProtocol subghz_protocol_subaru = {
.name = SUBGHZ_PROTOCOL_SUBARU_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |
SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_subaru_decoder,
.encoder = &subghz_protocol_subaru_encoder,
};
static uint8_t subaru_get_button_code(uint8_t custom_btn) {
switch(custom_btn) {
case 1: return 0x01;
case 2: return 0x02;
case 3: return 0x03;
case 4: return 0x04;
case 5: return 0x08;
default: return 0x01;
}
}
static uint8_t subaru_btn_to_custom(uint8_t btn_code) {
switch(btn_code) {
case 0x01: return 1;
case 0x02: return 2;
case 0x03: return 3;
case 0x04: return 4;
case 0x08: return 5;
default: return 1;
}
}
static const char* subaru_get_button_name(uint8_t btn) {
switch(btn) {
case 0x01: return "Lock";
case 0x02: return "Unlock";
case 0x03: return "Trunk";
case 0x04: return "Panic";
case 0x08: return "0x08";
default: return "??";
}
}
static void subaru_decode_count(const uint8_t* KB, uint16_t* count) {
uint8_t lo = 0;
if((KB[4] & 0x40) == 0) lo |= 0x01;
if((KB[4] & 0x80) == 0) lo |= 0x02;
if((KB[5] & 0x01) == 0) lo |= 0x04;
if((KB[5] & 0x02) == 0) lo |= 0x08;
if((KB[6] & 0x01) == 0) lo |= 0x10;
if((KB[6] & 0x02) == 0) lo |= 0x20;
if((KB[5] & 0x40) == 0) lo |= 0x40;
if((KB[5] & 0x80) == 0) lo |= 0x80;
uint8_t REG_SH1 = (KB[7] << 4) & 0xF0;
if(KB[5] & 0x04) REG_SH1 |= 0x04;
if(KB[5] & 0x08) REG_SH1 |= 0x08;
if(KB[6] & 0x80) REG_SH1 |= 0x02;
if(KB[6] & 0x40) REG_SH1 |= 0x01;
uint8_t REG_SH2 = ((KB[6] << 2) & 0xF0) | ((KB[7] >> 4) & 0x0F);
uint8_t SER0 = KB[3];
uint8_t SER1 = KB[1];
uint8_t SER2 = KB[2];
uint8_t total_rot = 4 + lo;
for(uint8_t i = 0; i < total_rot; ++i) {
uint8_t t_bit = (SER0 >> 7) & 1;
SER0 = ((SER0 << 1) & 0xFE) | ((SER1 >> 7) & 1);
SER1 = ((SER1 << 1) & 0xFE) | ((SER2 >> 7) & 1);
SER2 = ((SER2 << 1) & 0xFE) | t_bit;
}
uint8_t T1 = SER1 ^ REG_SH1;
uint8_t T2 = SER2 ^ REG_SH2;
uint8_t hi = 0;
if((T1 & 0x10) == 0) hi |= 0x04;
if((T1 & 0x20) == 0) hi |= 0x08;
if((T2 & 0x80) == 0) hi |= 0x02;
if((T2 & 0x40) == 0) hi |= 0x01;
if((T1 & 0x01) == 0) hi |= 0x40;
if((T1 & 0x02) == 0) hi |= 0x80;
if((T2 & 0x08) == 0) hi |= 0x20;
if((T2 & 0x04) == 0) hi |= 0x10;
*count = ((hi << 8) | lo) & 0xFFFF;
}
static void subaru_encode_count(uint8_t* KB, uint16_t count) {
uint8_t lo = count & 0xFF;
uint8_t hi = (count >> 8) & 0xFF;
KB[4] &= ~0xC0;
KB[5] &= ~0xC3;
KB[6] &= ~0x03;
if((lo & 0x01) == 0) KB[4] |= 0x40;
if((lo & 0x02) == 0) KB[4] |= 0x80;
if((lo & 0x04) == 0) KB[5] |= 0x01;
if((lo & 0x08) == 0) KB[5] |= 0x02;
if((lo & 0x10) == 0) KB[6] |= 0x01;
if((lo & 0x20) == 0) KB[6] |= 0x02;
if((lo & 0x40) == 0) KB[5] |= 0x40;
if((lo & 0x80) == 0) KB[5] |= 0x80;
uint8_t SER0 = KB[3];
uint8_t SER1 = KB[1];
uint8_t SER2 = KB[2];
uint8_t total_rot = 4 + lo;
for(uint8_t i = 0; i < total_rot; ++i) {
uint8_t t_bit = (SER0 >> 7) & 1;
SER0 = ((SER0 << 1) & 0xFE) | ((SER1 >> 7) & 1);
SER1 = ((SER1 << 1) & 0xFE) | ((SER2 >> 7) & 1);
SER2 = ((SER2 << 1) & 0xFE) | t_bit;
}
uint8_t T1 = 0xFF;
uint8_t T2 = 0xFF;
if(hi & 0x04) T1 &= ~0x10;
if(hi & 0x08) T1 &= ~0x20;
if(hi & 0x02) T2 &= ~0x80;
if(hi & 0x01) T2 &= ~0x40;
if(hi & 0x40) T1 &= ~0x01;
if(hi & 0x80) T1 &= ~0x02;
if(hi & 0x20) T2 &= ~0x08;
if(hi & 0x10) T2 &= ~0x04;
uint8_t new_REG_SH1 = T1 ^ SER1;
uint8_t new_REG_SH2 = T2 ^ SER2;
KB[5] &= ~0x0C;
KB[6] &= ~0xC0;
KB[7] = (KB[7] & 0xF0) | ((new_REG_SH1 >> 4) & 0x0F);
if(new_REG_SH1 & 0x04) KB[5] |= 0x04;
if(new_REG_SH1 & 0x08) KB[5] |= 0x08;
if(new_REG_SH1 & 0x02) KB[6] |= 0x80;
if(new_REG_SH1 & 0x01) KB[6] |= 0x40;
KB[6] = (KB[6] & 0xC3) | ((new_REG_SH2 >> 2) & 0x3C);
KB[7] = (KB[7] & 0x0F) | ((new_REG_SH2 << 4) & 0xF0);
}
static void subaru_add_bit(SubGhzProtocolDecoderSubaru* instance, bool bit) {
if(instance->bit_count < 64) {
uint8_t byte_idx = instance->bit_count / 8;
uint8_t bit_idx = 7 - (instance->bit_count % 8);
if(bit) {
instance->data[byte_idx] |= (1 << bit_idx);
} else {
instance->data[byte_idx] &= ~(1 << bit_idx);
}
instance->bit_count++;
}
}
static bool subaru_process_data(SubGhzProtocolDecoderSubaru* instance) {
if(instance->bit_count < 64) {
return false;
}
uint8_t* b = instance->data;
instance->key = ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) |
((uint64_t)b[2] << 40) | ((uint64_t)b[3] << 32) |
((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) |
((uint64_t)b[6] << 8) | ((uint64_t)b[7]);
instance->serial = ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | b[3];
instance->button = b[0] & 0x0F;
subaru_decode_count(b, &instance->count);
instance->decoder.decode_data = instance->key;
instance->decoder.decode_count_bit = 64;
return true;
}
void* subghz_protocol_decoder_subaru_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderSubaru* instance = malloc(sizeof(SubGhzProtocolDecoderSubaru));
instance->base.protocol = &subghz_protocol_subaru;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_subaru_free(void* context) {
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
free(instance);
}
void subghz_protocol_decoder_subaru_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
instance->decoder.parser_step = SubaruDecoderStepReset;
instance->decoder.te_last = 0;
instance->header_count = 0;
instance->bit_count = 0;
memset(instance->data, 0, sizeof(instance->data));
}
void subghz_protocol_decoder_subaru_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
switch(instance->decoder.parser_step) {
case SubaruDecoderStepReset:
if(level && DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
instance->decoder.parser_step = SubaruDecoderStepCheckPreamble;
instance->decoder.te_last = duration;
instance->header_count = 1;
}
break;
case SubaruDecoderStepCheckPreamble:
if(!level) {
if(DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
instance->header_count++;
} else if(duration > 2000 && duration < 3500) {
if(instance->header_count > 20) {
instance->decoder.parser_step = SubaruDecoderStepFoundGap;
} else {
instance->decoder.parser_step = SubaruDecoderStepReset;
}
} else {
instance->decoder.parser_step = SubaruDecoderStepReset;
}
} else {
if(DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
instance->decoder.te_last = duration;
instance->header_count++;
} else {
instance->decoder.parser_step = SubaruDecoderStepReset;
}
}
break;
case SubaruDecoderStepFoundGap:
if(level && duration > 2000 && duration < 3500) {
instance->decoder.parser_step = SubaruDecoderStepFoundSync;
} else {
instance->decoder.parser_step = SubaruDecoderStepReset;
}
break;
case SubaruDecoderStepFoundSync:
if(!level && DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
instance->decoder.parser_step = SubaruDecoderStepSaveDuration;
instance->bit_count = 0;
memset(instance->data, 0, sizeof(instance->data));
} else {
instance->decoder.parser_step = SubaruDecoderStepReset;
}
break;
case SubaruDecoderStepSaveDuration:
if(level) {
if(DURATION_DIFF(duration, subghz_protocol_subaru_const.te_short) < subghz_protocol_subaru_const.te_delta) {
subaru_add_bit(instance, true);
instance->decoder.te_last = duration;
instance->decoder.parser_step = SubaruDecoderStepCheckDuration;
} else if(DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
subaru_add_bit(instance, false);
instance->decoder.te_last = duration;
instance->decoder.parser_step = SubaruDecoderStepCheckDuration;
} else if(duration > 3000) {
if(instance->bit_count >= 64) {
if(subaru_process_data(instance)) {
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->count;
uint8_t custom_btn = subaru_btn_to_custom(instance->button);
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(custom_btn);
}
subghz_custom_btn_set_max(5);
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
}
}
instance->decoder.parser_step = SubaruDecoderStepReset;
} else {
instance->decoder.parser_step = SubaruDecoderStepReset;
}
} else {
instance->decoder.parser_step = SubaruDecoderStepReset;
}
break;
case SubaruDecoderStepCheckDuration:
if(!level) {
if(DURATION_DIFF(duration, subghz_protocol_subaru_const.te_short) < subghz_protocol_subaru_const.te_delta ||
DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
instance->decoder.parser_step = SubaruDecoderStepSaveDuration;
} else if(duration > 3000) {
if(instance->bit_count >= 64) {
if(subaru_process_data(instance)) {
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->count;
uint8_t custom_btn = subaru_btn_to_custom(instance->button);
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(custom_btn);
}
subghz_custom_btn_set_max(5);
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
}
}
instance->decoder.parser_step = SubaruDecoderStepReset;
} else {
instance->decoder.parser_step = SubaruDecoderStepReset;
}
} else {
instance->decoder.parser_step = SubaruDecoderStepReset;
}
break;
}
}
uint8_t subghz_protocol_decoder_subaru_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_subaru_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
SubGhzProtocolStatus subghz_protocol_decoder_subaru_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, subghz_protocol_subaru_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
instance->key = instance->generic.data;
uint8_t b[8];
for(int i = 0; i < 8; i++) {
b[i] = (uint8_t)(instance->key >> (56 - i * 8));
}
instance->serial = ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | b[3];
instance->button = b[0] & 0x0F;
subaru_decode_count(b, &instance->count);
instance->generic.serial = instance->serial;
instance->generic.btn = instance->button;
instance->generic.cnt = instance->count;
uint8_t custom_btn = subaru_btn_to_custom(instance->button);
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(custom_btn);
}
subghz_custom_btn_set_max(5);
}
return ret;
}
void subghz_protocol_decoder_subaru_get_string(void* context, FuriString* output) {
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
uint32_t key_hi = (uint32_t)(instance->key >> 32);
uint32_t key_lo = (uint32_t)(instance->key & 0xFFFFFFFF);
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Key:%08lX%08lX\r\n"
"Sn:%06lX Cnt:%04X\r\n"
"Btn:%X [%s]",
instance->generic.protocol_name,
instance->generic.data_count_bit,
key_hi,
key_lo,
instance->serial,
instance->count,
instance->button,
subaru_get_button_name(instance->button));
}
void* subghz_protocol_encoder_subaru_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderSubaru* instance = malloc(sizeof(SubGhzProtocolEncoderSubaru));
instance->base.protocol = &subghz_protocol_subaru;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 2048;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
instance->encoder.front = 0;
return instance;
}
void subghz_protocol_encoder_subaru_free(void* context) {
furi_assert(context);
SubGhzProtocolEncoderSubaru* instance = context;
free(instance->encoder.upload);
free(instance);
}
void subghz_protocol_encoder_subaru_stop(void* context) {
furi_assert(context);
SubGhzProtocolEncoderSubaru* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_subaru_yield(void* context) {
furi_assert(context);
SubGhzProtocolEncoderSubaru* 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 void subghz_protocol_encoder_subaru_get_upload(SubGhzProtocolEncoderSubaru* instance) {
furi_assert(instance);
size_t index = 0;
uint32_t te_short = subghz_protocol_subaru_const.te_short;
uint32_t te_long = subghz_protocol_subaru_const.te_long;
uint32_t gap_duration = 2500;
uint32_t sync_duration = 2500;
for(int i = 0; i < 20; i++) {
instance->encoder.upload[index++] = level_duration_make(true, te_long);
instance->encoder.upload[index++] = level_duration_make(false, te_long);
}
instance->encoder.upload[index++] = level_duration_make(true, te_long);
instance->encoder.upload[index++] = level_duration_make(false, gap_duration);
instance->encoder.upload[index++] = level_duration_make(true, sync_duration);
instance->encoder.upload[index++] = level_duration_make(false, te_long);
for(int i = 63; i >= 0; i--) {
bool bit = (instance->key >> i) & 1;
if(bit) {
instance->encoder.upload[index++] = level_duration_make(true, te_short);
instance->encoder.upload[index++] = level_duration_make(false, te_short);
} else {
instance->encoder.upload[index++] = level_duration_make(true, te_long);
instance->encoder.upload[index++] = level_duration_make(false, te_short);
}
}
instance->encoder.upload[index++] = level_duration_make(true, te_long);
instance->encoder.upload[index++] = level_duration_make(false, gap_duration * 2);
instance->encoder.size_upload = index;
instance->encoder.front = 0;
}
SubGhzProtocolStatus subghz_protocol_encoder_subaru_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolEncoderSubaru* instance = context;
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
do {
ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);
if(ret != SubGhzProtocolStatusOk) {
break;
}
uint64_t original_key = instance->generic.data;
uint8_t b[8];
for(int i = 0; i < 8; i++) {
b[i] = (uint8_t)(original_key >> (56 - i * 8));
}
instance->serial = ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | b[3];
instance->button = b[0] & 0x0F;
subaru_decode_count(b, &instance->count);
uint8_t original_custom_btn = subaru_btn_to_custom(instance->button);
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(original_custom_btn);
}
subghz_custom_btn_set_max(5);
uint8_t selected_custom_btn;
if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK) {
selected_custom_btn = subghz_custom_btn_get_original();
} else {
selected_custom_btn = subghz_custom_btn_get();
}
uint8_t new_button = subaru_get_button_code(selected_custom_btn);
instance->button = new_button;
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
instance->count = (instance->count + mult) & 0xFFFF;
b[0] = (b[0] & 0xF0) | (instance->button & 0x0F);
subaru_encode_count(b, instance->count);
instance->key = 0;
for(int i = 0; i < 8; i++) {
instance->key = (instance->key << 8) | b[i];
}
instance->generic.data = instance->key;
instance->generic.serial = instance->serial;
instance->generic.btn = instance->button;
instance->generic.cnt = instance->count;
subghz_protocol_encoder_subaru_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->key >> (i * 8)) & 0xFF;
}
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
ret = SubGhzProtocolStatusErrorParserKey;
break;
}
instance->encoder.is_running = true;
ret = SubGhzProtocolStatusOk;
} while(false);
return ret;
}