mirror of
https://protopirate.net/ProtoPirate/ProtoPirate.git
synced 2026-09-25 22:05:51 +00:00
Merge branch 'zero-mega' into pp_main
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user