Merge branch 'zero-mega' into pp_main

This commit is contained in:
MX
2026-09-15 02:11:11 +03:00
25 changed files with 2789 additions and 2006 deletions
+2 -3
View File
@@ -36,9 +36,9 @@ Protocols are split into **AM** and **FM** registries. The active registry is ch
| Honda V1 | ✅ | ✅ | Manchester | AM650 | Rolling Code | CRC4 | 315.00 / 433.92 |
| Kia V1 | ✅ | ✅ | Manchester | AM650 | Rolling Code | CRC4 | 315.00 / 433.92 |
| Mazda V0 | ✅ | ✅ | Manchester | AM650 | Rolling Code | Checksum | 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 |
| Renault V0 | ✅ | ✅ | Manchester | AM650 | Rolling Code / Replay | Type/IC | 315.00 / 433.92 |
| Renault V1 | ✅ | ✅ | Manchester | AM650 | HITAG2 | XOR8 | 315.00 / 433.92 |
| StarLine | ✅ | ✅ | PWM | AM650 | KeeLoq | ❌ | 315.00 / 433.92 |
| Subaru | ✅ | ✅ | PPM | AM650 | Rolling Code | ❌ | 315.00 / 433.92 |
| VAG (VW/Audi/Seat/Skoda) | ✅ | ✅ | Manchester | AM650 | AUT64/XTEA | ❌ | 434.42 |
@@ -53,7 +53,7 @@ Protocols are split into **AM** and **FM** registries. The active registry is ch
| Ford V2 | ✅ | ✅ | Manchester | F4 | Rolling Code (simple replay) | ❌ | 434.25 |
| Ford V3 | ✅ | ❌ | Manchester | F4 | Rolling Code | ❌ | 434.25 |
| Honda Static | ✅ | ✅ | PWM | Honda1 | Static Code | Checksum | 315.00 / 433.92 |
| Kia V0 / Suzuki V0 / Honda V0 | ✅ | ✅ | PWM | FM476 | Rolling Code | CRC8 | 315.00 / 433.92 |
| Kia V0 / Suzuki V0 / Honda V0 / Mitsubishi V0 | ✅ | ✅ | PWM | FM476 | Rolling Code | CRC8 | 315.00 / 433.92 |
| Kia V2 | ✅ | ✅ | Manchester | FM476 | Rolling Code | CRC4 | 315.00 / 433.92 |
| Kia V3 / V4 | ✅ | ✅ | PWM | FM476 | KeeLoq | CRC4 (BF) | 315.00 / 433.92 |
| Kia V5 | ✅ | ✅ | PWM | FM476 | Rolling Code | ✅ | 315.00 / 433.92 |
@@ -61,7 +61,6 @@ Protocols are split into **AM** and **FM** registries. The active registry is ch
| Kia V7 | ✅ | ✅ | Manchester | FM476 | Rolling Code | CRC8 | 315.00 / 433.92 |
| Honda V2 | ✅ | ✅ | PWM | F4 | Rolling Code | Check+Tail | 315.00 / 433.92 |
| Mazda V0 | ✅ | ✅ | Manchester | FM (F2?) | Rolling Code | Checksum | 315.00 / 433.92 |
| Mitsubishi V0 | ✅ | ❌ | PWM | FM476 | Rolling Code | ❌ | 315.00 / 433.92 |
| PSA (Peugeot/Citroen) | ✅ | ✅ | Manchester | FM (F3?) | XTEA/XOR | CRC8 | 315.00 / 433.92 |
| Scher-Khan | ✅ | ❌ | PWM | FM | Magic Code | ❌ | 315.00 / 433.92 |
+10 -2
View File
@@ -77,10 +77,10 @@ App(
"protocols/kia_v1.c",
"protocols/kia_v2.c",
"protocols/mazda_v0.c",
"protocols/porsche_touareg.c",
"protocols/psa.c",
"protocols/psa_crypto.c",
"protocols/renault_v0.c",
"protocols/renault_v1.c",
"protocols/subaru.c",
"protocols/star_line.c",
],
@@ -119,8 +119,8 @@ App(
"protocols/kia_v6.c",
"protocols/kia_v7.c",
"protocols/mazda_v0.c",
"protocols/mitsubishi_v0.c",
"protocols/psa.c",
"protocols/renault_v1.c",
"protocols/psa_crypto.c",
],
fal_embedded=True,
@@ -326,6 +326,13 @@ ProtoPirateTxProtocolPlugin(
"renault_v0_protocol",
["renault_v0.c"],
)
ProtoPirateTxProtocolPlugin(
"renault_v1",
"renault_v1.h",
"RENAULT_PROTOCOL_V1_NAME",
"renault_v1_protocol",
["renault_v1.c"],
)
ProtoPirateTxProtocolPlugin(
"star_line",
"star_line.h",
@@ -406,6 +413,7 @@ App(
"protocols/psa_crypto.c",
"protocols/psa_crypto_bf.c",
"protocols/psa_bf_core.c",
"protocols/renault_v1.c",
"protocols/protocols_common.c",
],
fap_icon_assets="images",
+180 -85
View File
@@ -23,10 +23,15 @@
#define FIAT_V1_XOR_FIELD "XOR"
#define FIAT_V1_HITAG2_KEY_FIELD "Hitag2 Key"
#define FIAT_V1_HITAG2_EPOCH_FIELD "Hitag2 Epoch"
#define FIAT_V1_KNOWN_KEY_COUNT 8U
#define FIAT_V1_KNOWN_KEY_COUNT 9U
#define FIAT_V1_ENC_LEAD_US 2033U
#define FIAT_V1_ENC_GAP_US 3252U
#define FIAT_V1_TE_VARIANT_A 0U
#define FIAT_V1_TE_VARIANT_B 1U
#define FIAT_V1_DEFAULT_TE_VARIANT FIAT_V1_TE_VARIANT_B
#define FIAT_V1_ENC_LEAD_A_US 2033U
#define FIAT_V1_ENC_GAP_A_US 3252U
#define FIAT_V1_ENC_LEAD_B_US 850U
#define FIAT_V1_ENC_GAP_B_US 1230U
#define FIAT_V1_ENC_DEFAULT_REPEAT 6U
#define FIAT_V1_UPLOAD_CAPACITY 240U
_Static_assert(
@@ -48,9 +53,24 @@ static const SubGhzBlockConst subghz_protocol_fiat_v1_const_b = {
};
static const SubGhzBlockConst* fiat_v1_variant_const(uint8_t variant) {
return (variant == 0U) ? &subghz_protocol_fiat_v1_const : &subghz_protocol_fiat_v1_const_b;
return (variant == FIAT_V1_TE_VARIANT_A) ? &subghz_protocol_fiat_v1_const :
&subghz_protocol_fiat_v1_const_b;
}
#if PROTOPIRATE_WITH_ENCODER
static uint32_t fiat_v1_enc_lead_us(uint8_t variant) {
return (variant == FIAT_V1_TE_VARIANT_A) ? FIAT_V1_ENC_LEAD_A_US : FIAT_V1_ENC_LEAD_B_US;
}
static uint32_t fiat_v1_enc_gap_us(uint8_t variant) {
return (variant == FIAT_V1_TE_VARIANT_A) ? FIAT_V1_ENC_GAP_A_US : FIAT_V1_ENC_GAP_B_US;
}
static uint32_t fiat_v1_enc_te_us(uint8_t variant) {
return fiat_v1_variant_const(variant)->te_short;
}
#endif
typedef enum {
FiatV1DecoderStepReset = 0,
FiatV1DecoderStepData = 1,
@@ -77,6 +97,7 @@ struct SubGhzProtocolDecoderFiatV1 {
uint8_t hitag2_key[6];
uint32_t hitag2_epoch;
bool hitag2_key_valid;
uint8_t te_variant;
};
#if PROTOPIRATE_WITH_ENCODER
@@ -91,6 +112,7 @@ struct SubGhzProtocolEncoderFiatV1 {
uint32_t hop;
uint8_t tail_bits;
uint8_t frame_xor;
uint8_t te_variant;
};
#endif
@@ -280,7 +302,10 @@ static void fiat_v1_decode_fields(SubGhzProtocolDecoderFiatV1* instance) {
}
static bool
fiat_v1_commit(SubGhzProtocolDecoderFiatV1* instance, const uint8_t raw[FIAT_V1_WIRE_BYTES]) {
fiat_v1_commit(
SubGhzProtocolDecoderFiatV1* instance,
const uint8_t raw[FIAT_V1_WIRE_BYTES],
uint8_t te_variant) {
if(!fiat_v1_frame_valid(raw)) {
return false;
}
@@ -292,6 +317,8 @@ static bool
memcpy(instance->raw_data, raw, FIAT_V1_WIRE_BYTES);
memcpy(instance->last_raw_data, raw, FIAT_V1_WIRE_BYTES);
instance->last_raw_valid = true;
instance->te_variant = (te_variant == FIAT_V1_TE_VARIANT_A) ? FIAT_V1_TE_VARIANT_A :
FIAT_V1_TE_VARIANT_B;
fiat_v1_decode_fields(instance);
FURI_LOG_D(
@@ -333,7 +360,7 @@ static bool
}
}
return fiat_v1_commit(instance, raw);
return fiat_v1_commit(instance, raw, variant);
}
static void fiat_v1_try_decode(SubGhzProtocolDecoderFiatV1* instance, uint8_t variant) {
@@ -525,6 +552,7 @@ static const uint8_t fiat_v1_known_keys[FIAT_V1_KNOWN_KEY_COUNT][6] = {
{0x4DU, 0x49U, 0x4BU, 0x52U, 0x4FU, 0x4EU},
{0xCDU, 0x49U, 0x4BU, 0x52U, 0x4FU, 0x4EU},
{0x33U, 0xFAU, 0x2FU, 0xCDU, 0xC3U, 0x3BU},
{0xF6U, 0x1AU, 0xEFU, 0x9CU, 0xD0U, 0x1BU},
};
static bool fiat_v1_key_matches(
@@ -537,24 +565,73 @@ static bool fiat_v1_key_matches(
return fiat_v1_bcm_generate_authenticator(uid, button, control, key, epoch) == hop;
}
static bool fiat_v1_key_matches_any_button(
uint32_t uid,
uint16_t control,
uint32_t hop,
const uint8_t key[6],
uint32_t epoch) {
static const uint8_t buttons[] = {0x1U, 0x2U, 0x4U, 0x8U};
for(size_t i = 0; i < COUNT_OF(buttons); i++) {
if(fiat_v1_key_matches(uid, buttons[i], control, hop, key, epoch)) {
return true;
}
}
return false;
}
static bool fiat_v1_resolve_hitag2_key(
FlipperFormat* ff,
uint32_t uid,
uint8_t button,
uint16_t control,
uint32_t hop,
uint8_t key_out[6],
uint32_t* epoch_out) {
if(!key_out) return false;
uint8_t key[6] = {0};
uint32_t epoch = 0U;
if(ff) {
flipper_format_rewind(ff);
if(flipper_format_read_hex(ff, FIAT_V1_HITAG2_KEY_FIELD, key, sizeof(key))) {
flipper_format_rewind(ff);
if(flipper_format_read_uint32(ff, FIAT_V1_HITAG2_EPOCH_FIELD, &epoch, 1U)) {
epoch &= 0x3FFFFUL;
}
if(fiat_v1_key_matches(uid, button, control, hop, key, epoch) ||
fiat_v1_key_matches_any_button(uid, control, hop, key, epoch)) {
memcpy(key_out, key, sizeof(key));
if(epoch_out) *epoch_out = epoch;
return true;
}
}
}
for(uint8_t i = 0U; i < FIAT_V1_KNOWN_KEY_COUNT; i++) {
if(fiat_v1_key_matches(uid, button, control, hop, fiat_v1_known_keys[i], 0U) ||
fiat_v1_key_matches_any_button(uid, control, hop, fiat_v1_known_keys[i], 0U)) {
memcpy(key_out, fiat_v1_known_keys[i], sizeof(key));
if(epoch_out) *epoch_out = 0U;
return true;
}
}
return false;
}
static void fiat_v1_verify_hitag2_key(SubGhzProtocolDecoderFiatV1* instance) {
instance->hitag2_key_valid = false;
instance->hitag2_epoch = 0U;
memset(instance->hitag2_key, 0, sizeof(instance->hitag2_key));
const uint32_t uid = instance->uid;
const uint8_t button = instance->generic.btn;
const uint16_t control = (uint16_t)(instance->generic.cnt & 0x03FFU);
const uint32_t hop = instance->hop;
for(uint8_t i = 0U; i < FIAT_V1_KNOWN_KEY_COUNT; i++) {
if(fiat_v1_key_matches(
uid, button, control, hop, fiat_v1_known_keys[i], instance->hitag2_epoch)) {
memcpy(instance->hitag2_key, fiat_v1_known_keys[i], sizeof(instance->hitag2_key));
instance->hitag2_key_valid = true;
return;
}
}
instance->hitag2_key_valid = fiat_v1_resolve_hitag2_key(
NULL,
instance->uid,
instance->generic.btn,
(uint16_t)(instance->generic.cnt & 0x03FFU),
instance->hop,
instance->hitag2_key,
&instance->hitag2_epoch);
}
#if PROTOPIRATE_WITH_ENCODER
@@ -565,18 +642,20 @@ static bool fiat_v1_encoder_build_upload(SubGhzProtocolEncoderFiatV1* instance)
return false;
}
const uint8_t variant = instance->te_variant;
const uint32_t te = fiat_v1_enc_te_us(variant);
size_t index = 0U;
const size_t cap = FIAT_V1_UPLOAD_CAPACITY;
index = pp_emit_merge(upload, index, cap, true, FIAT_V1_ENC_LEAD_US);
index = pp_emit_merge(upload, index, cap, true, fiat_v1_enc_lead_us(variant));
for(uint8_t bit_index = 0U; bit_index < FIAT_V1_WIRE_BITS; bit_index++) {
const bool bit = ((instance->raw_data[bit_index >> 3U] >> (7U - (bit_index & 7U))) & 1U) !=
0U;
index = pp_emit_merge(upload, index, cap, bit, FIAT_V1_TE_SHORT);
index = pp_emit_merge(upload, index, cap, !bit, FIAT_V1_TE_SHORT);
index = pp_emit_merge(upload, index, cap, bit, te);
index = pp_emit_merge(upload, index, cap, !bit, te);
}
index = pp_emit_merge(upload, index, cap, false, FIAT_V1_ENC_GAP_US);
index = pp_emit_merge(upload, index, cap, false, fiat_v1_enc_gap_us(variant));
furi_check(index <= cap);
instance->encoder.size_upload = index;
instance->encoder.front = 0U;
@@ -591,6 +670,7 @@ void* subghz_protocol_encoder_fiat_v1_alloc(SubGhzEnvironment* environment) {
instance->base.protocol = &fiat_v1_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
instance->tail_bits = FIAT_V1_DEFAULT_TAIL_BITS;
instance->te_variant = FIAT_V1_DEFAULT_TE_VARIANT;
instance->encoder.repeat = FIAT_V1_ENC_DEFAULT_REPEAT;
return instance;
}
@@ -618,18 +698,22 @@ SubGhzProtocolStatus
}
instance->generic.data_count_bit = bit_count;
uint32_t serial = 0U;
uint32_t button = 0U;
uint32_t control = 0U;
uint32_t key_serial = 0U;
uint32_t key_button = 0U;
uint32_t key_control = 0U;
uint32_t captured_hop = 0U;
uint8_t raw_from_file[FIAT_V1_WIRE_BYTES] = {0};
bool have_raw = false;
flipper_format_rewind(flipper_format);
if(flipper_format_read_hex(
flipper_format, FIAT_V1_RAW_FIELD, raw_from_file, sizeof(raw_from_file)) &&
fiat_v1_frame_valid(raw_from_file)) {
serial = fiat_v1_uid(raw_from_file);
button = raw_from_file[6] >> 4U;
control = fiat_v1_counter(raw_from_file);
have_raw = true;
key_serial = fiat_v1_uid(raw_from_file);
key_button = raw_from_file[6] >> 4U;
key_control = fiat_v1_counter(raw_from_file);
captured_hop = fiat_v1_hop(raw_from_file);
instance->tail_bits = raw_from_file[11] & 0x03U;
} else {
SubGhzBlockGeneric generic = {0};
@@ -637,29 +721,53 @@ SubGhzProtocolStatus
if(subghz_block_generic_deserialize_check_count_bit(
&generic, flipper_format, subghz_protocol_fiat_v1_const.min_count_bit_for_found) ==
SubGhzProtocolStatusOk) {
serial = (uint32_t)(generic.data >> 32U);
button = generic.btn;
control = generic.cnt;
key_serial = (uint32_t)(generic.data >> 32U);
key_button = generic.btn;
key_control = generic.cnt;
captured_hop = (uint32_t)generic.data;
}
flipper_format_rewind(flipper_format);
uint32_t hop_field = 0U;
if(flipper_format_read_uint32(flipper_format, FIAT_V1_HOP_FIELD, &hop_field, 1U)) {
captured_hop = hop_field;
}
}
uint32_t serial = key_serial;
uint32_t button = key_button;
uint32_t control = key_control;
uint32_t type = FIAT_V1_DEFAULT_TE_VARIANT;
pp_encoder_read_fields(flipper_format, &serial, &button, &control, &type);
if(!have_raw) {
key_serial = serial;
key_control = control;
if(fiat_v1_button_valid((uint8_t)key_button) == false) {
key_button = button;
}
}
pp_encoder_read_fields(flipper_format, &serial, &button, &control, NULL);
if(serial == 0U || serial == UINT32_MAX || !fiat_v1_button_valid((uint8_t)button)) {
return SubGhzProtocolStatusErrorParserOthers;
}
flipper_format_rewind(flipper_format);
if(!flipper_format_read_hex(
flipper_format, FIAT_V1_HITAG2_KEY_FIELD, instance->hitag2_key, 6U)) {
return SubGhzProtocolStatusErrorParserOthers;
uint32_t tail_bits = instance->tail_bits;
if(flipper_format_read_uint32(flipper_format, FIAT_V1_TAIL_BITS_FIELD, &tail_bits, 1U)) {
instance->tail_bits = (uint8_t)(tail_bits & 0x03U);
}
uint32_t epoch = 0U;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, FIAT_V1_HITAG2_EPOCH_FIELD, &epoch, 1U)) {
instance->epoch = epoch & 0x3FFFFUL;
} else {
instance->epoch = 0U;
instance->te_variant = (type == FIAT_V1_TE_VARIANT_A) ? FIAT_V1_TE_VARIANT_A :
FIAT_V1_TE_VARIANT_B;
if(!fiat_v1_resolve_hitag2_key(
flipper_format,
key_serial ? key_serial : serial,
(uint8_t)(key_button ? key_button : button),
(uint16_t)(key_control & 0x03FFU),
captured_hop,
instance->hitag2_key,
&instance->epoch)) {
return SubGhzProtocolStatusErrorParserOthers;
}
control &= 0x03FFU;
@@ -694,12 +802,13 @@ SubGhzProtocolStatus
FURI_LOG_I(
TAG,
"TX UID:%08lX Btn:%02lX Cnt:%03lX Auth:%08lX Epoch:%05lX XOR:%02X",
"TX UID:%08lX Btn:%02lX Cnt:%03lX Auth:%08lX Epoch:%05lX TE:%u XOR:%02X",
(unsigned long)serial,
(unsigned long)button,
(unsigned long)control,
(unsigned long)instance->hop,
(unsigned long)instance->epoch,
instance->te_variant,
instance->frame_xor);
return SubGhzProtocolStatusOk;
@@ -738,6 +847,7 @@ void subghz_protocol_decoder_fiat_v1_reset(void* context) {
instance->frame_xor = 0U;
instance->hitag2_key_valid = false;
instance->hitag2_epoch = 0U;
instance->te_variant = FIAT_V1_DEFAULT_TE_VARIANT;
memset(instance->hitag2_key, 0, sizeof(instance->hitag2_key));
fiat_v1_clear_all_cells(instance);
}
@@ -789,36 +899,28 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_v1_serialize(
}
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_hex(
flipper_format, FIAT_V1_RAW_FIELD, instance->raw_data, FIAT_V1_WIRE_BYTES);
flipper_format_delete_key(flipper_format, FIAT_V1_RAW_FIELD);
uint32_t hop = instance->hop;
uint32_t frame_xor = instance->frame_xor;
uint32_t tail_bits = instance->tail_bits;
if(!flipper_format_write_uint32(flipper_format, FIAT_V1_HOP_FIELD, &hop, 1) ||
!flipper_format_write_uint32(flipper_format, FIAT_V1_XOR_FIELD, &frame_xor, 1) ||
!flipper_format_write_uint32(flipper_format, FIAT_V1_TAIL_BITS_FIELD, &tail_bits, 1)) {
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, FF_TYPE, instance->te_variant);
pp_flipper_update_or_insert_u32(flipper_format, FIAT_V1_HOP_FIELD, instance->hop);
pp_flipper_update_or_insert_u32(flipper_format, FIAT_V1_XOR_FIELD, instance->frame_xor);
pp_flipper_update_or_insert_u32(flipper_format, FIAT_V1_TAIL_BITS_FIELD, instance->tail_bits);
if(!flipper_format_insert_or_update_hex(
flipper_format, FIAT_V1_RAW_FIELD, instance->raw_data, FIAT_V1_WIRE_BYTES)) {
return SubGhzProtocolStatusErrorParserOthers;
}
ret = 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(ret != SubGhzProtocolStatusOk) {
return ret;
}
if(instance->hitag2_key_valid) {
uint32_t epoch = instance->hitag2_epoch & 0x3FFFFUL;
if(!flipper_format_insert_or_update_hex(
flipper_format, FIAT_V1_HITAG2_KEY_FIELD, instance->hitag2_key, 6U) ||
!flipper_format_write_uint32(flipper_format, FIAT_V1_HITAG2_EPOCH_FIELD, &epoch, 1)) {
flipper_format, FIAT_V1_HITAG2_KEY_FIELD, instance->hitag2_key, 6U)) {
return SubGhzProtocolStatusErrorParserOthers;
}
pp_flipper_update_or_insert_u32(flipper_format, FIAT_V1_HITAG2_EPOCH_FIELD, epoch);
}
return pp_write_display(
@@ -829,29 +931,14 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_v1_serialize(
static void
fiat_v1_load_hitag2_key(SubGhzProtocolDecoderFiatV1* instance, FlipperFormat* flipper_format) {
uint8_t key[6] = {0};
flipper_format_rewind(flipper_format);
if(!flipper_format_read_hex(flipper_format, FIAT_V1_HITAG2_KEY_FIELD, key, 6U)) {
return;
}
uint32_t epoch = 0U;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, FIAT_V1_HITAG2_EPOCH_FIELD, &epoch, 1U)) {
epoch &= 0x3FFFFUL;
} else {
epoch = 0U;
}
memcpy(instance->hitag2_key, key, sizeof(instance->hitag2_key));
instance->hitag2_epoch = epoch;
instance->hitag2_key_valid = fiat_v1_key_matches(
instance->hitag2_key_valid = fiat_v1_resolve_hitag2_key(
flipper_format,
instance->uid,
instance->generic.btn,
(uint16_t)(instance->generic.cnt & 0x03FFU),
instance->hop,
key,
epoch);
instance->hitag2_key,
&instance->hitag2_epoch);
}
SubGhzProtocolStatus
@@ -868,6 +955,14 @@ SubGhzProtocolStatus
return SubGhzProtocolStatusErrorValueBitCount;
}
flipper_format_rewind(flipper_format);
uint32_t value = 0U;
instance->te_variant = FIAT_V1_DEFAULT_TE_VARIANT;
if(flipper_format_read_uint32(flipper_format, FF_TYPE, &value, 1)) {
instance->te_variant = (value == FIAT_V1_TE_VARIANT_A) ? FIAT_V1_TE_VARIANT_A :
FIAT_V1_TE_VARIANT_B;
}
flipper_format_rewind(flipper_format);
if(flipper_format_read_hex(
flipper_format, FIAT_V1_RAW_FIELD, instance->raw_data, FIAT_V1_WIRE_BYTES)) {
@@ -885,7 +980,6 @@ SubGhzProtocolStatus
instance->uid = instance->generic.serial;
instance->tail_bits = FIAT_V1_DEFAULT_TAIL_BITS;
uint32_t value = 0U;
if(flipper_format_read_uint32(flipper_format, FF_SERIAL, &value, 1)) {
instance->generic.serial = value;
}
@@ -924,7 +1018,7 @@ void subghz_protocol_decoder_fiat_v1_get_string(void* context, FuriString* outpu
"%08lX %03lX%01X %08lX\r\n"
"Sync:%02X UID:%08lX Auth:%08lX\r\n"
"Btn:%02X [%s] Ctrl:%03lX\r\n"
"Tail:%u XOR:%02X\r\n",
"TE:%u Tail:%u XOR:%02X\r\n",
instance->generic.protocol_name,
FIAT_V1_LOGICAL_BITS,
instance->hitag2_key_valid ? "KEY:OK" : "KEY:??",
@@ -938,6 +1032,7 @@ void subghz_protocol_decoder_fiat_v1_get_string(void* context, FuriString* outpu
instance->generic.btn,
fiat_v1_button_name(instance->generic.btn),
(unsigned long)instance->generic.cnt,
instance->te_variant,
instance->tail_bits,
instance->frame_xor);
}
+8 -9
View File
@@ -347,19 +347,18 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_v2_serialize(
}
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_hex(
flipper_format, FIAT_V2_RAW_FIELD, instance->raw_data, FIAT_V2_WIRE_BYTES);
uint32_t hop = instance->hop;
uint32_t button = instance->button;
if(!flipper_format_write_uint32(flipper_format, FIAT_V2_HOP_FIELD, &hop, 1) ||
!flipper_format_write_uint32(flipper_format, FIAT_V2_BTN_FIELD, &button, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
flipper_format_delete_key(flipper_format, FIAT_V2_RAW_FIELD);
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, FIAT_V2_HOP_FIELD, instance->hop);
pp_flipper_update_or_insert_u32(flipper_format, FIAT_V2_BTN_FIELD, instance->button);
if(!flipper_format_insert_or_update_hex(
flipper_format, FIAT_V2_RAW_FIELD, instance->raw_data, FIAT_V2_WIRE_BYTES)) {
return SubGhzProtocolStatusErrorParserOthers;
}
return SubGhzProtocolStatusOk;
}
+103 -15
View File
@@ -16,6 +16,7 @@ static const SubGhzBlockConst kia_protocol_v0_const = {
#define KIA_V0_TYPE_KIA 1U
#define KIA_V0_TYPE_SUZUKI 2U
#define KIA_V0_TYPE_HONDA 3U
#define KIA_V0_TYPE_MITSU 4U
#define KIA_V0_BIT_COUNT_KIA 61U
#define KIA_V0_BIT_COUNT_SUZUKI 64U
@@ -29,17 +30,20 @@ static const SubGhzBlockConst kia_protocol_v0_const = {
#define KIA_V0_SUZUKI_GAP_SPAN 500U
#define KIA_V0_TYPE1_SYNC 750U
#define KIA_V0_TYPE1_PREAMBLE_PAIRS 0x13FU
#define KIA_V0_TYPE2_PREAMBLE_PAIRS 0x140U
#define KIA_V0_TAIL_PREAMBLE_PAIRS 0x0FU
#define KIA_V0_TYPE1_PREAMBLE_PAIRS 0x13FU
#define KIA_V0_TYPE2_PREAMBLE_PAIRS 0x140U
#define KIA_V0_TAIL_PREAMBLE_PAIRS 0x0FU
#define KIA_V0_MITSU_TAIL_PREAMBLE_PAIRS 0x50U
#define KIA_V0_MITSU_PREAMBLE_MIN 72U
#define KIA_V0_MITSU_PREAMBLE_MAX 88U
#define KIA_V0_UPLOAD_CAPACITY \
((KIA_V0_TYPE2_PREAMBLE_PAIRS * 2U) + (KIA_V0_BIT_COUNT_SUZUKI * 2U) + 3U + \
(KIA_V0_TAIL_PREAMBLE_PAIRS * 2U) + (KIA_V0_BIT_COUNT_SUZUKI * 2U))
#define KIA_V0_UPLOAD_CAPACITY \
((KIA_V0_TYPE2_PREAMBLE_PAIRS * 2U) + 2U + (KIA_V0_BIT_COUNT_SUZUKI * 2U) + 3U + \
(KIA_V0_MITSU_TAIL_PREAMBLE_PAIRS * 2U) + (KIA_V0_BIT_COUNT_SUZUKI * 2U) + 4U)
_Static_assert(
KIA_V0_UPLOAD_CAPACITY <= PP_SHARED_UPLOAD_CAPACITY,
"KIA_V0_UPLOAD_CAPACITY exceeds shared upload slab");
#define KIA_V0_ENCODER_DEFAULT_REPEAT 10U
#define KIA_V0_ENCODER_DEFAULT_REPEAT 3U
typedef enum {
KiaV0DecoderStepReset = 0,
@@ -64,6 +68,8 @@ struct SubGhzProtocolDecoderKIA {
uint16_t packet_bit_count;
uint16_t preamble_pairs;
uint8_t type;
uint64_t last_kia_data;
bool have_last_kia;
};
struct SubGhzProtocolEncoderKIA {
@@ -328,6 +334,8 @@ static const char* kia_v0_protocol_name(uint8_t type) {
return "Suzuki V0";
case KIA_V0_TYPE_HONDA:
return "Honda V0";
case KIA_V0_TYPE_MITSU:
return "Mitsubishi V0";
default:
return KIA_PROTOCOL_V0_NAME;
}
@@ -378,7 +386,7 @@ static void kia_v0_parse_data(
kia_v0_parse_family_raw(generic->data, type, fields);
generic->data_count_bit = (type == KIA_V0_TYPE_SUZUKI) ? KIA_V0_BIT_COUNT_SUZUKI :
KIA_V0_BIT_COUNT_KIA;
if(type == KIA_V0_TYPE_KIA) {
if((type == KIA_V0_TYPE_KIA) || (type == KIA_V0_TYPE_MITSU)) {
fields->crc_valid = kia_v0_verify_crc_poly(generic->data);
}
}
@@ -427,7 +435,15 @@ static void kia_v0_decoder_finish_kia_or_honda_at_gap(SubGhzProtocolDecoderKIA*
const uint64_t data = instance->decoder.decode_data;
if(kia_v0_verify_crc_poly(data)) {
kia_v0_decoder_commit(instance, data, KIA_V0_TYPE_KIA, KIA_V0_BIT_COUNT_KIA);
uint8_t type = KIA_V0_TYPE_KIA;
if(instance->have_last_kia && (instance->last_kia_data == data) &&
(instance->preamble_pairs >= KIA_V0_MITSU_PREAMBLE_MIN) &&
(instance->preamble_pairs <= KIA_V0_MITSU_PREAMBLE_MAX)) {
type = KIA_V0_TYPE_MITSU;
}
instance->last_kia_data = data;
instance->have_last_kia = true;
kia_v0_decoder_commit(instance, data, type, KIA_V0_BIT_COUNT_KIA);
return;
}
@@ -527,6 +543,26 @@ static void kia_v0_build_kia_upload(SubGhzProtocolEncoderKIA* instance, uint64_t
instance->encoder.size_upload = index;
}
static void kia_v0_build_mitsu_upload(SubGhzProtocolEncoderKIA* instance, uint64_t raw) {
size_t index = 0;
instance->encoder.upload[index++] = level_duration_make(true, KIA_V0_TYPE1_SYNC);
instance->encoder.upload[index++] = level_duration_make(false, KIA_V0_TYPE1_SYNC);
index =
kia_v0_append_short_pairs(instance->encoder.upload, index, KIA_V0_TYPE1_PREAMBLE_PAIRS);
index = kia_v0_append_data_pairs(instance->encoder.upload, index, raw, KIA_V0_BIT_COUNT_KIA);
instance->encoder.upload[index++] = level_duration_make(true, 1500);
instance->encoder.upload[index++] = level_duration_make(false, 1500);
index = kia_v0_append_short_pairs(
instance->encoder.upload, index, KIA_V0_MITSU_TAIL_PREAMBLE_PAIRS);
index = kia_v0_append_data_pairs(instance->encoder.upload, index, raw, KIA_V0_BIT_COUNT_KIA);
instance->encoder.upload[index++] = level_duration_make(true, 1500);
instance->encoder.upload[index++] = level_duration_make(false, 1500);
instance->encoder.front = 0;
instance->encoder.size_upload = index;
}
#endif
#if PROTOPIRATE_WITH_ENCODER
@@ -557,6 +593,23 @@ static uint8_t kia_v0_infer_type_from_bits(uint32_t bits) {
if(bits == KIA_V0_BIT_COUNT_HONDA) return KIA_V0_TYPE_HONDA;
return KIA_V0_TYPE_KIA;
}
static uint8_t kia_v0_type_from_protocol_name(const char* name) {
if(!name) {
return 0;
}
if((strcmp(name, "Mitsu v0") == 0) || (strcmp(name, "Mitsu V0") == 0) ||
(strcmp(name, "Mitsubishi v0") == 0) || (strcmp(name, "Mitsubishi V0") == 0)) {
return KIA_V0_TYPE_MITSU;
}
if((strcmp(name, "Suzuki") == 0) || (strcmp(name, "Suzuki V0") == 0)) {
return KIA_V0_TYPE_SUZUKI;
}
if((strcmp(name, "Honda v0") == 0) || (strcmp(name, "Honda V0") == 0)) {
return KIA_V0_TYPE_HONDA;
}
return 0;
}
#if PROTOPIRATE_WITH_ENCODER
static void kia_v0_encoder_apply_fields(SubGhzProtocolEncoderKIA* instance) {
@@ -605,7 +658,11 @@ static void kia_v0_encoder_apply_fields(SubGhzProtocolEncoderKIA* instance) {
instance->fields.crc);
instance->generic.data_count_bit = KIA_V0_BIT_COUNT_KIA;
kia_v0_parse_data(&instance->generic, instance->type, &instance->fields, NULL);
kia_v0_build_kia_upload(instance, instance->generic.data);
if(instance->type == KIA_V0_TYPE_MITSU) {
kia_v0_build_mitsu_upload(instance, instance->generic.data);
} else {
kia_v0_build_kia_upload(instance, instance->generic.data);
}
}
}
}
@@ -699,7 +756,16 @@ SubGhzProtocolStatus
if(pp_verify_protocol_name(flipper_format, instance->base.protocol->name) !=
SubGhzProtocolStatusOk) {
return SubGhzProtocolStatusErrorParserProtocolName;
FuriString* proto = furi_string_alloc();
bool named_ok = false;
flipper_format_rewind(flipper_format);
if(flipper_format_read_string(flipper_format, FF_PROTOCOL, proto)) {
named_ok = kia_v0_type_from_protocol_name(furi_string_get_cstr(proto)) != 0;
}
furi_string_free(proto);
if(!named_ok) {
return SubGhzProtocolStatusErrorParserProtocolName;
}
}
static const uint16_t allowed_bits[] = {
@@ -709,8 +775,20 @@ SubGhzProtocolStatus
if(bit_st != SubGhzProtocolStatusOk) return bit_st;
uint32_t type_u32 = kia_v0_infer_type_from_bits(bits);
pp_encoder_read_fields(flipper_format, NULL, NULL, NULL, &type_u32);
if(type_u32 < KIA_V0_TYPE_KIA || type_u32 > KIA_V0_TYPE_HONDA) {
uint32_t type_from_file = 0;
pp_encoder_read_fields(flipper_format, NULL, NULL, NULL, &type_from_file);
if(type_from_file >= KIA_V0_TYPE_KIA && type_from_file <= KIA_V0_TYPE_MITSU) {
type_u32 = type_from_file;
} else {
FuriString* proto = furi_string_alloc();
flipper_format_rewind(flipper_format);
if(flipper_format_read_string(flipper_format, FF_PROTOCOL, proto)) {
const uint8_t named = kia_v0_type_from_protocol_name(furi_string_get_cstr(proto));
if(named) type_u32 = named;
}
furi_string_free(proto);
}
if(type_u32 < KIA_V0_TYPE_KIA || type_u32 > KIA_V0_TYPE_MITSU) {
return SubGhzProtocolStatusErrorValueBitCount;
}
instance->type = (uint8_t)type_u32;
@@ -834,6 +912,8 @@ void subghz_protocol_decoder_kia_reset(void* context) {
SubGhzProtocolDecoderKIA* instance = context;
kia_v0_decoder_state_clear(instance);
instance->type = 0;
instance->have_last_kia = false;
instance->last_kia_data = 0;
}
void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration) {
@@ -1030,10 +1110,18 @@ SubGhzProtocolStatus
uint32_t type_u32 = kia_v0_infer_type_from_bits(bits);
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, FF_TYPE, &type_u32, 1)) {
if(flipper_format_read_uint32(flipper_format, FF_TYPE, &type_u32, 1) &&
(type_u32 >= KIA_V0_TYPE_KIA) && (type_u32 <= KIA_V0_TYPE_MITSU)) {
instance->type = (uint8_t)type_u32;
} else {
instance->type = (uint8_t)kia_v0_infer_type_from_bits(bits);
FuriString* proto = furi_string_alloc();
uint8_t named = 0;
flipper_format_rewind(flipper_format);
if(flipper_format_read_string(flipper_format, FF_PROTOCOL, proto)) {
named = kia_v0_type_from_protocol_name(furi_string_get_cstr(proto));
}
furi_string_free(proto);
instance->type = named ? named : (uint8_t)kia_v0_infer_type_from_bits(bits);
}
KiaV0Fields scratch;
+68 -48
View File
@@ -2,14 +2,16 @@
#include "protocols_common.h"
#include <string.h>
#define KIA_V7_PREAMBLE_PAIRS 0x13FU
#define KIA_V7_TAIL_PREAMBLE_PAIRS 0x0FU
#define KIA_V7_PREAMBLE_MIN_PAIRS 16
#define KIA_V7_HEADER 0x4C
#define KIA_V7_TAIL_GAP_US 0x7D0
#define KIA_V7_KEY_BITS 64U
#define KIA_V7_DEFAULT_TX_REPEAT 3U
#define KIA_V7_FRAME_SLOTS(pairs) (((pairs) * 2U) + 1U + (KIA_V7_KEY_BITS * 2U) + 2U)
#define KIA_V7_UPLOAD_CAPACITY \
(1U + (KIA_V7_PREAMBLE_PAIRS * 2U) + 1U + (KIA_V7_KEY_BITS * 2U) + 2U)
#define KIA_V7_PREAMBLE_PAIRS 0x13F
#define KIA_V7_PREAMBLE_MIN_PAIRS 16
#define KIA_V7_HEADER 0x4C
#define KIA_V7_TAIL_GAP_US 0x7D0
#define KIA_V7_KEY_BITS 64U
#define KIA_V7_DEFAULT_TX_REPEAT 10U
(KIA_V7_FRAME_SLOTS(KIA_V7_PREAMBLE_PAIRS) + KIA_V7_FRAME_SLOTS(KIA_V7_TAIL_PREAMBLE_PAIRS))
_Static_assert(
KIA_V7_UPLOAD_CAPACITY <= PP_SHARED_UPLOAD_CAPACITY,
"KIA_V7_UPLOAD_CAPACITY exceeds shared upload slab");
@@ -166,58 +168,76 @@ static void kia_v7_decode_key_encoder(SubGhzProtocolEncoderKiaV7* instance) {
&instance->crc_valid);
}
static bool kia_v7_encoder_get_upload(SubGhzProtocolEncoderKiaV7* instance) {
furi_check(instance);
static bool kia_v7_encoder_append_frame(
LevelDuration* upload,
size_t* index,
size_t max_size,
uint64_t data,
uint8_t bit_count,
size_t preamble_pairs) {
const LevelDuration high_short = level_duration_make(true, kia_protocol_v7_const.te_short);
const LevelDuration low_short = level_duration_make(false, kia_protocol_v7_const.te_short);
const LevelDuration low_tail = level_duration_make(false, KIA_V7_TAIL_GAP_US);
const size_t max_size = KIA_V7_UPLOAD_CAPACITY;
for(size_t i = 0; i < preamble_pairs; i++) {
if((*index + 2U) > max_size) {
return false;
}
upload[(*index)++] = high_short;
upload[(*index)++] = low_short;
}
if((*index + 1U) > max_size) {
return false;
}
upload[(*index)++] = high_short;
for(int32_t bit = (int32_t)bit_count - 1; bit >= 0; bit--) {
if((*index + 2U) > max_size) {
return false;
}
const bool value = ((data >> bit) & 1ULL) != 0ULL;
upload[(*index)++] = value ? high_short : low_short;
upload[(*index)++] = value ? low_short : high_short;
}
if((*index + 2U) > max_size) {
return false;
}
upload[(*index)++] = high_short;
upload[(*index)++] = low_tail;
return true;
}
static bool kia_v7_encoder_get_upload(SubGhzProtocolEncoderKiaV7* instance) {
furi_check(instance);
const uint8_t bit_count = (instance->tx_bit_count > 0U && instance->tx_bit_count <= 64U) ?
instance->tx_bit_count :
64U;
size_t index = 0;
size_t final_size = 0;
for(uint8_t pass = 0; pass < 2; pass++) {
size_t index = pass;
for(size_t i = 0; i < KIA_V7_PREAMBLE_PAIRS; i++) {
if((index + 2U) > max_size) {
return false;
}
instance->encoder.upload[index++] = high_short;
instance->encoder.upload[index++] = low_short;
}
if((index + 1U) > max_size) {
return false;
}
instance->encoder.upload[index++] = high_short;
for(int32_t bit = (int32_t)bit_count - 1; bit >= 0; bit--) {
if((index + 2U) > max_size) {
return false;
}
const bool value = ((instance->generic.data >> bit) & 1ULL) != 0ULL;
instance->encoder.upload[index++] = value ? high_short : low_short;
instance->encoder.upload[index++] = value ? low_short : high_short;
}
if((index + 2U) > max_size) {
return false;
}
instance->encoder.upload[index++] = high_short;
instance->encoder.upload[index++] = low_tail;
final_size = index;
if(!kia_v7_encoder_append_frame(
instance->encoder.upload,
&index,
KIA_V7_UPLOAD_CAPACITY,
instance->generic.data,
bit_count,
KIA_V7_PREAMBLE_PAIRS)) {
return false;
}
if(!kia_v7_encoder_append_frame(
instance->encoder.upload,
&index,
KIA_V7_UPLOAD_CAPACITY,
instance->generic.data,
bit_count,
KIA_V7_TAIL_PREAMBLE_PAIRS)) {
return false;
}
instance->encoder.front = 0;
instance->encoder.size_upload = final_size;
instance->encoder.size_upload = index;
return true;
}
#endif
-261
View File
@@ -1,261 +0,0 @@
#include "mitsubishi_v0.h"
#include "protocols_common.h"
#include <string.h>
// Original implementation by @lupettohf
#define MITSUBISHI_BIT_COUNT 96
#define MITSUBISHI_DATA_BYTES 12
static const SubGhzBlockConst subghz_protocol_mitsubishi_const = {
.te_short = 250,
.te_long = 500,
.te_delta = 100,
.min_count_bit_for_found = 80,
};
typedef enum {
MitsubishiDecoderStepReset = 0,
MitsubishiDecoderStepDataSave,
MitsubishiDecoderStepDataCheck,
} MitsubishiDecoderStep;
struct SubGhzProtocolDecoderMitsubishi {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint8_t decoder_state;
uint16_t bit_count;
uint8_t decode_data[MITSUBISHI_DATA_BYTES];
};
static void mitsubishi_unscramble_payload(uint8_t* payload) {
for(uint8_t i = 0; i < 8; i++) {
payload[i] = (uint8_t)~payload[i];
}
uint16_t counter = ((uint16_t)payload[4] << 8) | payload[5];
uint8_t hi = (counter >> 8) & 0xFF;
uint8_t lo = counter & 0xFF;
uint8_t mask1 = (hi & 0xAAU) | (lo & 0x55U);
uint8_t mask2 = (lo & 0xAAU) | (hi & 0x55U);
uint8_t mask3 = mask1 ^ mask2;
for(uint8_t i = 0; i < 5; i++) {
payload[i] ^= mask3;
}
}
static void mitsubishi_reset_payload(SubGhzProtocolDecoderMitsubishi* instance) {
instance->bit_count = 0;
memset(instance->decode_data, 0, sizeof(instance->decode_data));
}
static bool mitsubishi_collect_pair(
SubGhzProtocolDecoderMitsubishi* instance,
uint32_t high,
uint32_t low) {
bool bit_value;
if(pp_is_short(high, &subghz_protocol_mitsubishi_const) &&
pp_is_long(low, &subghz_protocol_mitsubishi_const)) {
bit_value = true;
} else if(
pp_is_long(high, &subghz_protocol_mitsubishi_const) &&
pp_is_short(low, &subghz_protocol_mitsubishi_const)) {
bit_value = false;
} else {
return false;
}
uint16_t bit_index = instance->bit_count;
if(bit_index < MITSUBISHI_BIT_COUNT) {
if(bit_value) {
uint8_t byte_index = bit_index >> 3;
uint8_t bit_position = 7 - (bit_index & 0x07);
instance->decode_data[byte_index] |= (1U << bit_position);
}
instance->bit_count++;
}
return true;
}
static void mitsubishi_publish_frame(SubGhzProtocolDecoderMitsubishi* instance) {
uint8_t payload[MITSUBISHI_DATA_BYTES];
memcpy(payload, instance->decode_data, sizeof(payload));
mitsubishi_unscramble_payload(payload);
instance->generic.data_count_bit = instance->bit_count;
instance->generic.serial = ((uint32_t)payload[0] << 24) | ((uint32_t)payload[1] << 16) |
((uint32_t)payload[2] << 8) | payload[3];
instance->generic.cnt = ((uint16_t)payload[4] << 8) | payload[5];
instance->generic.btn = payload[6];
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
}
const SubGhzProtocolDecoder subghz_protocol_mitsubishi_decoder = {
.alloc = subghz_protocol_decoder_mitsubishi_alloc,
.free = pp_decoder_free_default,
.feed = subghz_protocol_decoder_mitsubishi_feed,
.reset = subghz_protocol_decoder_mitsubishi_reset,
.get_hash_data = subghz_protocol_decoder_mitsubishi_get_hash_data,
.serialize = subghz_protocol_decoder_mitsubishi_serialize,
.deserialize = subghz_protocol_decoder_mitsubishi_deserialize,
.get_string = subghz_protocol_decoder_mitsubishi_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_mitsubishi_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol mitsubishi_v0_protocol = {
.name = MITSUBISHI_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
#if PROTOPIRATE_WITH_DECODER
.decoder = &subghz_protocol_mitsubishi_decoder,
#else
.decoder = NULL,
#endif
#if PROTOPIRATE_WITH_ENCODER
.encoder = &subghz_protocol_mitsubishi_encoder,
#else
.encoder = NULL,
#endif
};
void* subghz_protocol_decoder_mitsubishi_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderMitsubishi* instance = calloc(1, sizeof(SubGhzProtocolDecoderMitsubishi));
furi_check(instance);
instance->base.protocol = &mitsubishi_v0_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_mitsubishi_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
instance->decoder_state = MitsubishiDecoderStepReset;
instance->decoder.te_last = 0;
instance->generic.data_count_bit = 0;
mitsubishi_reset_payload(instance);
}
void subghz_protocol_decoder_mitsubishi_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
switch(instance->decoder_state) {
case MitsubishiDecoderStepReset:
if(level) {
instance->decoder.te_last = duration;
instance->decoder_state = MitsubishiDecoderStepDataCheck;
}
break;
case MitsubishiDecoderStepDataSave:
if(level) {
instance->decoder.te_last = duration;
instance->decoder_state = MitsubishiDecoderStepDataCheck;
} else {
instance->decoder_state = MitsubishiDecoderStepReset;
mitsubishi_reset_payload(instance);
}
break;
case MitsubishiDecoderStepDataCheck:
if(!level) {
if(mitsubishi_collect_pair(instance, instance->decoder.te_last, duration)) {
if(instance->bit_count >= MITSUBISHI_BIT_COUNT) {
mitsubishi_publish_frame(instance);
mitsubishi_reset_payload(instance);
instance->decoder_state = MitsubishiDecoderStepReset;
} else {
instance->decoder_state = MitsubishiDecoderStepDataSave;
}
} else {
mitsubishi_reset_payload(instance);
instance->decoder_state = MitsubishiDecoderStepReset;
}
} else {
instance->decoder.te_last = duration;
}
break;
}
}
uint8_t subghz_protocol_decoder_mitsubishi_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
uint8_t hash = 0;
for(size_t i = 0; i < sizeof(instance->decode_data); i++) {
hash ^= instance->decode_data[i];
}
return hash;
}
SubGhzProtocolStatus subghz_protocol_decoder_mitsubishi_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
SubGhzProtocolStatus ret =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(ret == SubGhzProtocolStatusOk) {
pp_serialize_fields(
flipper_format,
PP_FIELD_SERIAL | PP_FIELD_BTN | PP_FIELD_CNT,
instance->generic.serial,
instance->generic.btn,
instance->generic.cnt,
0);
}
return ret;
}
SubGhzProtocolStatus
subghz_protocol_decoder_mitsubishi_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
subghz_protocol_mitsubishi_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
uint32_t btn = instance->generic.btn;
pp_encoder_read_fields(
flipper_format, &instance->generic.serial, &btn, &instance->generic.cnt, NULL);
instance->generic.btn = (uint8_t)btn;
}
return ret;
}
void subghz_protocol_decoder_mitsubishi_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Sn:%08lX Cnt:%04lX\r\n"
"Btn:%02X\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
instance->generic.serial,
instance->generic.cnt,
instance->generic.btn);
}
-31
View File
@@ -1,31 +0,0 @@
#pragma once
#include <furi.h>
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/types.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
#include <flipper_format/flipper_format.h>
#include "../defines.h"
#define MITSUBISHI_PROTOCOL_NAME "Mitsubishi V0"
typedef struct SubGhzProtocolDecoderMitsubishi SubGhzProtocolDecoderMitsubishi;
extern const SubGhzProtocol mitsubishi_v0_protocol;
void* subghz_protocol_decoder_mitsubishi_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_mitsubishi_free(void* context);
void subghz_protocol_decoder_mitsubishi_reset(void* context);
void subghz_protocol_decoder_mitsubishi_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_mitsubishi_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_mitsubishi_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_mitsubishi_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_mitsubishi_get_string(void* context, FuriString* output);
+2 -2
View File
@@ -9,9 +9,9 @@
#include "../kia_v1.h"
#include "../kia_v2.h"
#include "../mazda_v0.h"
#include "../porsche_touareg.h"
#include "../psa.h"
#include "../renault_v0.h"
#include "../renault_v1.h"
#include "../subaru.h"
#include "../star_line.h"
#include "../honda_v1.h"
@@ -27,9 +27,9 @@ static const SubGhzProtocol* const protopirate_protocol_registry_am_items[] = {
&kia_protocol_v1,
&kia_protocol_v2,
&mazda_v0_protocol,
&porsche_touareg_protocol,
&psa_protocol,
&renault_v0_protocol,
&renault_v1_protocol,
&subaru_protocol,
&subghz_protocol_star_line,
};
+2 -2
View File
@@ -7,8 +7,8 @@
#include "../kia_v6.h"
#include "../kia_v7.h"
#include "../mazda_v0.h"
#include "../mitsubishi_v0.h"
#include "../psa.h"
#include "../renault_v1.h"
static const SubGhzProtocol* const protopirate_protocol_registry_fm_items[] = {
&subghz_protocol_scher_khan,
@@ -17,9 +17,9 @@ static const SubGhzProtocol* const protopirate_protocol_registry_fm_items[] = {
&kia_protocol_v5,
&kia_protocol_v6,
&mazda_v0_protocol,
&mitsubishi_v0_protocol,
&kia_protocol_v7,
&psa_protocol,
&renault_v1_protocol,
};
static const SubGhzProtocolRegistry protopirate_protocol_registry_fm = {
-378
View File
@@ -1,378 +0,0 @@
#include "porsche_touareg.h"
#include "protocols_common.h"
#include <string.h>
// Original implementation by @lupettohf
#define PORSCHE_CAYENNE_BIT_COUNT 64
#define PC_TE_SYNC 3370U
#define PC_TE_GAP 5930U
#define PC_SYNC_MIN 15
static const SubGhzBlockConst subghz_protocol_porsche_cayenne_const = {
.te_short = 1680,
.te_long = 3370,
.te_delta = 500,
.min_count_bit_for_found = PORSCHE_CAYENNE_BIT_COUNT,
};
typedef enum {
PorscheCayenneDecoderStepReset = 0,
PorscheCayenneDecoderStepSync,
PorscheCayenneDecoderStepGapHigh,
PorscheCayenneDecoderStepGapLow,
PorscheCayenneDecoderStepData,
} PorscheCayenneDecoderStep;
struct SubGhzProtocolDecoderPorscheCayenne {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint16_t sync_count;
uint64_t raw_data;
uint8_t bit_count;
};
static void porsche_cayenne_compute_frame(
uint32_t serial24,
uint8_t btn,
uint16_t counter,
uint8_t frame_type,
uint8_t* pkt) {
uint8_t b0 = (uint8_t)((btn << 4) | (frame_type & 0x07));
uint8_t b1 = (serial24 >> 16) & 0xFF;
uint8_t b2 = (serial24 >> 8) & 0xFF;
uint8_t b3 = serial24 & 0xFF;
uint16_t cnt = counter + 1;
uint8_t cnt_lo = cnt & 0xFF;
uint8_t cnt_hi = (cnt >> 8) & 0xFF;
uint8_t r_h = b3;
uint8_t r_m = b1;
uint8_t r_l = b2;
#define ROTATE24(rh, rm, rl) \
do { \
uint8_t _ch = ((rh) >> 7) & 1U; \
uint8_t _cm = ((rm) >> 7) & 1U; \
uint8_t _cl = ((rl) >> 7) & 1U; \
(rh) = (uint8_t)(((rh) << 1) | _cm); \
(rm) = (uint8_t)(((rm) << 1) | _cl); \
(rl) = (uint8_t)(((rl) << 1) | _ch); \
} while(0)
for(uint8_t i = 0; i < 4; i++) {
ROTATE24(r_h, r_m, r_l);
}
for(uint16_t i = 0; i < cnt_lo; i++) {
ROTATE24(r_h, r_m, r_l);
}
#undef ROTATE24
uint8_t a9a = r_h ^ b0;
uint8_t nb9b_p1 = (uint8_t)((~cnt_lo << 2) & 0xFC) ^ r_m;
uint8_t nb9b_p2 = (uint8_t)((~cnt_hi << 2) & 0xFC) ^ r_m;
uint8_t nb9b_p3 = (uint8_t)((~cnt_hi >> 6) & 0x03) ^ r_m;
uint8_t a9b = (nb9b_p1 & 0xCC) | (nb9b_p2 & 0x30) | (nb9b_p3 & 0x03);
uint8_t nb9c_p1 = (uint8_t)((~cnt_lo >> 2) & 0x3F) ^ r_l;
uint8_t nb9c_p2 = (uint8_t)((~cnt_hi & 0x03) << 6) ^ r_l;
uint8_t nb9c_p3 = (uint8_t)((~cnt_hi >> 2) & 0x3F) ^ r_l;
uint8_t a9c = (nb9c_p1 & 0x33) | (nb9c_p2 & 0xC0) | (nb9c_p3 & 0x0C);
pkt[0] = b0;
pkt[1] = b1;
pkt[2] = b2;
pkt[3] = b3;
pkt[4] = (uint8_t)(((a9a >> 2) & 0x3F) | ((~cnt_lo & 0x03U) << 6));
pkt[5] = (uint8_t)((~cnt_lo & 0xC0U) | ((a9a & 0x03U) << 4) | (a9b & 0x0CU) |
((~cnt_lo >> 2) & 0x03U));
pkt[6] = (uint8_t)(((a9b & 0x03U) << 6) | ((a9c >> 2) & 0x3CU) | ((~cnt_lo >> 4) & 0x03U));
pkt[7] = (uint8_t)(((a9b >> 4) & 0x0FU) | ((a9c & 0x0FU) << 4));
}
static void porsche_cayenne_parse_data(SubGhzProtocolDecoderPorscheCayenne* instance) {
uint8_t pkt[8];
uint64_t raw = instance->generic.data;
for(int8_t i = 7; i >= 0; i--) {
pkt[i] = (uint8_t)(raw & 0xFF);
raw >>= 8;
}
instance->generic.serial = ((uint32_t)pkt[1] << 16) | ((uint32_t)pkt[2] << 8) | pkt[3];
instance->generic.btn = (uint8_t)(pkt[0] >> 4);
instance->generic.cnt = 0;
uint8_t frame_type = pkt[0] & 0x07;
uint8_t try_pkt[8];
for(uint16_t try_cnt = 1; try_cnt <= 256; try_cnt++) {
porsche_cayenne_compute_frame(
instance->generic.serial,
instance->generic.btn,
(uint16_t)(try_cnt - 1),
frame_type,
try_pkt);
if(try_pkt[4] == pkt[4] && try_pkt[5] == pkt[5] && try_pkt[6] == pkt[6] &&
try_pkt[7] == pkt[7]) {
instance->generic.cnt = try_cnt;
break;
}
}
}
static void porsche_cayenne_publish_frame(SubGhzProtocolDecoderPorscheCayenne* instance) {
instance->generic.data = instance->raw_data;
instance->generic.data_count_bit = PORSCHE_CAYENNE_BIT_COUNT;
porsche_cayenne_parse_data(instance);
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
}
const SubGhzProtocolDecoder subghz_protocol_porsche_cayenne_decoder = {
.alloc = subghz_protocol_decoder_porsche_cayenne_alloc,
.free = pp_decoder_free_default,
.feed = subghz_protocol_decoder_porsche_cayenne_feed,
.reset = subghz_protocol_decoder_porsche_cayenne_reset,
.get_hash_data = subghz_protocol_decoder_porsche_cayenne_get_hash_data,
.serialize = subghz_protocol_decoder_porsche_cayenne_serialize,
.deserialize = subghz_protocol_decoder_porsche_cayenne_deserialize,
.get_string = subghz_protocol_decoder_porsche_cayenne_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_porsche_cayenne_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol porsche_touareg_protocol = {
.name = PORSCHE_CAYENNE_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
#if PROTOPIRATE_WITH_DECODER
.decoder = &subghz_protocol_porsche_cayenne_decoder,
#else
.decoder = NULL,
#endif
#if PROTOPIRATE_WITH_ENCODER
.encoder = &subghz_protocol_porsche_cayenne_encoder,
#else
.encoder = NULL,
#endif
};
void* subghz_protocol_decoder_porsche_cayenne_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderPorscheCayenne* instance =
calloc(1, sizeof(SubGhzProtocolDecoderPorscheCayenne));
furi_check(instance);
instance->base.protocol = &porsche_touareg_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_porsche_cayenne_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
instance->decoder.te_last = 0;
instance->sync_count = 0;
instance->raw_data = 0;
instance->bit_count = 0;
instance->generic.data = 0;
instance->generic.data_count_bit = 0;
}
void subghz_protocol_decoder_porsche_cayenne_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
const uint32_t te_short = subghz_protocol_porsche_cayenne_const.te_short;
const uint32_t te_long = subghz_protocol_porsche_cayenne_const.te_long;
const uint32_t te_delta = subghz_protocol_porsche_cayenne_const.te_delta;
switch(instance->decoder.parser_step) {
case PorscheCayenneDecoderStepReset:
if(!level && DURATION_DIFF(duration, PC_TE_SYNC) < te_delta) {
instance->sync_count = 1;
instance->decoder.parser_step = PorscheCayenneDecoderStepSync;
}
break;
case PorscheCayenneDecoderStepSync:
if(level) {
if(DURATION_DIFF(duration, PC_TE_SYNC) < te_delta) {
// keep collecting sync pairs
} else if(
instance->sync_count >= PC_SYNC_MIN &&
DURATION_DIFF(duration, PC_TE_GAP) < te_delta) {
instance->decoder.parser_step = PorscheCayenneDecoderStepGapLow;
} else {
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
}
} else {
if(DURATION_DIFF(duration, PC_TE_SYNC) < te_delta) {
instance->sync_count++;
} else if(
instance->sync_count >= PC_SYNC_MIN &&
DURATION_DIFF(duration, PC_TE_GAP) < te_delta) {
instance->decoder.parser_step = PorscheCayenneDecoderStepGapHigh;
} else {
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
}
}
break;
case PorscheCayenneDecoderStepGapHigh:
if(level && DURATION_DIFF(duration, PC_TE_GAP) < te_delta) {
instance->raw_data = 0;
instance->bit_count = 0;
instance->decoder.parser_step = PorscheCayenneDecoderStepData;
} else {
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
}
break;
case PorscheCayenneDecoderStepGapLow:
if(!level && DURATION_DIFF(duration, PC_TE_GAP) < te_delta) {
instance->raw_data = 0;
instance->bit_count = 0;
instance->decoder.parser_step = PorscheCayenneDecoderStepData;
} else {
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
}
break;
case PorscheCayenneDecoderStepData:
if(level) {
bool bit_value = false;
if(DURATION_DIFF(instance->decoder.te_last, te_short) < te_delta &&
DURATION_DIFF(duration, te_long) < te_delta) {
bit_value = false;
} else if(
DURATION_DIFF(instance->decoder.te_last, te_long) < te_delta &&
DURATION_DIFF(duration, te_short) < te_delta) {
bit_value = true;
} else {
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
break;
}
instance->raw_data = (instance->raw_data << 1) | (bit_value ? 1U : 0U);
instance->bit_count++;
if(instance->bit_count >= PORSCHE_CAYENNE_BIT_COUNT) {
porsche_cayenne_publish_frame(instance);
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
}
} else {
instance->decoder.te_last = duration;
}
break;
}
}
uint8_t subghz_protocol_decoder_porsche_cayenne_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
SubGhzBlockDecoder decoder = {
.decode_data = instance->generic.data,
.decode_count_bit = instance->generic.data_count_bit,
};
return subghz_protocol_blocks_get_hash_data(&decoder, (decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_porsche_cayenne_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
SubGhzProtocolStatus ret =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(ret == SubGhzProtocolStatusOk) {
pp_serialize_fields(
flipper_format,
PP_FIELD_SERIAL | PP_FIELD_BTN | PP_FIELD_CNT,
instance->generic.serial & 0xFFFFFF,
instance->generic.btn,
instance->generic.cnt,
0);
}
return ret;
}
SubGhzProtocolStatus subghz_protocol_decoder_porsche_cayenne_deserialize(
void* context,
FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
subghz_protocol_porsche_cayenne_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
porsche_cayenne_parse_data(instance);
uint32_t serial = 0;
if(flipper_format_read_uint32(flipper_format, FF_SERIAL, &serial, 1)) {
instance->generic.serial = serial & 0xFFFFFF;
}
uint32_t cnt = 0;
if(flipper_format_read_uint32(flipper_format, FF_CNT, &cnt, 1)) {
instance->generic.cnt = cnt;
}
uint32_t btn = 0;
if(flipper_format_read_uint32(flipper_format, FF_BTN, &btn, 1)) {
instance->generic.btn = (uint8_t)btn;
}
}
return ret;
}
void subghz_protocol_decoder_porsche_cayenne_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
uint8_t frame_type = (uint8_t)((instance->generic.data >> 56) & 0x07);
const char* frame_type_name = "??";
if(frame_type == 0x02) {
frame_type_name = "First";
} else if(frame_type == 0x01) {
frame_type_name = "Cont";
} else if(frame_type == 0x04) {
frame_type_name = "Final";
}
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Sn:%06lX Btn:%X\r\n"
"Cnt:%04lX FT:%s\r\n"
"Raw:%08lX%08lX\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(unsigned long)(instance->generic.serial & 0xFFFFFF),
(unsigned int)instance->generic.btn,
(unsigned long)instance->generic.cnt,
frame_type_name,
(unsigned long)(instance->generic.data >> 32),
(unsigned long)(instance->generic.data & 0xFFFFFFFF));
}
-32
View File
@@ -1,32 +0,0 @@
#pragma once
#include <furi.h>
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/types.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
#include <flipper_format/flipper_format.h>
#include "../defines.h"
#define PORSCHE_CAYENNE_PROTOCOL_NAME "Porsche Touareg"
typedef struct SubGhzProtocolDecoderPorscheCayenne SubGhzProtocolDecoderPorscheCayenne;
extern const SubGhzProtocol porsche_touareg_protocol;
void* subghz_protocol_decoder_porsche_cayenne_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_porsche_cayenne_free(void* context);
void subghz_protocol_decoder_porsche_cayenne_reset(void* context);
void subghz_protocol_decoder_porsche_cayenne_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_porsche_cayenne_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_porsche_cayenne_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus subghz_protocol_decoder_porsche_cayenne_deserialize(
void* context,
FlipperFormat* flipper_format);
void subghz_protocol_decoder_porsche_cayenne_get_string(void* context, FuriString* output);
+20 -2
View File
@@ -55,10 +55,9 @@ static const ProtoPirateProtocolCatalogEntry protopirate_protocol_catalog[] = {
{"Kia V7", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v7")},
{"Honda V2", ProtoPirateProtocolCatalogRouteFMF4, PROTOPIRATE_TX_KEY("honda_v2")},
{"Mazda V0", ProtoPirateProtocolCatalogRouteByModulation, PROTOPIRATE_TX_KEY("mazda_v0")},
{"Mitsubishi V0", ProtoPirateProtocolCatalogRouteFMDefault, NULL},
{"Porsche Touareg", ProtoPirateProtocolCatalogRouteAMDefault, NULL},
{"PSA", ProtoPirateProtocolCatalogRouteByModulation, PROTOPIRATE_TX_KEY("psa")},
{"Renault V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("renault_v0")},
{"Renault V1", ProtoPirateProtocolCatalogRouteByModulation, PROTOPIRATE_TX_KEY("renault_v1")},
{"Scher-Khan", ProtoPirateProtocolCatalogRouteFMDefault, NULL},
{"Star Line", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("star_line")},
{"Subaru", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("subaru")},
@@ -74,6 +73,10 @@ static const ProtoPirateProtocolCatalogAlias protopirate_protocol_catalog_aliase
{"Suzuki", "Kia V0"},
{"Suzuki V0", "Kia V0"},
{"Honda V0", "Kia V0"},
{"Mitsu V0", "Kia V0"},
{"Mitsu v0", "Kia V0"},
{"Mitsubishi V0", "Kia V0"},
{"Mitsubishi v0", "Kia V0"},
{"Land Rover V0", "Honda V2"},
{"VW", "VAG"},
};
@@ -162,6 +165,12 @@ bool protopirate_protocol_catalog_can_tx(const char* protocol_name) {
return protopirate_protocol_catalog_tx_key(protocol_name) != NULL;
}
bool protopirate_protocol_catalog_offers_bruteforce(const char* protocol_name) {
const char* canonical = protopirate_protocol_catalog_canonical_name(protocol_name);
return protopirate_catalog_string_equal(canonical, "PSA") ||
protopirate_catalog_string_equal(canonical, "Renault V1");
}
const char* protopirate_protocol_catalog_tx_key(const char* protocol_name) {
const ProtoPirateProtocolCatalogEntry* entry =
protopirate_protocol_catalog_find(protocol_name);
@@ -181,6 +190,12 @@ const char*
if(protopirate_catalog_string_equal(protocol_name, "Honda V0")) {
return "Honda V0";
}
if(protopirate_catalog_string_equal(protocol_name, "Mitsu V0") ||
protopirate_catalog_string_equal(protocol_name, "Mitsu v0") ||
protopirate_catalog_string_equal(protocol_name, "Mitsubishi V0") ||
protopirate_catalog_string_equal(protocol_name, "Mitsubishi v0")) {
return "Mitsubishi V0";
}
const char* canonical_name = protopirate_protocol_catalog_canonical_name(protocol_name);
if(protopirate_catalog_string_equal(canonical_name, "Kia V0")) {
@@ -190,6 +205,9 @@ const char*
if(protocol_type == 3U) {
return "Honda V0";
}
if(protocol_type == 4U) {
return "Mitsubishi V0";
}
}
return canonical_name;
+2
View File
@@ -34,6 +34,8 @@ const char* protopirate_protocol_catalog_canonical_name(const char* protocol_nam
bool protopirate_protocol_catalog_can_tx(const char* protocol_name);
bool protopirate_protocol_catalog_offers_bruteforce(const char* protocol_name);
const char* protopirate_protocol_catalog_tx_key(const char* protocol_name);
const char*
+9 -7
View File
@@ -130,6 +130,13 @@ static const ProtoPirateProtocolTiming protocol_timings[] = {
.te_delta = 60,
.min_count_bit = 82,
},
{
.name = "Renault V1",
.te_short = 125,
.te_long = 250,
.te_delta = 50,
.min_count_bit = 88,
},
{
.name = "Mazda V0",
.te_short = 250,
@@ -144,13 +151,6 @@ static const ProtoPirateProtocolTiming protocol_timings[] = {
.te_delta = 100,
.min_count_bit = 81,
},
{
.name = "Porsche Touareg",
.te_short = 1680,
.te_long = 3370,
.te_delta = 500,
.min_count_bit = 64,
},
{
.name = "Subaru",
.te_short = 800,
@@ -206,6 +206,8 @@ const ProtoPirateProtocolTiming* protopirate_get_protocol_timing(const char* pro
} aliases[] = {
{"Honda V0", "Kia V0"},
{"Land Rover V0", "Honda V2"},
{"Mitsu", "Kia V0"},
{"Mitsubishi V0", "Kia V0"},
{"Suzuki", "Kia V0"},
{"V3", "Kia V3/V4"},
{"V4", "Kia V3/V4"},
+10 -9
View File
@@ -196,17 +196,17 @@ SubGhzProtocolStatus pp_serialize_fields(
uint32_t type) {
if(!ff) return SubGhzProtocolStatusError;
if((field_mask & PP_FIELD_SERIAL) && !flipper_format_write_uint32(ff, FF_SERIAL, &serial, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
if(field_mask & PP_FIELD_SERIAL) {
pp_flipper_update_or_insert_u32(ff, FF_SERIAL, serial);
}
if((field_mask & PP_FIELD_BTN) && !flipper_format_write_uint32(ff, FF_BTN, &btn, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
if(field_mask & PP_FIELD_BTN) {
pp_flipper_update_or_insert_u32(ff, FF_BTN, btn);
}
if((field_mask & PP_FIELD_CNT) && !flipper_format_write_uint32(ff, FF_CNT, &cnt, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
if(field_mask & PP_FIELD_CNT) {
pp_flipper_update_or_insert_u32(ff, FF_CNT, cnt);
}
if((field_mask & PP_FIELD_TYPE) && !flipper_format_write_uint32(ff, FF_TYPE, &type, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
if(field_mask & PP_FIELD_TYPE) {
pp_flipper_update_or_insert_u32(ff, FF_TYPE, type);
}
return SubGhzProtocolStatusOk;
}
@@ -221,8 +221,9 @@ SubGhzProtocolStatus
return SubGhzProtocolStatusError;
}
furi_string_printf(display, "%s - %s", protocol_name, suffix);
flipper_format_rewind(ff);
SubGhzProtocolStatus status =
flipper_format_write_string_cstr(ff, "Disp", furi_string_get_cstr(display)) ?
flipper_format_insert_or_update_string_cstr(ff, "Disp", furi_string_get_cstr(display)) ?
SubGhzProtocolStatusOk :
SubGhzProtocolStatusErrorParserOthers;
furi_string_free(display);
+337 -978
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -16,8 +16,6 @@
extern const SubGhzProtocol renault_v0_protocol;
bool renault_v0_flipper_is_rolling(FlipperFormat* flipper_format);
void* subghz_protocol_decoder_renault_v0_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_renault_v0_reset(void* context);
void subghz_protocol_decoder_renault_v0_feed(void* context, bool level, uint32_t duration);
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include <furi.h>
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/types.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
#include <flipper_format/flipper_format.h>
#include "../defines.h"
#define RENAULT_PROTOCOL_V1_NAME "Renault V1"
typedef struct SubGhzProtocolDecoderRenaultV1 SubGhzProtocolDecoderRenaultV1;
typedef struct SubGhzProtocolEncoderRenaultV1 SubGhzProtocolEncoderRenaultV1;
#define HITAG2_BF_STATUS_IDLE 0
#define HITAG2_BF_STATUS_RUNNING 1
#define HITAG2_BF_STATUS_FOUND 2
#define HITAG2_BF_STATUS_NOT_FOUND 3
#define HITAG2_BF_STATUS_CANCELLED 4
typedef struct Hitag2BfState Hitag2BfState;
struct Hitag2BfState {
volatile uint8_t cancel;
volatile uint32_t progress_current;
volatile uint8_t status;
volatile uint32_t progress_total;
uint8_t frame[11];
uint8_t iv[4];
void (*on_done)(void* context);
void* on_done_ctx;
};
int32_t hitag2_brute_force_thread_entry(void* arg);
bool hitag2_bf_state_from_flipper_format(Hitag2BfState* state, FlipperFormat* ff);
bool hitag2_bf_needs_bruteforce(FlipperFormat* ff, bool require_renault_v1);
bool hitag2_bf_patch_flipper_format_on_success(FlipperFormat* ff, const Hitag2BfState* state);
bool hitag2_bf_patch_flipper_format_on_miss(FlipperFormat* ff);
bool hitag2_flipper_format_get_string(FlipperFormat* ff, FuriString* output);
extern const SubGhzProtocol renault_v1_protocol;
void* subghz_protocol_decoder_renault_v1_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_renault_v1_reset(void* context);
void subghz_protocol_decoder_renault_v1_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_renault_v1_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_renault_v1_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_renault_v1_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_renault_v1_get_string(void* context, FuriString* output);
#if PROTOPIRATE_WITH_ENCODER
void* subghz_protocol_encoder_renault_v1_alloc(SubGhzEnvironment* environment);
SubGhzProtocolStatus
subghz_protocol_encoder_renault_v1_deserialize(void* context, FlipperFormat* flipper_format);
#endif
+193 -64
View File
@@ -15,6 +15,7 @@
#include "../../protocols/kia_v7.h"
#include "../../protocols/psa.h"
#include "../../protocols/renault_v0.h"
#include "../../protocols/renault_v1.h"
#include <input/input.h>
#include <gui/canvas.h>
@@ -46,7 +47,8 @@
#define EMU_PRESET_KEY_HITAG2_KEY "Hitag2 Key"
#define EMU_PRESET_KEY_HITAG2_EPOCH "Hitag2 Epoch"
#define EMU_CUSTOM_PRESET_KEY "Custom_preset_data"
#define EMU_FIAT_V1_KEY_TEXT_LEN 12U
#define EMU_HITAG2_KEY_TEXT_LEN 12U
#define EMU_HITAG2_RECOVERED_YES 1U
typedef struct {
uint32_t original_counter;
@@ -63,7 +65,7 @@ typedef struct {
bool flag_stop_called;
bool replay_only;
Storage* storage;
char hitag2_key_text[EMU_FIAT_V1_KEY_TEXT_LEN + 1U];
char hitag2_key_text[EMU_HITAG2_KEY_TEXT_LEN + 1U];
} EmulateContext;
typedef struct {
@@ -75,32 +77,57 @@ typedef struct {
static EmulateContext* emulate_context = NULL;
static const ProtoPirateEmulateHostApi* g_host_api = NULL;
static bool emulate_hex_nibble(char c, uint8_t* nibble) {
if(c >= '0' && c <= '9') {
*nibble = (uint8_t)(c - '0');
return true;
}
if(c >= 'A' && c <= 'F') {
*nibble = (uint8_t)(c - 'A' + 10);
return true;
}
if(c >= 'a' && c <= 'f') {
*nibble = (uint8_t)(c - 'a' + 10);
return true;
static bool emulate_hitag2_key_nonzero(const uint8_t key[6]) {
for(size_t i = 0; i < 6U; i++) {
if(key[i]) {
return true;
}
}
return false;
}
static bool emulate_has_hitag2_key(FlipperFormat* flipper_format) {
if(!flipper_format) return false;
uint8_t key[6] = {0};
flipper_format_rewind(flipper_format);
return flipper_format_read_hex(flipper_format, EMU_PRESET_KEY_HITAG2_KEY, key, sizeof(key));
}
static bool emulate_has_nonzero_hitag2_key(FlipperFormat* flipper_format) {
if(!flipper_format) return false;
uint8_t key[6] = {0};
flipper_format_rewind(flipper_format);
if(!flipper_format_read_hex(flipper_format, EMU_PRESET_KEY_HITAG2_KEY, key, sizeof(key))) {
return false;
}
return emulate_hitag2_key_nonzero(key);
}
static bool emulate_parse_hitag2_key_text(const char* text, uint8_t key[6]) {
if(!text || !key) return false;
if(!text || !key) {
return false;
}
uint8_t hex_count = 0U;
uint8_t high_nibble = 0U;
for(size_t i = 0U; text[i] != '\0'; i++) {
if(text[i] == ' ') continue;
const char c = text[i];
if(c == ' ') {
continue;
}
uint8_t nibble = 0U;
if(!emulate_hex_nibble(text[i], &nibble) || hex_count >= EMU_FIAT_V1_KEY_TEXT_LEN) {
if(c >= '0' && c <= '9') {
nibble = (uint8_t)(c - '0');
} else if(c >= 'A' && c <= 'F') {
nibble = (uint8_t)(c - 'A' + 10);
} else if(c >= 'a' && c <= 'f') {
nibble = (uint8_t)(c - 'a' + 10);
} else {
return false;
}
if(hex_count >= 12U) {
return false;
}
if((hex_count & 1U) == 0U) {
@@ -110,15 +137,46 @@ static bool emulate_parse_hitag2_key_text(const char* text, uint8_t key[6]) {
}
hex_count++;
}
return hex_count == EMU_FIAT_V1_KEY_TEXT_LEN;
return hex_count == 12U;
}
static bool emulate_has_hitag2_key(FlipperFormat* flipper_format) {
if(!flipper_format) return false;
uint8_t key[6] = {0};
static bool emulate_write_hitag2_key(FlipperFormat* flipper_format, const uint8_t key[6]) {
if(!flipper_format || !key) {
return false;
}
flipper_format_rewind(flipper_format);
return flipper_format_read_hex(flipper_format, EMU_PRESET_KEY_HITAG2_KEY, key, sizeof(key));
if(!flipper_format_insert_or_update_hex(
flipper_format, EMU_PRESET_KEY_HITAG2_KEY, key, 6U)) {
return false;
}
uint32_t epoch = 0U;
flipper_format_rewind(flipper_format);
if(!flipper_format_read_uint32(flipper_format, EMU_PRESET_KEY_HITAG2_EPOCH, &epoch, 1U)) {
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(
flipper_format, EMU_PRESET_KEY_HITAG2_EPOCH, &epoch, 1U);
}
flipper_format_rewind(flipper_format);
return true;
}
static bool emulate_hitag2_recovered_yes(FlipperFormat* flipper_format) {
if(!flipper_format) {
return false;
}
uint8_t recovered_hex = 0;
flipper_format_rewind(flipper_format);
if(flipper_format_read_hex(flipper_format, "Recovered", &recovered_hex, 1)) {
return recovered_hex == EMU_HITAG2_RECOVERED_YES;
}
uint32_t recovered_u32 = 0;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Recovered", &recovered_u32, 1)) {
return (uint8_t)recovered_u32 == EMU_HITAG2_RECOVERED_YES;
}
return false;
}
static void emulate_request_nav_pop(ProtoPirateApp* app) {
@@ -133,9 +191,9 @@ static void emulate_request_nav_after_exit(ProtoPirateApp* app) {
}
}
static bool emulate_prompt_fiat_v1_key(ProtoPirateApp* app, EmulateContext* ctx);
static bool emulate_prompt_hitag2_key(ProtoPirateApp* app, EmulateContext* ctx);
static void emulate_fiat_v1_key_input_callback(void* context) {
static void emulate_hitag2_key_input_callback(void* context) {
ProtoPirateApp* app = context;
EmulateContext* ctx = emulate_context;
uint8_t key[6] = {0};
@@ -146,34 +204,24 @@ static void emulate_fiat_v1_key_input_callback(void* context) {
notification_message(app->notifications, &sequence_error);
}
if(app && ctx) {
(void)emulate_prompt_fiat_v1_key(app, ctx);
(void)emulate_prompt_hitag2_key(app, ctx);
}
return;
}
flipper_format_rewind(ctx->flipper_format);
if(!flipper_format_insert_or_update_hex(
ctx->flipper_format, EMU_PRESET_KEY_HITAG2_KEY, key, sizeof(key))) {
if(!emulate_write_hitag2_key(ctx->flipper_format, key)) {
notification_message(app->notifications, &sequence_error);
emulate_request_nav_pop(app);
return;
}
uint32_t epoch = 0U;
flipper_format_rewind(ctx->flipper_format);
if(!flipper_format_read_uint32(ctx->flipper_format, EMU_PRESET_KEY_HITAG2_EPOCH, &epoch, 1U)) {
flipper_format_rewind(ctx->flipper_format);
flipper_format_insert_or_update_uint32(
ctx->flipper_format, EMU_PRESET_KEY_HITAG2_EPOCH, &epoch, 1U);
}
view_dispatcher_switch_to_view(app->view_dispatcher, ProtoPirateViewAbout);
if(app->view_about) {
view_commit_model(app->view_about, true);
}
}
static bool emulate_prompt_fiat_v1_key(ProtoPirateApp* app, EmulateContext* ctx) {
static bool emulate_prompt_hitag2_key(ProtoPirateApp* app, EmulateContext* ctx) {
furi_check(app);
furi_check(ctx);
@@ -186,7 +234,7 @@ static bool emulate_prompt_fiat_v1_key(ProtoPirateApp* app, EmulateContext* ctx)
text_input_set_header_text(app->text_input, "HITAG2 key (12 hex):");
text_input_set_result_callback(
app->text_input,
emulate_fiat_v1_key_input_callback,
emulate_hitag2_key_input_callback,
app,
ctx->hitag2_key_text,
sizeof(ctx->hitag2_key_text),
@@ -195,6 +243,22 @@ static bool emulate_prompt_fiat_v1_key(ProtoPirateApp* app, EmulateContext* ctx)
return true;
}
static bool emulate_needs_hitag2_prompt(EmulateContext* ctx) {
if(!ctx || !ctx->flipper_format) {
return false;
}
if(furi_string_equal(ctx->protocol_name, FIAT_V1_PROTOCOL_NAME)) {
return !emulate_has_hitag2_key(ctx->flipper_format);
}
if(furi_string_equal(ctx->protocol_name, RENAULT_PROTOCOL_V1_NAME)) {
if(emulate_hitag2_recovered_yes(ctx->flipper_format)) {
return false;
}
return !emulate_has_nonzero_hitag2_key(ctx->flipper_format);
}
return false;
}
static bool emu_preset_name_is_custom_marker(const char* preset_name) {
return preset_name && (!strcmp(preset_name, "Custom") || !strcmp(preset_name, "CUSTOM") ||
!strcmp(preset_name, "FuriHalSubGhzPresetCustom") ||
@@ -784,10 +848,34 @@ static uint8_t emu_button_for_protocol(
default:
return original;
}
} else if(strstr(protocol, RENAULT_PROTOCOL_V1_NAME)) {
switch(key) {
case InputKeyUp:
return 0x1; // Lock
case InputKeyOk:
return 0x2; // Unlock
case InputKeyDown:
return 0x2; // Unlock
case InputKeyLeft:
return 0x4; // Trunk
case InputKeyRight:
return 0x8; // Panic
default:
return original;
}
} else if(strstr(protocol, RENAULT_PROTOCOL_V0_NAME)) {
switch(key) {
case InputKeyOk:
return 0x06; // Lock
case InputKeyUp:
return 0x0A; // Unlock
case InputKeyDown:
return 0x05; // Trunk
default:
return original;
}
} else if(strstr(protocol, "Fiat")) {
return original;
} else if(strstr(protocol, "Porsche")) {
return original;
} else if(strstr(protocol, "Scher")) {
return original;
} else if(strstr(protocol, "Star Line")) {
@@ -796,19 +884,6 @@ static uint8_t emu_button_for_protocol(
return original;
}
static bool emulate_renault_is_rolling(FlipperFormat* ff) {
if(!ff) {
return false;
}
uint32_t rolling = 0U;
flipper_format_rewind(ff);
if(flipper_format_read_uint32(ff, "Rolling", &rolling, 1)) {
return rolling != 0U;
}
return false;
}
static bool emulate_update_data(EmulateContext* ctx, uint8_t button) {
if(!ctx || !ctx->flipper_format) return false;
@@ -1115,14 +1190,24 @@ static void plugin_on_enter(void* context) {
furi_string_set(ctx->protocol_name, "Unknown");
}
ctx->replay_only = furi_string_equal(ctx->protocol_name, RENAULT_PROTOCOL_V0_NAME) &&
!emulate_renault_is_rolling(ctx->flipper_format);
ctx->replay_only = false;
// Standalone Suzuki/Honda V0 captures: merged into Kia V0
// Standalone Suzuki/Honda/Mitsubishi V0 captures: merged into Kia V0
if(furi_string_equal(ctx->protocol_name, "Suzuki") ||
furi_string_equal(ctx->protocol_name, "Suzuki V0") ||
furi_string_equal(ctx->protocol_name, "Honda V0")) {
uint32_t kia_v0_type = furi_string_equal(ctx->protocol_name, "Honda V0") ? 3U : 2U;
furi_string_equal(ctx->protocol_name, "Honda V0") ||
furi_string_equal(ctx->protocol_name, "Mitsu V0") ||
furi_string_equal(ctx->protocol_name, "Mitsu v0") ||
furi_string_equal(ctx->protocol_name, "Mitsubishi V0") ||
furi_string_equal(ctx->protocol_name, "Mitsubishi v0")) {
uint32_t kia_v0_type =
furi_string_equal(ctx->protocol_name, "Honda V0") ? 3U :
(furi_string_equal(ctx->protocol_name, "Mitsu V0") ||
furi_string_equal(ctx->protocol_name, "Mitsu v0") ||
furi_string_equal(ctx->protocol_name, "Mitsubishi V0") ||
furi_string_equal(ctx->protocol_name, "Mitsubishi v0")) ?
4U :
2U;
furi_string_set(ctx->protocol_name, KIA_PROTOCOL_V0_NAME);
flipper_format_rewind(ctx->flipper_format);
flipper_format_insert_or_update_string_cstr(
@@ -1166,15 +1251,59 @@ static void plugin_on_enter(void* context) {
ctx->current_counter = ctx->original_counter;
}
if(furi_string_equal(ctx->protocol_name, FIAT_V1_PROTOCOL_NAME)) {
uint8_t raw[13] = {0};
bool have_raw = false;
flipper_format_rewind(ctx->flipper_format);
if(flipper_format_read_hex(ctx->flipper_format, "Raw", raw, sizeof(raw)) && raw[0] == 0x00U &&
raw[1] == 0x01U) {
have_raw = true;
}
if(ctx->serial == 0U) {
if(have_raw) {
ctx->serial = ((uint32_t)raw[2] << 24U) | ((uint32_t)raw[3] << 16U) |
((uint32_t)raw[4] << 8U) | raw[5];
} else {
uint8_t key_bytes[8] = {0};
flipper_format_rewind(ctx->flipper_format);
if(flipper_format_read_hex(ctx->flipper_format, "Key", key_bytes, sizeof(key_bytes))) {
ctx->serial = ((uint32_t)key_bytes[0] << 24U) | ((uint32_t)key_bytes[1] << 16U) |
((uint32_t)key_bytes[2] << 8U) | key_bytes[3];
}
}
if(ctx->serial != 0U) {
flipper_format_rewind(ctx->flipper_format);
flipper_format_insert_or_update_uint32(
ctx->flipper_format, EMU_PRESET_KEY_SERIAL, &ctx->serial, 1);
}
}
if(have_raw && (ctx->current_counter > 0x3FFU || ctx->original_counter > 0x3FFU)) {
const uint32_t ctrl =
((uint32_t)(raw[6] & 0x0FU) << 6U) | ((uint32_t)raw[7] >> 2U);
ctx->original_counter = ctrl;
ctx->current_counter = ctrl;
flipper_format_rewind(ctx->flipper_format);
flipper_format_insert_or_update_uint32(
ctx->flipper_format, EMU_PRESET_KEY_CNT, &ctrl, 1);
if(ctx->original_button == 0U) {
ctx->original_button = (uint8_t)(raw[6] >> 4U);
uint32_t btn = ctx->original_button;
flipper_format_rewind(ctx->flipper_format);
flipper_format_insert_or_update_uint32(
ctx->flipper_format, EMU_PRESET_KEY_BTN, &btn, 1);
}
}
}
view_set_draw_callback(app->view_about, emulate_draw_callback);
view_set_input_callback(app->view_about, emulate_input_callback);
view_set_context(app->view_about, app);
view_set_previous_callback(app->view_about, NULL);
if(furi_string_equal(ctx->protocol_name, FIAT_V1_PROTOCOL_NAME) &&
!emulate_has_hitag2_key(ctx->flipper_format)) {
if(!emulate_prompt_fiat_v1_key(app, ctx)) {
FURI_LOG_E(TAG, "Failed to show Fiat V1 HITAG2 key input");
if(emulate_needs_hitag2_prompt(ctx)) {
if(!emulate_prompt_hitag2_key(app, ctx)) {
FURI_LOG_E(TAG, "Failed to show HITAG2 key input");
notification_message(app->notifications, &sequence_error);
emulate_context_free();
emulate_request_nav_pop(app);
+196 -64
View File
@@ -2,6 +2,7 @@
#include "../../defines.h"
#include "../../protocols/psa_bf_core.h"
#include "../../protocols/renault_v1.h"
#include "../../protocols/protocols_common.h"
#include "../../helpers/protopirate_types.h"
@@ -18,15 +19,69 @@
#define PSA_BF_PROGRESS_BAR_Y 24
#define PSA_BF_PROGRESS_BAR_H 8
typedef enum {
ProtoPirateBfKindNone = 0,
ProtoPirateBfKindPsa,
ProtoPirateBfKindHitag2,
} ProtoPirateBfKind;
static const ProtoPiratePsaBfHostApi* g_host_api = NULL;
static ProtoPirateBfKind g_bf_kind = ProtoPirateBfKindNone;
static PsaBfState* g_bf_state = NULL;
static Hitag2BfState* g_hitag2_state = NULL;
static FuriThread* g_bf_thread = NULL;
static ProtoPiratePsaBfContext g_active_ctx = ProtoPiratePsaBfContextReceiverInfo;
static void show_bf_result(void* app, uint8_t status, ButtonCallback callback);
static void bf_finish_and_show_result(void* app, ButtonCallback result_callback);
static uint8_t bf_status(void) {
if(g_bf_kind == ProtoPirateBfKindHitag2 && g_hitag2_state) {
return g_hitag2_state->status;
}
if(g_bf_state) {
return g_bf_state->status;
}
return PSA_BF_STATUS_IDLE;
}
static void bf_progress_values(uint32_t* cur, uint32_t* total) {
if(g_bf_kind == ProtoPirateBfKindHitag2 && g_hitag2_state) {
*cur = g_hitag2_state->progress_current;
*total = g_hitag2_state->progress_total;
return;
}
if(g_bf_state) {
*cur = g_bf_state->progress_current;
*total = g_bf_state->progress_total;
return;
}
*cur = 0;
*total = 0;
}
static void bf_set_cancel(void) {
if(g_hitag2_state) {
g_hitag2_state->cancel = 1;
}
if(g_bf_state) {
g_bf_state->cancel = 1;
}
}
static void bf_free_states(void) {
if(g_bf_state) {
free(g_bf_state);
g_bf_state = NULL;
}
if(g_hitag2_state) {
free(g_hitag2_state);
g_hitag2_state = NULL;
}
g_bf_kind = ProtoPirateBfKindNone;
}
static bool item_needs_bruteforce_from_ff(FlipperFormat* ff, bool require_psa_protocol) {
if(!ff) return false;
FuriString* s = furi_string_alloc();
@@ -52,46 +107,76 @@ static bool item_needs_bruteforce_from_ff(FlipperFormat* ff, bool require_psa_pr
static void show_bf_progress(void* app) {
Widget* widget = g_host_api->get_widget(app);
if(!widget || !g_bf_state) return;
if(!widget || (!g_bf_state && !g_hitag2_state)) return;
widget_reset(widget);
widget_add_icon_element(widget, 0, 5, &I_DolphinWait_59x54);
widget_add_string_element(widget, 62, 0, AlignLeft, AlignTop, FontPrimary, "Bruteforcing...");
uint32_t cur = g_bf_state->progress_current;
uint32_t total = g_bf_state->progress_total;
uint32_t cur = 0;
uint32_t total = 0;
bf_progress_values(&cur, &total);
uint32_t pct_tenths = total ? (uint32_t)((uint64_t)cur * 1000 / total) : 0;
if(pct_tenths > 1000) pct_tenths = 1000;
if(g_bf_kind == ProtoPirateBfKindHitag2) {
widget_add_string_element(widget, 62, 0, AlignLeft, AlignTop, FontPrimary, "Recover Key");
widget_add_string_element(widget, 62, 12, AlignLeft, AlignTop, FontSecondary, "Max ETA:");
widget_add_string_element(widget, 62, 22, AlignLeft, AlignTop, FontSecondary, "60 seconds");
} else {
widget_add_string_element(widget, 62, 0, AlignLeft, AlignTop, FontPrimary, "Bruteforcing...");
}
FuriString* pct_str =
furi_string_alloc_printf("%lu.%u%%", pct_tenths / 10, (unsigned)(pct_tenths % 10));
widget_add_string_element(
widget, 62, 12, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(pct_str));
widget,
62,
(g_bf_kind == ProtoPirateBfKindHitag2) ? 34 : 12,
AlignLeft,
AlignTop,
FontSecondary,
furi_string_get_cstr(pct_str));
furi_string_free(pct_str);
const uint8_t bar_y = (g_bf_kind == ProtoPirateBfKindHitag2) ? 46 : PSA_BF_PROGRESS_BAR_Y;
widget_add_rect_element(
widget,
PSA_BF_PROGRESS_BAR_X,
PSA_BF_PROGRESS_BAR_Y,
bar_y,
PSA_BF_PROGRESS_BAR_W,
PSA_BF_PROGRESS_BAR_H,
2,
false);
static uint16_t bf_frame = 0;
bf_frame++;
uint8_t inner_w = PSA_BF_PROGRESS_BAR_W - 4;
uint8_t block_w = 16;
uint8_t travel = inner_w - block_w;
uint16_t phase = (bf_frame * 2) % (uint16_t)(2 * travel);
uint8_t block_x = (phase <= travel) ? (uint8_t)phase : (uint8_t)(2 * travel - phase);
widget_add_rect_element(
widget,
PSA_BF_PROGRESS_BAR_X + 2 + block_x,
PSA_BF_PROGRESS_BAR_Y + 2,
block_w,
PSA_BF_PROGRESS_BAR_H - 4,
0,
true);
if(g_bf_kind == ProtoPirateBfKindHitag2) {
uint8_t fill_w = total ? (uint8_t)(((uint64_t)cur * inner_w) / total) : 0;
if(fill_w > 0) {
widget_add_rect_element(
widget,
PSA_BF_PROGRESS_BAR_X + 2,
bar_y + 2,
fill_w,
PSA_BF_PROGRESS_BAR_H - 4,
0,
true);
}
} else {
static uint16_t bf_frame = 0;
bf_frame++;
uint8_t block_w = 16;
uint8_t travel = inner_w - block_w;
uint16_t phase = (bf_frame * 2) % (uint16_t)(2 * travel);
uint8_t block_x = (phase <= travel) ? (uint8_t)phase : (uint8_t)(2 * travel - phase);
widget_add_rect_element(
widget,
PSA_BF_PROGRESS_BAR_X + 2 + block_x,
bar_y + 2,
block_w,
PSA_BF_PROGRESS_BAR_H - 4,
0,
true);
}
}
static void bf_result_ok_callback(GuiButtonType result, InputType type, void* context) {
@@ -125,9 +210,32 @@ static void show_bf_result(void* app, uint8_t status, ButtonCallback callback) {
}
}
static void apply_success_to_history(void* app, PsaBfState* s) {
static void hitag2_refresh_history_text(void* app, FlipperFormat* ff) {
if(!app || !ff || !g_host_api || !g_host_api->history_set_item_str) {
return;
}
FuriString* text = furi_string_alloc();
if(hitag2_flipper_format_get_string(ff, text)) {
g_host_api->history_set_item_str(
app, g_host_api->get_history_index(app), furi_string_get_cstr(text));
}
furi_string_free(text);
}
static void apply_success_to_history(void* app) {
FlipperFormat* ff = g_host_api->get_history_flipper_format(app);
uint16_t idx = g_host_api->get_history_index(app);
if(g_bf_kind == ProtoPirateBfKindHitag2 && g_hitag2_state) {
if(ff) {
hitag2_bf_patch_flipper_format_on_success(ff, g_hitag2_state);
hitag2_refresh_history_text(app, ff);
}
return;
}
if(!g_bf_state) {
return;
}
PsaBfState* s = g_bf_state;
if(ff) {
g_host_api->patch_flipper_format_on_success(ff, s);
}
@@ -153,10 +261,9 @@ static void apply_success_to_history(void* app, PsaBfState* s) {
}
static void bf_finish_and_show_result(void* app, ButtonCallback result_callback) {
if(!g_bf_state) return;
if(!g_bf_state && !g_hitag2_state) return;
PsaBfState* s = g_bf_state;
uint8_t status = s->status;
uint8_t status = bf_status();
if(g_bf_thread) {
furi_thread_join(g_bf_thread);
@@ -165,7 +272,7 @@ static void bf_finish_and_show_result(void* app, ButtonCallback result_callback)
}
if(status == PSA_BF_STATUS_FOUND) {
apply_success_to_history(app, s);
apply_success_to_history(app);
if(g_active_ctx == ProtoPiratePsaBfContextSubDecode) {
g_host_api->notification_success(app);
}
@@ -175,28 +282,32 @@ static void bf_finish_and_show_result(void* app, ButtonCallback result_callback)
}
show_bf_result(app, status, ok_cb);
} else {
free(g_bf_state);
g_bf_state = NULL;
if(status == PSA_BF_STATUS_NOT_FOUND && g_bf_kind == ProtoPirateBfKindHitag2) {
FlipperFormat* ff = g_host_api->get_history_flipper_format(app);
if(ff) {
hitag2_bf_patch_flipper_format_on_miss(ff);
hitag2_refresh_history_text(app, ff);
}
}
bf_free_states();
show_bf_result(app, status, NULL);
}
}
static void bf_cancel_thread(void) {
if(g_bf_thread) {
if(g_bf_state) g_bf_state->cancel = 1;
bf_set_cancel();
furi_thread_join(g_bf_thread);
furi_thread_free(g_bf_thread);
g_bf_thread = NULL;
}
if(g_bf_state) {
free(g_bf_state);
g_bf_state = NULL;
}
bf_free_states();
}
static bool plugin_needs_bruteforce(void* app, ProtoPiratePsaBfContext ctx) {
FlipperFormat* ff = g_host_api->get_history_flipper_format(app);
return item_needs_bruteforce_from_ff(ff, ctx == ProtoPiratePsaBfContextReceiverInfo);
const bool require = ctx == ProtoPiratePsaBfContextReceiverInfo;
return item_needs_bruteforce_from_ff(ff, require) || hitag2_bf_needs_bruteforce(ff, require);
}
static bool plugin_is_running(void* app) {
@@ -206,11 +317,11 @@ static bool plugin_is_running(void* app) {
static void plugin_on_scene_enter(void* app, ProtoPiratePsaBfContext ctx) {
g_active_ctx = ctx;
if(g_bf_thread && g_bf_state) {
if(g_bf_state->status == PSA_BF_STATUS_RUNNING) {
if(g_bf_thread && (g_bf_state || g_hitag2_state)) {
if(bf_status() == PSA_BF_STATUS_RUNNING) {
show_bf_progress(app);
} else {
show_bf_result(app, g_bf_state->status, NULL);
show_bf_result(app, bf_status(), NULL);
}
}
}
@@ -221,23 +332,46 @@ static bool start_bruteforce(void* app) {
FlipperFormat* ff = g_host_api->get_history_flipper_format(app);
if(!ff || !plugin_needs_bruteforce(app, g_active_ctx)) return false;
PsaBfState* state = malloc(sizeof(PsaBfState));
if(!state) {
g_host_api->notification_error(app);
const bool require = g_active_ctx == ProtoPiratePsaBfContextReceiverInfo;
if(item_needs_bruteforce_from_ff(ff, require)) {
PsaBfState* state = malloc(sizeof(PsaBfState));
if(!state) {
g_host_api->notification_error(app);
return false;
}
if(!psa_bf_state_from_flipper_format(state, ff)) {
free(state);
g_host_api->notification_error(app);
return false;
}
state->on_done = NULL;
state->on_done_ctx = NULL;
g_bf_state = state;
g_bf_kind = ProtoPirateBfKindPsa;
g_bf_thread = furi_thread_alloc_ex("PsaBf", 2048, psa_brute_force_thread_entry, state);
} else if(hitag2_bf_needs_bruteforce(ff, require)) {
Hitag2BfState* state = malloc(sizeof(Hitag2BfState));
if(!state) {
g_host_api->notification_error(app);
return false;
}
if(!hitag2_bf_state_from_flipper_format(state, ff)) {
free(state);
g_host_api->notification_error(app);
return false;
}
state->on_done = NULL;
state->on_done_ctx = NULL;
g_hitag2_state = state;
g_bf_kind = ProtoPirateBfKindHitag2;
g_bf_thread =
furi_thread_alloc_ex("Hitag2Bf", 2048, hitag2_brute_force_thread_entry, state);
} else {
return false;
}
if(!psa_bf_state_from_flipper_format(state, ff)) {
free(state);
g_host_api->notification_error(app);
return false;
}
state->on_done = NULL;
state->on_done_ctx = NULL;
g_bf_state = state;
g_bf_thread = furi_thread_alloc_ex("PsaBf", 2048, psa_brute_force_thread_entry, state);
if(!g_bf_thread) {
free(state);
g_bf_state = NULL;
bf_free_states();
g_host_api->notification_error(app);
return false;
}
@@ -251,24 +385,23 @@ static bool
g_active_ctx = ctx;
if(event.type == SceneManagerEventTypeBack) {
if(g_bf_state && g_bf_state->status == PSA_BF_STATUS_FOUND) {
if(bf_status() == PSA_BF_STATUS_FOUND) {
if(ctx == ProtoPiratePsaBfContextReceiverInfo) {
g_host_api->receiver_info_rebuild_widget(app);
}
free(g_bf_state);
g_bf_state = NULL;
bf_free_states();
return true;
}
if(g_bf_thread && g_bf_state && g_bf_state->status == PSA_BF_STATUS_RUNNING) {
g_bf_state->cancel = 1;
if(g_bf_thread && bf_status() == PSA_BF_STATUS_RUNNING) {
bf_set_cancel();
return true;
}
return false;
}
if(event.type == SceneManagerEventTypeTick) {
if(g_bf_thread && g_bf_state) {
uint8_t bfst = g_bf_state->status;
if(g_bf_thread && (g_bf_state || g_hitag2_state)) {
uint8_t bfst = bf_status();
if(bfst == PSA_BF_STATUS_IDLE || bfst == PSA_BF_STATUS_RUNNING) {
show_bf_progress(app);
} else {
@@ -289,7 +422,7 @@ static bool
}
if(event.event == ProtoPirateCustomEventPsaBruteforceComplete) {
if(g_bf_state) {
if(g_bf_state || g_hitag2_state) {
bf_finish_and_show_result(app, NULL);
if(g_active_ctx == ProtoPiratePsaBfContextSubDecode) {
g_host_api->subdecode_signal_info_refresh(app);
@@ -303,14 +436,13 @@ static bool
return start_bruteforce(app);
}
if(event.event == ProtoPirateCustomEventReceiverInfoBruteforceCancel) {
if(g_bf_state && g_bf_state->status == PSA_BF_STATUS_FOUND) {
if(bf_status() == PSA_BF_STATUS_FOUND) {
g_host_api->receiver_info_rebuild_widget(app);
free(g_bf_state);
g_bf_state = NULL;
} else if(g_bf_state && g_bf_state->status == PSA_BF_STATUS_RUNNING) {
g_bf_state->cancel = 1;
bf_free_states();
} else if(bf_status() == PSA_BF_STATUS_RUNNING) {
bf_set_cancel();
} else {
if(g_bf_state) {
if(g_bf_state || g_hitag2_state) {
bf_finish_and_show_result(app, NULL);
}
g_host_api->scene_previous(app);
+1 -1
View File
@@ -9,7 +9,7 @@
#include "../../protocols/psa_bf_types.h"
#define PROTOPIRATE_PSA_BF_PLUGIN_APP_ID "protopirate_psa_bf_plugin"
#define PROTOPIRATE_PSA_BF_PLUGIN_API_VERSION 1U
#define PROTOPIRATE_PSA_BF_PLUGIN_API_VERSION 2U
typedef struct ProtoPirateApp ProtoPirateApp;
typedef struct ProtoPirateHistory ProtoPirateHistory;
+11 -8
View File
@@ -52,6 +52,7 @@ static void protopirate_receiver_info_build_normal_widget(ProtoPirateApp* app) {
app->txrx->history, app->txrx->idx_menu_chosen, text, app->txrx->environment);
bool is_psa = false;
bool offers_bf = false;
FlipperFormat* ff =
protopirate_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen);
if(ff) {
@@ -59,8 +60,9 @@ static void protopirate_receiver_info_build_normal_widget(ProtoPirateApp* app) {
flipper_format_rewind(ff);
if(flipper_format_read_string(ff, FF_PROTOCOL, protocol)) {
const char* protocol_name = furi_string_get_cstr(protocol);
if(strcmp(protopirate_protocol_catalog_canonical_name(protocol_name), "PSA") == 0)
is_psa = true;
const char* canonical = protopirate_protocol_catalog_canonical_name(protocol_name);
if(strcmp(canonical, "PSA") == 0) is_psa = true;
offers_bf = protopirate_protocol_catalog_offers_bruteforce(protocol_name);
app->emulate_disabled_for_loaded = !protopirate_protocol_catalog_can_tx(protocol_name);
}
furi_string_free(protocol);
@@ -126,16 +128,16 @@ static void protopirate_receiver_info_build_normal_widget(ProtoPirateApp* app) {
app->widget, 0, 11, AlignLeft, AlignTop, FontSecondary, text_str);
}
bool psa_needs_bf = false;
if(is_psa && protopirate_psa_bf_plugin_ensure_loaded(app) && app->psa_bf_plugin) {
psa_needs_bf = app->psa_bf_plugin->widget_left_should_bruteforce(
bool needs_bf = false;
if(offers_bf && protopirate_psa_bf_plugin_ensure_loaded(app) && app->psa_bf_plugin) {
needs_bf = app->psa_bf_plugin->widget_left_should_bruteforce(
app, ProtoPiratePsaBfContextReceiverInfo);
}
if(psa_needs_bf) {
if(needs_bf) {
widget_add_button_element(
app->widget,
GuiButtonTypeLeft,
"Brute force",
"BF",
protopirate_scene_receiver_info_widget_callback,
app);
} else
@@ -180,7 +182,8 @@ static void protopirate_scene_receiver_info_widget_callback(
has_match ? ProtoPirateCustomEventReceiverInfoUpdate :
ProtoPirateCustomEventReceiverInfoSave);
} else if(result == GuiButtonTypeLeft) {
if(protopirate_receiver_info_selected_protocol_is(app, "PSA") &&
if((protopirate_receiver_info_selected_protocol_is(app, "PSA") ||
protopirate_receiver_info_selected_protocol_is(app, "Renault V1")) &&
protopirate_psa_bf_plugin_ensure_loaded(app) && app->psa_bf_plugin &&
app->psa_bf_plugin->widget_left_should_bruteforce(
app, ProtoPiratePsaBfContextReceiverInfo)) {
+4 -3
View File
@@ -1212,7 +1212,8 @@ bool protopirate_scene_sub_decode_on_event(void* context, SceneManagerEvent even
FuriString* proto_str = furi_string_alloc();
flipper_format_rewind(ff);
bool have_proto = flipper_format_read_string(ff, FF_PROTOCOL, proto_str);
bool is_psa = have_proto && furi_string_cmp_str(proto_str, "PSA") == 0;
bool offers_bf = have_proto && protopirate_protocol_catalog_offers_bruteforce(
furi_string_get_cstr(proto_str));
if(have_proto) {
const char* protocol_name = furi_string_get_cstr(proto_str);
@@ -1222,7 +1223,7 @@ bool protopirate_scene_sub_decode_on_event(void* context, SceneManagerEvent even
app->emulate_disabled_for_loaded = true;
}
furi_string_free(proto_str);
if(is_psa) {
if(offers_bf) {
app->txrx->idx_menu_chosen = ctx->selected_history_index;
bool needs_bf = false;
if(protopirate_psa_bf_plugin_ensure_loaded(app) && app->psa_bf_plugin) {
@@ -1233,7 +1234,7 @@ bool protopirate_scene_sub_decode_on_event(void* context, SceneManagerEvent even
widget_add_button_element(
app->widget,
GuiButtonTypeLeft,
"Brute force",
"BF",
protopirate_scene_sub_decode_widget_callback,
app);
#ifdef ENABLE_EMULATE_FEATURE