//! Common utilities for protocol implementations. //! //! Aligned with ProtoPirate reference: `REFERENCES/ProtoPirate/protocols/protocols_common.c` //! and `protocols_common.h`. The reference provides only Flipper preset name mapping //! (`protopirate_get_short_preset_name`); we implement the same mapping in `short_preset_name`. //! This module also holds shared types and helpers used by multiple protocol decoders. //! //! **CRC / add_bit**: `crc8_kia` matches `kia_crc8` in kia_v0.c (polynomial 0x7F, init 0x00); //! `add_bit` matches the common shift-left-and-append pattern used in the reference decoders. //! //! **Manchester**: Ford V0 and Fiat V0 each have their own state machine in their protocol //! modules. This module provides CommonManchesterState / common_manchester_advance for //! protocols that use the Flipper-style event mapping (0=ShortLow, 1=ShortHigh, 2=LongLow, //! 3=LongHigh) and want the shared implementation. /// Decoded signal information #[derive(Debug, Clone)] pub struct DecodedSignal { /// Serial number / device ID pub serial: Option, /// Button code pub button: Option, /// Rolling counter pub counter: Option, /// CRC is valid pub crc_valid: bool, /// Raw data (up to 64 bits) pub data: u64, /// Number of bits in data pub data_count_bit: usize, /// Whether encoding is supported pub encoder_capable: bool, } impl DecodedSignal { #[allow(dead_code)] pub fn new(data: u64, bit_count: usize) -> Self { Self { serial: None, button: None, counter: None, crc_valid: false, data, data_count_bit: bit_count, encoder_capable: false, } } } /// CRC8 calculation with custom polynomial (MSB-first, shift-left style). /// /// # Arguments /// * `data` - Data bytes to calculate CRC over /// * `poly` - CRC polynomial (e.g. 0x7F for Kia) /// * `init` - Initial CRC value (e.g. 0x00) pub fn crc8(data: &[u8], poly: u8, init: u8) -> u8 { let mut crc = init; for &byte in data { crc ^= byte; for _ in 0..8 { if (crc & 0x80) != 0 { crc = (crc << 1) ^ poly; } else { crc <<= 1; } } } crc } /// CRC8 for Kia protocol (matches kia_v0.c kia_crc8: polynomial 0x7F, init 0x00) pub fn crc8_kia(data: &[u8]) -> u8 { crc8(data, 0x7F, 0x00) } /// Add a bit to the decoder's data accumulator (shift-left, LSB last; matches reference add_bit pattern) #[inline] pub fn add_bit(data: &mut u64, count: &mut usize, bit: bool) { *data = (*data << 1) | (bit as u64); *count += 1; } // ============================================================================= // Preset name mapping (protocols_common.c: protopirate_get_short_preset_name) // ============================================================================= /// Short preset name for display. Matches `protopirate_get_short_preset_name`. /// Returns "UNKNOWN" for null/empty or unknown preset names (reference returns the /// original pointer for unknown; we use "UNKNOWN" to avoid allocation). #[allow(dead_code)] #[inline] pub fn short_preset_name(preset: Option<&str>) -> &'static str { let p = match preset { None => return "UNKNOWN", Some(s) if s.is_empty() => return "UNKNOWN", Some(s) => s, }; match p { "FuriHalSubGhzPresetOok270Async" => "AM270", "FuriHalSubGhzPresetOok650Async" => "AM650", "FuriHalSubGhzPreset2FSKDev238Async" => "FM238", "FuriHalSubGhzPreset2FSKDev12KAsync" => "FM12K", "FuriHalSubGhzPreset2FSKDev476Async" => "FM476", "FuriHalSubGhzPresetCustom" => "CUSTOM", _ => "UNKNOWN", } } // ============================================================================= // Common Manchester state machine (Flipper lib/toolbox/manchester_decoder) // ============================================================================= // ProtoPirate Ford and Fiat use Flipper's lib/toolbox/manchester_decoder.h: // ManchesterState (Mid0, Mid1, Start0, Start1), ManchesterEvent (ShortLow, // ShortHigh, LongLow, LongHigh, Reset), manchester_advance(state, event, &state, &bit). // This module provides a separate implementation of the same transition table for // protocols that want the shared behaviour. Ford and Fiat keep their own // FordV0ManchesterState and FiatV0ManchesterState in their modules (same table, no reuse). // // Event encoding: 0=ShortLow, 1=ShortHigh, 2=LongLow, 3=LongHigh. // Level mapping in ProtoPirate: level ? ShortLow : ShortHigh (and same for long). /// Manchester decoder states for the common (Flipper-style) differential Manchester decoder. #[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq)] pub enum CommonManchesterState { Mid0 = 0, Mid1 = 1, Start0 = 2, Start1 = 3, } /// Advance the common Manchester state machine by one event. /// Returns `(new_state, Some(bit))` when a data bit is emitted, otherwise `(new_state, None)`. /// Event: 0=ShortLow, 1=ShortHigh, 2=LongLow, 3=LongHigh (level ? ShortLow : ShortHigh for short/long). #[allow(dead_code)] #[inline] pub fn common_manchester_advance( state: CommonManchesterState, event: u8, ) -> (CommonManchesterState, Option) { use CommonManchesterState::{Mid0, Mid1, Start0, Start1}; let (new_state, emit) = match (state, event) { (Mid0, 0) => (Mid0, false), (Mid0, 1) => (Start1, true), (Mid0, 2) => (Mid0, false), (Mid0, 3) => (Mid1, true), (Mid1, 0) => (Start0, true), (Mid1, 1) => (Mid1, false), (Mid1, 2) => (Mid0, true), (Mid1, 3) => (Mid1, false), (Start0, 0) => (Mid0, false), (Start0, 1) => (Mid0, false), (Start0, 2) => (Mid0, false), (Start0, 3) => (Mid1, false), (Start1, 0) => (Mid0, false), (Start1, 1) => (Mid1, false), (Start1, 2) => (Mid0, false), (Start1, 3) => (Mid1, false), _ => (Mid1, false), }; let bit = if emit { Some((event & 1) == 1) } else { None }; (new_state, bit) } /// Button names for common keyfob buttons #[allow(dead_code)] pub fn get_button_name(btn: u8) -> &'static str { match btn { 0x01 => "Lock", 0x02 => "Unlock", 0x03 => "Lock+Unlock", 0x04 => "Trunk", 0x08 => "Panic", _ => "Unknown", } } /// Button code constants #[allow(dead_code)] pub mod buttons { pub const LOCK: u8 = 0x01; pub const UNLOCK: u8 = 0x02; pub const TRUNK: u8 = 0x04; pub const PANIC: u8 = 0x08; }