Fix Mazda V0 decoder and add encoder

This commit is contained in:
0mega
2026-04-14 16:10:50 +02:00
parent fc1ba45851
commit 6758a4e6e3
4 changed files with 681 additions and 279 deletions
+645 -268
View File
@@ -1,350 +1,727 @@
#include "mazda_v0.h"
#include <string.h>
// Original implementation by @lupettohf
#define MAZDA_PREAMBLE_MIN 13
#define MAZDA_COMPLETION_MIN 80
#define MAZDA_COMPLETION_MAX 105
#define MAZDA_DATA_BUFFER_SIZE 14
// =============================================================================
// PROTOCOL CONSTANTS
// =============================================================================
static const SubGhzBlockConst subghz_protocol_mazda_const = {
static const SubGhzBlockConst subghz_protocol_mazda_v0_const = {
.te_short = 250,
.te_long = 500,
.te_delta = 100,
.min_count_bit_for_found = 64,
};
typedef enum {
MazdaDecoderStepReset = 0,
MazdaDecoderStepPreambleSave,
MazdaDecoderStepPreambleCheck,
MazdaDecoderStepDataSave,
MazdaDecoderStepDataCheck,
} MazdaDecoderStep;
#define MAZDA_V0_UPLOAD_CAPACITY 0x184
#define MAZDA_V0_GAP_US 0xCB20
#define MAZDA_V0_SYNC_BYTE 0xD7
#define MAZDA_V0_TAIL_BYTE 0x5A
#define MAZDA_V0_PREAMBLE_ONES 16
struct SubGhzProtocolDecoderMazda {
// =============================================================================
// STRUCT DEFINITIONS
// =============================================================================
typedef struct SubGhzProtocolDecoderMazdaV0 {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
ManchesterState manchester_state;
uint16_t preamble_count;
uint16_t bit_counter;
uint8_t prev_state;
uint8_t data_buffer[MAZDA_DATA_BUFFER_SIZE];
uint8_t preamble_pattern;
uint32_t serial;
uint8_t button;
uint32_t count;
} SubGhzProtocolDecoderMazdaV0;
#ifdef ENABLE_EMULATE_FEATURE
typedef struct SubGhzProtocolEncoderMazdaV0 {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
uint32_t serial;
uint8_t button;
uint32_t count;
} SubGhzProtocolEncoderMazdaV0;
#endif
typedef enum {
MazdaV0DecoderStepReset = 0,
MazdaV0DecoderStepPreamble = 5,
MazdaV0DecoderStepData = 6,
} MazdaV0DecoderStep;
// =============================================================================
// FUNCTION PROTOTYPES
// =============================================================================
static bool mazda_v0_get_event(uint32_t duration, bool level, ManchesterEvent* event);
static void mazda_v0_decode_key(SubGhzBlockGeneric* generic);
#ifdef ENABLE_EMULATE_FEATURE
static uint64_t mazda_v0_encode_key(uint32_t serial, uint8_t button, uint32_t counter);
static bool mazda_v0_encoder_add_level(
SubGhzProtocolEncoderMazdaV0* instance,
size_t* index,
bool level,
uint32_t duration);
static bool mazda_v0_append_byte(
SubGhzProtocolEncoderMazdaV0* instance,
size_t* index,
uint8_t value);
static bool mazda_v0_build_upload(SubGhzProtocolEncoderMazdaV0* instance);
#endif
static SubGhzProtocolStatus
mazda_v0_write_display(FlipperFormat* flipper_format, const char* protocol_name, uint8_t button);
// =============================================================================
// PROTOCOL INTERFACE DEFINITIONS
// =============================================================================
const SubGhzProtocolDecoder subghz_protocol_mazda_v0_decoder = {
.alloc = subghz_protocol_decoder_mazda_v0_alloc,
.free = subghz_protocol_decoder_mazda_v0_free,
.feed = subghz_protocol_decoder_mazda_v0_feed,
.reset = subghz_protocol_decoder_mazda_v0_reset,
.get_hash_data = subghz_protocol_decoder_mazda_v0_get_hash_data,
.serialize = subghz_protocol_decoder_mazda_v0_serialize,
.deserialize = subghz_protocol_decoder_mazda_v0_deserialize,
.get_string = subghz_protocol_decoder_mazda_v0_get_string,
};
// ============================================================================
// Helpers
// ============================================================================
static uint8_t mazda_byte_parity(uint8_t value) {
value ^= value >> 4;
value ^= value >> 2;
value ^= value >> 1;
return value & 1;
}
static void mazda_xor_deobfuscate(uint8_t* data) {
uint8_t parity = mazda_byte_parity(data[7]);
if(parity) {
uint8_t mask = data[6];
for(uint8_t i = 0; i < 6; i++) {
data[i] ^= mask;
}
} else {
uint8_t mask = data[5];
for(uint8_t i = 0; i < 5; i++) {
data[i] ^= mask;
}
data[6] ^= mask;
}
uint8_t old5 = data[5];
uint8_t old6 = data[6];
data[5] = (old5 & 0xAAU) | (old6 & 0x55U);
data[6] = (old5 & 0x55U) | (old6 & 0xAAU);
}
static void mazda_parse_data(SubGhzBlockGeneric* generic) {
generic->serial = (uint32_t)(generic->data >> 32);
generic->btn = (generic->data >> 24) & 0xFF;
generic->cnt = (generic->data >> 8) & 0xFFFF;
}
static const char* mazda_get_btn_name(uint8_t btn) {
switch(btn) {
case 0x10:
return "Lock";
case 0x20:
return "Unlock";
case 0x40:
return "Trunk";
default:
return "Unknown";
}
}
static inline bool mazda_is_short(uint32_t duration) {
return DURATION_DIFF(duration, subghz_protocol_mazda_const.te_short) <
subghz_protocol_mazda_const.te_delta;
}
static inline bool mazda_is_long(uint32_t duration) {
return DURATION_DIFF(duration, subghz_protocol_mazda_const.te_long) <
subghz_protocol_mazda_const.te_delta;
}
static void mazda_collect_bit(SubGhzProtocolDecoderMazda* instance, uint8_t state_bit) {
uint8_t byte_idx = instance->bit_counter >> 3;
if(byte_idx < MAZDA_DATA_BUFFER_SIZE) {
instance->data_buffer[byte_idx] <<= 1;
if(state_bit == 0) {
instance->data_buffer[byte_idx] |= 1;
}
}
instance->bit_counter++;
}
static bool mazda_check_completion(SubGhzProtocolDecoderMazda* instance) {
if(instance->bit_counter < MAZDA_COMPLETION_MIN || instance->bit_counter > MAZDA_COMPLETION_MAX) {
return false;
}
// Shift buffer by 1 byte (discard sync/header byte)
uint8_t data[8];
for(uint8_t i = 0; i < 8; i++) {
data[i] = instance->data_buffer[i + 1];
}
mazda_xor_deobfuscate(data);
uint8_t checksum = 0;
for(uint8_t i = 0; i < 7; i++) {
checksum += data[i];
}
if(checksum != data[7]) {
return false;
}
uint64_t packed = 0;
for(uint8_t i = 0; i < 8; i++) {
packed = (packed << 8) | data[i];
}
instance->generic.data = packed;
instance->generic.data_count_bit = 64;
mazda_parse_data(&instance->generic);
return true;
}
static bool
mazda_process_pair(SubGhzProtocolDecoderMazda* instance, uint32_t dur_first, uint32_t dur_second) {
bool first_short = mazda_is_short(dur_first);
bool first_long = mazda_is_long(dur_first);
bool second_short = mazda_is_short(dur_second);
bool second_long = mazda_is_long(dur_second);
if(first_long && second_short) {
mazda_collect_bit(instance, 0);
mazda_collect_bit(instance, 1);
instance->prev_state = 1;
return true;
}
if(first_short && second_long) {
mazda_collect_bit(instance, 1);
instance->prev_state = 0;
return true;
}
if(first_short && second_short) {
mazda_collect_bit(instance, instance->prev_state);
return true;
}
if(first_long && second_long) {
mazda_collect_bit(instance, 0);
mazda_collect_bit(instance, 1);
instance->prev_state = 0;
return true;
}
return false;
}
const SubGhzProtocolDecoder subghz_protocol_mazda_decoder = {
.alloc = subghz_protocol_decoder_mazda_alloc,
.free = subghz_protocol_decoder_mazda_free,
.feed = subghz_protocol_decoder_mazda_feed,
.reset = subghz_protocol_decoder_mazda_reset,
.get_hash_data = subghz_protocol_decoder_mazda_get_hash_data,
.serialize = subghz_protocol_decoder_mazda_serialize,
.deserialize = subghz_protocol_decoder_mazda_deserialize,
.get_string = subghz_protocol_decoder_mazda_get_string,
#ifdef ENABLE_EMULATE_FEATURE
const SubGhzProtocolEncoder subghz_protocol_mazda_v0_encoder = {
.alloc = subghz_protocol_encoder_mazda_v0_alloc,
.free = subghz_protocol_encoder_mazda_v0_free,
.deserialize = subghz_protocol_encoder_mazda_v0_deserialize,
.stop = subghz_protocol_encoder_mazda_v0_stop,
.yield = subghz_protocol_encoder_mazda_v0_yield,
};
const SubGhzProtocolEncoder subghz_protocol_mazda_encoder = {
#else
const SubGhzProtocolEncoder subghz_protocol_mazda_v0_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
#endif
const SubGhzProtocol mazda_v0_protocol = {
.name = MAZDA_PROTOCOL_NAME,
.type = SubGhzProtocolTypeStatic,
.name = MAZDA_PROTOCOL_V0_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
.decoder = &subghz_protocol_mazda_decoder,
.encoder = &subghz_protocol_mazda_encoder,
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_mazda_v0_decoder,
.encoder = &subghz_protocol_mazda_v0_encoder,
};
// ============================================================================
// Decoder
// ============================================================================
// =============================================================================
// HELPERS
// =============================================================================
void* subghz_protocol_decoder_mazda_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderMazda* instance = calloc(1, sizeof(SubGhzProtocolDecoderMazda));
static uint8_t mazda_v0_popcount8(uint8_t x) {
uint8_t count = 0;
while(x) {
count += x & 1;
x >>= 1;
}
return count;
}
static void mazda_v0_u64_to_bytes_be(uint64_t data, uint8_t bytes[8]) {
for(size_t i = 0; i < 8; i++) {
bytes[i] = (uint8_t)((data >> ((7 - i) * 8)) & 0xFF);
}
}
static uint64_t mazda_v0_bytes_to_u64_be(const uint8_t bytes[8]) {
uint64_t data = 0;
for(size_t i = 0; i < 8; i++) {
data = (data << 8) | bytes[i];
}
return data;
}
static uint8_t mazda_v0_calculate_checksum(uint32_t serial, uint8_t button, uint32_t counter) {
counter &= 0xFFFFFU;
return (uint8_t)(
((serial >> 24) & 0xFF) + ((serial >> 16) & 0xFF) + ((serial >> 8) & 0xFF) +
(serial & 0xFF) + ((counter >> 8) & 0xFF) + (counter & 0xFF) +
((((counter >> 16) & 0x0F) | ((button & 0x0F) << 4)) & 0xFF));
}
static const char* mazda_v0_get_button_name(uint8_t button) {
switch(button) {
case 0x01:
return "Lock";
case 0x02:
return "Unlock";
case 0x04:
return "Trunk";
case 0x08:
return "Remote";
default:
return "??";
}
}
static bool mazda_v0_get_event(uint32_t duration, bool level, ManchesterEvent* event) {
const uint32_t tol = (uint32_t)subghz_protocol_mazda_v0_const.te_delta + 20U;
if((uint32_t)DURATION_DIFF(duration, subghz_protocol_mazda_v0_const.te_short) < tol) {
*event = level ? ManchesterEventShortLow : ManchesterEventShortHigh;
return true;
}
if((uint32_t)DURATION_DIFF(duration, subghz_protocol_mazda_v0_const.te_long) < tol) {
*event = level ? ManchesterEventLongLow : ManchesterEventLongHigh;
return true;
}
return false;
}
static void mazda_v0_decode_key(SubGhzBlockGeneric* generic) {
uint8_t data[8];
mazda_v0_u64_to_bytes_be(generic->data, data);
const bool parity = (mazda_v0_popcount8(data[7]) & 1) != 0;
const uint8_t limit = parity ? 6 : 5;
const uint8_t mask = data[limit];
for(uint8_t i = 0; i < limit; i++) {
data[i] ^= mask;
}
if(!parity) {
data[6] ^= mask;
}
const uint8_t counter_lo = (data[5] & 0x55) | (data[6] & 0xAA);
const uint8_t counter_mid = (data[6] & 0x55) | (data[5] & 0xAA);
generic->serial = ((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) |
((uint32_t)data[2] << 8) | (uint32_t)data[3];
generic->btn = (data[4] >> 4) & 0x0F;
generic->cnt = (((uint32_t)data[4] & 0x0F) << 16) | ((uint32_t)counter_mid << 8) |
(uint32_t)counter_lo;
generic->data_count_bit = subghz_protocol_mazda_v0_const.min_count_bit_for_found;
}
#ifdef ENABLE_EMULATE_FEATURE
static uint64_t mazda_v0_encode_key(uint32_t serial, uint8_t button, uint32_t counter) {
uint8_t data[8];
counter &= 0xFFFFFU;
button &= 0x0F;
data[0] = (serial >> 24) & 0xFF;
data[1] = (serial >> 16) & 0xFF;
data[2] = (serial >> 8) & 0xFF;
data[3] = serial & 0xFF;
data[4] = (button << 4) | ((counter >> 16) & 0x0F);
data[5] = (counter >> 8) & 0xFF;
data[6] = counter & 0xFF;
data[7] = mazda_v0_calculate_checksum(serial, button, counter);
const uint8_t stored_5 = (data[6] & 0x55) | (data[5] & 0xAA);
const uint8_t stored_6 = (data[6] & 0xAA) | (data[5] & 0x55);
const uint8_t xor_mask = stored_5 ^ stored_6;
const bool replace_second = ((~mazda_v0_popcount8(data[7])) & 1) != 0;
const uint8_t forward_mask = replace_second ? stored_5 : stored_6;
data[5] = replace_second ? stored_5 : xor_mask;
data[6] = replace_second ? xor_mask : stored_6;
for(size_t i = 0; i < 5; i++) {
data[i] ^= forward_mask;
}
return mazda_v0_bytes_to_u64_be(data);
}
static bool mazda_v0_encoder_add_level(
SubGhzProtocolEncoderMazdaV0* instance,
size_t* index,
bool level,
uint32_t duration) {
if(*index >= MAZDA_V0_UPLOAD_CAPACITY) {
return false;
}
instance->encoder.upload[(*index)++] = level_duration_make(level, duration);
return true;
}
static bool mazda_v0_append_byte(
SubGhzProtocolEncoderMazdaV0* instance,
size_t* index,
uint8_t value) {
if(*index + 16 > MAZDA_V0_UPLOAD_CAPACITY) {
return false;
}
const uint32_t te = subghz_protocol_mazda_v0_const.te_short;
for(int8_t bit = 7; bit >= 0; bit--) {
const bool bit_value = ((value >> bit) & 1) != 0;
if(!bit_value) {
if(!mazda_v0_encoder_add_level(instance, index, false, te)) {
return false;
}
if(!mazda_v0_encoder_add_level(instance, index, true, te)) {
return false;
}
} else {
if(!mazda_v0_encoder_add_level(instance, index, true, te)) {
return false;
}
if(!mazda_v0_encoder_add_level(instance, index, false, te)) {
return false;
}
}
}
return true;
}
static bool mazda_v0_build_upload(SubGhzProtocolEncoderMazdaV0* instance) {
furi_check(instance);
size_t index = 0;
const uint64_t key64 = instance->generic.data;
for(size_t r = 0; r < 12; r++) {
if(!mazda_v0_append_byte(instance, &index, 0xFF)) {
return false;
}
}
if(!mazda_v0_encoder_add_level(instance, &index, false, MAZDA_V0_GAP_US)) {
return false;
}
if(!mazda_v0_append_byte(instance, &index, 0xFF) ||
!mazda_v0_append_byte(instance, &index, 0xFF) ||
!mazda_v0_append_byte(instance, &index, MAZDA_V0_SYNC_BYTE)) {
return false;
}
for(int bi = 0; bi < 8; bi++) {
const uint8_t raw = (uint8_t)((key64 >> (56 - bi * 8)) & 0xFF);
const uint8_t air = (uint8_t)~raw;
if(!mazda_v0_append_byte(instance, &index, air)) {
return false;
}
}
if(!mazda_v0_append_byte(instance, &index, MAZDA_V0_TAIL_BYTE)) {
return false;
}
if(!mazda_v0_encoder_add_level(instance, &index, false, MAZDA_V0_GAP_US)) {
return false;
}
instance->encoder.front = 0;
instance->encoder.size_upload = index;
return true;
}
#endif
static SubGhzProtocolStatus
mazda_v0_write_display(FlipperFormat* flipper_format, const char* protocol_name, uint8_t button) {
SubGhzProtocolStatus status = SubGhzProtocolStatusOk;
FuriString* display = furi_string_alloc();
furi_string_printf(
display, "%s - %s", protocol_name, mazda_v0_get_button_name(button));
if(!flipper_format_write_string_cstr(flipper_format, "Disp", furi_string_get_cstr(display))) {
status = SubGhzProtocolStatusErrorParserOthers;
}
furi_string_free(display);
return status;
}
// =============================================================================
// ENCODER
// =============================================================================
#ifdef ENABLE_EMULATE_FEATURE
void* subghz_protocol_encoder_mazda_v0_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderMazdaV0* instance = calloc(1, sizeof(SubGhzProtocolEncoderMazdaV0));
furi_check(instance);
instance->base.protocol = &mazda_v0_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 0;
instance->encoder.front = 0;
instance->encoder.is_running = false;
instance->encoder.upload = malloc(MAZDA_V0_UPLOAD_CAPACITY * sizeof(LevelDuration));
furi_check(instance->encoder.upload);
return instance;
}
void subghz_protocol_decoder_mazda_free(void* context) {
void subghz_protocol_encoder_mazda_v0_free(void* context) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
SubGhzProtocolEncoderMazdaV0* instance = context;
free(instance->encoder.upload);
free(instance);
}
void subghz_protocol_decoder_mazda_reset(void* context) {
SubGhzProtocolStatus
subghz_protocol_encoder_mazda_v0_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
instance->decoder.parser_step = MazdaDecoderStepReset;
instance->preamble_count = 0;
instance->bit_counter = 0;
instance->prev_state = 0;
instance->generic.data = 0;
instance->generic.data_count_bit = 0;
memset(instance->data_buffer, 0, sizeof(instance->data_buffer));
SubGhzProtocolEncoderMazdaV0* instance = context;
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
instance->encoder.is_running = false;
instance->encoder.front = 0;
instance->encoder.repeat = 10;
do {
FuriString* temp_str = furi_string_alloc();
if(!temp_str) {
break;
}
flipper_format_rewind(flipper_format);
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
furi_string_free(temp_str);
break;
}
if(!furi_string_equal(temp_str, instance->base.protocol->name)) {
furi_string_free(temp_str);
break;
}
furi_string_free(temp_str);
flipper_format_rewind(flipper_format);
SubGhzProtocolStatus load_st = subghz_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
subghz_protocol_mazda_v0_const.min_count_bit_for_found);
if(load_st != SubGhzProtocolStatusOk) {
break;
}
mazda_v0_decode_key(&instance->generic);
uint32_t u32 = 0;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Serial", &u32, 1)) {
instance->generic.serial = u32;
}
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Btn", &u32, 1)) {
instance->generic.btn = (uint8_t)u32;
}
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Cnt", &u32, 1)) {
instance->generic.cnt = u32;
}
instance->serial = instance->generic.serial;
instance->button = instance->generic.btn;
instance->count = instance->generic.cnt;
flipper_format_rewind(flipper_format);
if(!flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1)) {
instance->encoder.repeat = 10;
}
instance->generic.btn &= 0x0FU;
instance->generic.cnt &= 0xFFFFFU;
instance->generic.data = mazda_v0_encode_key(
instance->generic.serial, instance->generic.btn, instance->generic.cnt);
instance->generic.data_count_bit = subghz_protocol_mazda_v0_const.min_count_bit_for_found;
instance->serial = instance->generic.serial;
instance->button = instance->generic.btn;
instance->count = instance->generic.cnt;
if(!mazda_v0_build_upload(instance)) {
break;
}
if(instance->encoder.size_upload == 0) {
break;
}
flipper_format_rewind(flipper_format);
uint8_t key_data[sizeof(uint64_t)];
mazda_v0_u64_to_bytes_be(instance->generic.data, key_data);
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(key_data))) {
break;
}
uint32_t chk = mazda_v0_calculate_checksum(
instance->serial, instance->button, instance->count);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Checksum", &chk, 1);
instance->encoder.is_running = true;
ret = SubGhzProtocolStatusOk;
} while(false);
return ret;
}
void subghz_protocol_decoder_mazda_feed(void* context, bool level, uint32_t duration) {
void subghz_protocol_encoder_mazda_v0_stop(void* context) {
furi_check(context);
UNUSED(level);
SubGhzProtocolDecoderMazda* instance = context;
SubGhzProtocolEncoderMazdaV0* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_mazda_v0_yield(void* context) {
furi_check(context);
SubGhzProtocolEncoderMazdaV0* instance = context;
if(!instance->encoder.is_running || instance->encoder.repeat == 0) {
instance->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration out = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0;
}
return out;
}
#endif
// =============================================================================
// DECODER
// =============================================================================
void* subghz_protocol_decoder_mazda_v0_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderMazdaV0* instance = calloc(1, sizeof(SubGhzProtocolDecoderMazdaV0));
furi_check(instance);
instance->base.protocol = &mazda_v0_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_mazda_v0_free(void* context) {
furi_check(context);
SubGhzProtocolDecoderMazdaV0* instance = context;
free(instance);
}
void subghz_protocol_decoder_mazda_v0_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderMazdaV0* instance = context;
instance->decoder.parser_step = MazdaV0DecoderStepReset;
instance->decoder.te_last = 0;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->manchester_state = ManchesterStateStart1;
instance->preamble_count = 0;
instance->preamble_pattern = 0;
}
void subghz_protocol_decoder_mazda_v0_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderMazdaV0* instance = context;
ManchesterEvent event = ManchesterEventReset;
bool data = false;
switch(instance->decoder.parser_step) {
case MazdaDecoderStepReset:
if(mazda_is_short(duration)) {
instance->decoder.te_last = duration;
case MazdaV0DecoderStepReset:
if(level &&
((uint32_t)DURATION_DIFF(duration, subghz_protocol_mazda_v0_const.te_short) <
(uint32_t)subghz_protocol_mazda_v0_const.te_delta + 20U)) {
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.parser_step = MazdaV0DecoderStepPreamble;
instance->manchester_state = ManchesterStateMid1;
instance->preamble_count = 0;
instance->decoder.parser_step = MazdaDecoderStepPreambleCheck;
instance->preamble_pattern = 0;
}
break;
case MazdaDecoderStepPreambleSave:
instance->decoder.te_last = duration;
instance->decoder.parser_step = MazdaDecoderStepPreambleCheck;
break;
case MazdaDecoderStepPreambleCheck:
if(mazda_is_short(instance->decoder.te_last) && mazda_is_short(duration)) {
instance->preamble_count++;
instance->decoder.parser_step = MazdaDecoderStepPreambleSave;
} else if(
mazda_is_short(instance->decoder.te_last) && mazda_is_long(duration) &&
instance->preamble_count >= MAZDA_PREAMBLE_MIN) {
instance->bit_counter = 1;
memset(instance->data_buffer, 0, sizeof(instance->data_buffer));
mazda_collect_bit(instance, 1);
instance->prev_state = 0;
instance->decoder.parser_step = MazdaDecoderStepDataSave;
} else {
instance->decoder.parser_step = MazdaDecoderStepReset;
case MazdaV0DecoderStepPreamble:
if(!mazda_v0_get_event(duration, level, &event)) {
instance->decoder.parser_step = MazdaV0DecoderStepReset;
break;
}
break;
case MazdaDecoderStepDataSave:
instance->decoder.te_last = duration;
instance->decoder.parser_step = MazdaDecoderStepDataCheck;
break;
if(manchester_advance(
instance->manchester_state, event, &instance->manchester_state, &data)) {
instance->preamble_pattern = (instance->preamble_pattern << 1) | (data ? 1 : 0);
case MazdaDecoderStepDataCheck:
if(mazda_process_pair(instance, instance->decoder.te_last, duration)) {
instance->decoder.parser_step = MazdaDecoderStepDataSave;
} else {
if(mazda_check_completion(instance) && instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
if(data) {
instance->preamble_count++;
} else if(instance->preamble_count <= MAZDA_V0_PREAMBLE_ONES - 1U) {
instance->preamble_count = 0;
instance->preamble_pattern = 0;
break;
}
if((instance->preamble_pattern == MAZDA_V0_SYNC_BYTE) &&
(instance->preamble_count > MAZDA_V0_PREAMBLE_ONES - 1U)) {
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.parser_step = MazdaV0DecoderStepData;
}
}
break;
case MazdaV0DecoderStepData:
if(!mazda_v0_get_event(duration, level, &event)) {
instance->decoder.parser_step = MazdaV0DecoderStepReset;
break;
}
if(manchester_advance(
instance->manchester_state, event, &instance->manchester_state, &data)) {
subghz_protocol_blocks_add_bit(&instance->decoder, data);
if(instance->decoder.decode_count_bit ==
subghz_protocol_mazda_v0_const.min_count_bit_for_found) {
instance->generic.data = ~instance->decoder.decode_data;
mazda_v0_decode_key(&instance->generic);
if(mazda_v0_calculate_checksum(
instance->generic.serial, instance->generic.btn, instance->generic.cnt) ==
(uint8_t)instance->generic.data) {
instance->serial = instance->generic.serial;
instance->button = instance->generic.btn;
instance->count = instance->generic.cnt;
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
}
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->preamble_count = 0;
instance->preamble_pattern = 0;
instance->manchester_state = ManchesterStateStart1;
instance->decoder.te_last = 0;
instance->decoder.parser_step = MazdaV0DecoderStepReset;
}
instance->decoder.parser_step = MazdaDecoderStepReset;
}
break;
}
}
uint8_t subghz_protocol_decoder_mazda_get_hash_data(void* context) {
uint8_t subghz_protocol_decoder_mazda_v0_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderMazda* 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);
SubGhzProtocolDecoderMazdaV0* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_mazda_serialize(
SubGhzProtocolStatus subghz_protocol_decoder_mazda_v0_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
SubGhzProtocolDecoderMazdaV0* instance = context;
mazda_v0_decode_key(&instance->generic);
instance->serial = instance->generic.serial;
instance->button = instance->generic.btn;
instance->count = instance->generic.cnt;
SubGhzProtocolStatus ret =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
SubGhzProtocolStatus
subghz_protocol_decoder_mazda_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, subghz_protocol_mazda_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
mazda_parse_data(&instance->generic);
uint32_t chk = mazda_v0_calculate_checksum(
instance->serial, instance->button, instance->count);
flipper_format_write_uint32(flipper_format, "Checksum", &chk, 1);
flipper_format_write_uint32(flipper_format, "Serial", &instance->serial, 1);
uint32_t temp = instance->button;
flipper_format_write_uint32(flipper_format, "Btn", &temp, 1);
flipper_format_write_uint32(flipper_format, "Cnt", &instance->count, 1);
ret = mazda_v0_write_display(
flipper_format, instance->generic.protocol_name, instance->button);
}
return ret;
}
void subghz_protocol_decoder_mazda_get_string(void* context, FuriString* output) {
SubGhzProtocolStatus
subghz_protocol_decoder_mazda_v0_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
mazda_parse_data(&instance->generic);
SubGhzProtocolDecoderMazdaV0* instance = context;
uint8_t data[8];
for(uint8_t i = 0; i < 8; i++) {
data[i] = (instance->generic.data >> (56 - 8 * i)) & 0xFF;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
subghz_protocol_mazda_v0_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
flipper_format_rewind(flipper_format);
flipper_format_read_uint32(flipper_format, "Serial", &instance->serial, 1);
instance->generic.serial = instance->serial;
uint32_t btn_temp = 0;
flipper_format_read_uint32(flipper_format, "Btn", &btn_temp, 1);
instance->button = (uint8_t)btn_temp;
instance->generic.btn = instance->button;
flipper_format_read_uint32(flipper_format, "Cnt", &instance->count, 1);
instance->generic.cnt = instance->count;
}
return ret;
}
void subghz_protocol_decoder_mazda_v0_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderMazdaV0* instance = context;
mazda_v0_decode_key(&instance->generic);
const uint8_t raw_crc = instance->generic.data & 0xFF;
const uint8_t calc_crc = mazda_v0_calculate_checksum(
instance->generic.serial, instance->generic.btn, instance->generic.cnt);
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Key:%02X %02X %02X %02X %02X %02X %02X %02X\r\n"
"Sn:%08lX Btn:%s\r\n"
"Cnt:%04lX Chk:%02X\r\n",
"%s %dbit CRC:%s\r\n"
"Key: %016llX\r\n"
"Sn: %08lX Btn: %02X - %s\r\n"
"Cnt: %05lX Chk: %02X\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
data[0],
data[1],
data[2],
data[3],
data[4],
data[5],
data[6],
data[7],
(uint32_t)instance->generic.serial,
mazda_get_btn_name(instance->generic.btn),
(uint32_t)instance->generic.cnt,
data[7]);
(raw_crc == calc_crc) ? "OK" : "BAD",
(unsigned long long)instance->generic.data,
(unsigned long)instance->generic.serial,
instance->generic.btn,
mazda_v0_get_button_name(instance->generic.btn),
(unsigned long)(instance->generic.cnt & 0xFFFFFU),
raw_crc);
}
+19 -11
View File
@@ -5,27 +5,35 @@
#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 <lib/toolbox/level_duration.h>
#include <lib/toolbox/manchester_decoder.h>
#include "../defines.h"
#define MAZDA_PROTOCOL_NAME "Mazda V0"
typedef struct SubGhzProtocolDecoderMazda SubGhzProtocolDecoderMazda;
#define MAZDA_PROTOCOL_V0_NAME "Mazda V0"
extern const SubGhzProtocol mazda_v0_protocol;
void* subghz_protocol_decoder_mazda_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_mazda_free(void* context);
void subghz_protocol_decoder_mazda_reset(void* context);
void subghz_protocol_decoder_mazda_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_mazda_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_mazda_serialize(
void* subghz_protocol_decoder_mazda_v0_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_mazda_v0_free(void* context);
void subghz_protocol_decoder_mazda_v0_reset(void* context);
void subghz_protocol_decoder_mazda_v0_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_mazda_v0_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_mazda_v0_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_mazda_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_mazda_get_string(void* context, FuriString* output);
subghz_protocol_decoder_mazda_v0_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_mazda_v0_get_string(void* context, FuriString* output);
void* subghz_protocol_encoder_mazda_v0_alloc(SubGhzEnvironment* environment);
void subghz_protocol_encoder_mazda_v0_free(void* context);
SubGhzProtocolStatus
subghz_protocol_encoder_mazda_v0_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_encoder_mazda_v0_stop(void* context);
LevelDuration subghz_protocol_encoder_mazda_v0_yield(void* context);
+15
View File
@@ -165,6 +165,21 @@ static uint8_t
return original;
}
}
// Mazda V0
else if(strstr(protocol, "Mazda")) {
switch(key) {
case InputKeyUp:
return 0x01; // Lock
case InputKeyOk:
return 0x02; // Unlock
case InputKeyDown:
return 0x04; // Trunk
case InputKeyRight:
return 0x08; // Remote
default:
return original;
}
}
// Ford - (needs testing)
else if(strstr(protocol, "Ford")) {
switch(key) {
+2
View File
@@ -2,6 +2,8 @@
#include "../protopirate_app_i.h"
#include "proto_pirate_icons.h"
#define TAG "ProtoPirateNeedSaving"
static void
protopirate_scene_need_saving_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);