#include #include #include "protocol_cyfral.h" #define CYFRAL_DATA_SIZE sizeof(uint16_t) #define CYFRAL_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond()) #define CYFRAL_0_LOW (CYFRAL_PERIOD * 0.66f) #define CYFRAL_0_HI (CYFRAL_PERIOD * 0.33f) #define CYFRAL_1_LOW (CYFRAL_PERIOD * 0.33f) #define CYFRAL_1_HI (CYFRAL_PERIOD * 0.66f) #define CYFRAL_MAX_PERIOD_US 230 typedef enum { CYFRAL_BIT_WAIT_FRONT_HIGH, CYFRAL_BIT_WAIT_FRONT_LOW, } CyfralBitState; typedef enum { CYFRAL_WAIT_START_NIBBLE, CYFRAL_READ_NIBBLE, CYFRAL_READ_STOP_NIBBLE, } CyfralState; typedef struct { CyfralState state; CyfralBitState bit_state; // high + low period time uint32_t period_time; // temporary nibble storage uint8_t nibble; // data valid flag // MUST be checked only in READ_STOP_NIBBLE state bool data_valid; // nibble index, we expect 8 nibbles uint8_t index; // bit index in nibble, 4 bit per nibble uint8_t bit_index; // max period, 230us x clock per us uint32_t max_period; } ProtocolCyfralDecoder; typedef struct { uint32_t data; uint32_t index; } ProtocolCyfralEncoder; typedef struct { uint16_t data; ProtocolCyfralDecoder decoder; ProtocolCyfralEncoder encoder; } ProtocolCyfral; static void* protocol_cyfral_alloc(void) { ProtocolCyfral* proto = malloc(sizeof(ProtocolCyfral)); return (void*)proto; } static void protocol_cyfral_free(ProtocolCyfral* proto) { free(proto); } static uint8_t* protocol_cyfral_get_data(ProtocolCyfral* proto) { return (uint8_t*)&proto->data; } static void protocol_cyfral_decoder_start(ProtocolCyfral* proto) { ProtocolCyfralDecoder* cyfral = &proto->decoder; cyfral->state = CYFRAL_WAIT_START_NIBBLE; cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; cyfral->period_time = 0; cyfral->bit_index = 0; cyfral->index = 0; cyfral->nibble = 0; cyfral->data_valid = true; cyfral->max_period = CYFRAL_MAX_PERIOD_US * furi_hal_cortex_instructions_per_microsecond(); proto->data = 0; } static bool protocol_cyfral_decoder_process_bit( ProtocolCyfralDecoder* cyfral, bool polarity, uint32_t length, bool* bit_ready, bool* bit_value) { bool result = true; *bit_ready = false; // bit start from low switch(cyfral->bit_state) { case CYFRAL_BIT_WAIT_FRONT_LOW: if(polarity == true) { cyfral->period_time += length; *bit_ready = true; if(cyfral->period_time <= cyfral->max_period) { if((cyfral->period_time / 2) > length) { *bit_value = false; } else { *bit_value = true; } } else { result = false; } cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_HIGH; } else { result = false; } break; case CYFRAL_BIT_WAIT_FRONT_HIGH: if(polarity == false) { cyfral->period_time = length; cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; } else { result = false; } break; } return result; } static bool protocol_cyfral_decoder_feed(ProtocolCyfral* proto, bool level, uint32_t duration) { ProtocolCyfralDecoder* cyfral = &proto->decoder; bool bit_ready; bool bit_value; bool decoded = false; switch(cyfral->state) { case CYFRAL_WAIT_START_NIBBLE: // wait for start word if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) { if(bit_ready) { cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; if(cyfral->nibble == 0b0001) { cyfral->nibble = 0; cyfral->state = CYFRAL_READ_NIBBLE; } } } else { protocol_cyfral_decoder_start(proto); } break; case CYFRAL_READ_NIBBLE: // read nibbles if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) { if(bit_ready) { cyfral->nibble = (cyfral->nibble << 1) | bit_value; cyfral->bit_index++; //convert every nibble to 2-bit index if(cyfral->bit_index == 4) { switch(cyfral->nibble) { case 0b1110: proto->data = (proto->data << 2) | 0b11; break; case 0b1101: proto->data = (proto->data << 2) | 0b10; break; case 0b1011: proto->data = (proto->data << 2) | 0b01; break; case 0b0111: proto->data = (proto->data << 2) | 0b00; break; default: cyfral->data_valid = false; break; } cyfral->nibble = 0; cyfral->bit_index = 0; cyfral->index++; } // successfully read 8 nibbles if(cyfral->index == 8) { cyfral->state = CYFRAL_READ_STOP_NIBBLE; } } } else { protocol_cyfral_decoder_start(proto); } break; case CYFRAL_READ_STOP_NIBBLE: // read stop nibble if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) { if(bit_ready) { cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; cyfral->bit_index++; switch(cyfral->bit_index) { case 0: case 1: case 2: case 3: break; case 4: if(cyfral->nibble == 0b0001) { // validate data if(cyfral->data_valid) { decoded = true; } else { protocol_cyfral_decoder_start(proto); } } else { protocol_cyfral_decoder_start(proto); } break; default: protocol_cyfral_decoder_start(proto); break; } } } else { protocol_cyfral_decoder_start(proto); } break; } return decoded; } static uint32_t protocol_cyfral_encoder_encode(const uint16_t data) { uint32_t value = 0; for(int8_t i = 0; i <= 7; i++) { switch((data >> (i * 2)) & 0b00000011) { case 0b11: value = value << 4; value += 0b00000111; break; case 0b10: value = value << 4; value += 0b00001011; break; case 0b01: value = value << 4; value += 0b00001101; break; case 0b00: value = value << 4; value += 0b00001110; break; default: break; } } return value; } static bool protocol_cyfral_encoder_start(ProtocolCyfral* proto) { proto->encoder.index = 0; proto->encoder.data = protocol_cyfral_encoder_encode(proto->data); return true; } static LevelDuration protocol_cyfral_encoder_yield(ProtocolCyfral* proto) { LevelDuration result; if(proto->encoder.index < 8) { // start word (0b0001) switch(proto->encoder.index) { case 0: result = level_duration_make(false, CYFRAL_0_LOW); //-V1037 break; case 1: result = level_duration_make(true, CYFRAL_0_HI); //-V1037 break; case 2: result = level_duration_make(false, CYFRAL_0_LOW); break; case 3: result = level_duration_make(true, CYFRAL_0_HI); break; case 4: result = level_duration_make(false, CYFRAL_0_LOW); break; case 5: result = level_duration_make(true, CYFRAL_0_HI); break; case 6: result = level_duration_make(false, CYFRAL_1_LOW); break; case 7: result = level_duration_make(true, CYFRAL_1_HI); break; } } else { // data uint8_t data_start_index = proto->encoder.index - 8; bool clock_polarity = (data_start_index) % 2; uint8_t bit_index = (data_start_index) / 2; bool bit_value = ((proto->encoder.data >> bit_index) & 1); if(!clock_polarity) { if(bit_value) { result = level_duration_make(false, CYFRAL_1_LOW); } else { result = level_duration_make(false, CYFRAL_0_LOW); } } else { if(bit_value) { result = level_duration_make(true, CYFRAL_1_HI); } else { result = level_duration_make(true, CYFRAL_0_HI); } } } proto->encoder.index++; if(proto->encoder.index >= (9 * 4 * 2)) { proto->encoder.index = 0; } return result; } static void protocol_cyfral_render_uid(ProtocolCyfral* proto, FuriString* result) { furi_string_cat_printf(result, "ID: "); for(size_t i = 0; i < CYFRAL_DATA_SIZE; ++i) { furi_string_cat_printf(result, "%02X ", ((uint8_t*)&proto->data)[i]); } } static void protocol_cyfral_render_brief_data(ProtocolCyfral* proto, FuriString* result) { protocol_cyfral_render_uid(proto, result); } const ProtocolBase ibutton_protocol_misc_cyfral = { .name = "Cyfral", .manufacturer = "Cyfral", .data_size = CYFRAL_DATA_SIZE, .alloc = (ProtocolAlloc)protocol_cyfral_alloc, .free = (ProtocolFree)protocol_cyfral_free, .get_data = (ProtocolGetData)protocol_cyfral_get_data, .decoder = { .start = (ProtocolDecoderStart)protocol_cyfral_decoder_start, .feed = (ProtocolDecoderFeed)protocol_cyfral_decoder_feed, }, .encoder = { .start = (ProtocolEncoderStart)protocol_cyfral_encoder_start, .yield = (ProtocolEncoderYield)protocol_cyfral_encoder_yield, }, .render_uid = (ProtocolRenderData)protocol_cyfral_render_uid, .render_brief_data = (ProtocolRenderData)protocol_cyfral_render_brief_data, };