Added Keeloq
This commit is contained in:
@@ -767,6 +767,7 @@ impl App {
|
|||||||
data_count_bit: capture.data_count_bit,
|
data_count_bit: capture.data_count_bit,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: capture.data_extra,
|
extra: capture.data_extra,
|
||||||
|
protocol_display_name: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate the signal with the new button
|
// Generate the signal with the new button
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ pub struct DecodedSignal {
|
|||||||
pub encoder_capable: bool,
|
pub encoder_capable: bool,
|
||||||
/// Protocol-specific extra data for encoding (e.g. VAG: vag_type + key_idx)
|
/// Protocol-specific extra data for encoding (e.g. VAG: vag_type + key_idx)
|
||||||
pub extra: Option<u64>,
|
pub extra: Option<u64>,
|
||||||
|
/// Optional protocol display name (e.g. "KeeLoq (DoorHan)"). When set, used as the protocol name for this decode.
|
||||||
|
pub protocol_display_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DecodedSignal {
|
impl DecodedSignal {
|
||||||
@@ -46,6 +48,7 @@ impl DecodedSignal {
|
|||||||
data_count_bit: bit_count,
|
data_count_bit: bit_count,
|
||||||
encoder_capable: false,
|
encoder_capable: false,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ impl FiatV0Decoder {
|
|||||||
data_count_bit: 71,
|
data_count_bit: 71,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -617,6 +617,7 @@ impl ProtocolDecoder for FordV0Decoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.data_low = 0;
|
self.data_low = 0;
|
||||||
|
|||||||
@@ -0,0 +1,354 @@
|
|||||||
|
//! KeeLoq protocol decoder and encoder (unleashed format).
|
||||||
|
//!
|
||||||
|
//! Timing and state machine match Flipper Unleashed:
|
||||||
|
//! `REFERENCES/unleashed-firmware/lib/subghz/protocols/keeloq.c`
|
||||||
|
//! - te_short=400µs, te_long=800µs, te_delta=140µs, 64 data bits.
|
||||||
|
//! - Preamble: HIGH pulses ~400µs; when LOW and header_count>2 and LOW ~4000µs, start data.
|
||||||
|
//! - Data: short HIGH + long LOW = 1, long HIGH + short LOW = 0. End when LOW ≥ 940µs.
|
||||||
|
//! Decryption tries all keystore keys with simple, normal, secure, magic_xor_type1,
|
||||||
|
//! magic_serial type1/2/3 (and both key byte orders). Encoder uses simple learning
|
||||||
|
//! and the key stored in DecodedSignal::extra (set when decoded).
|
||||||
|
|
||||||
|
use super::common::DecodedSignal;
|
||||||
|
use super::keeloq_common::{
|
||||||
|
keeloq_decrypt, keeloq_encrypt, keeloq_magic_serial_type1_learning,
|
||||||
|
keeloq_magic_serial_type2_learning, keeloq_magic_serial_type3_learning,
|
||||||
|
keeloq_magic_xor_type1_learning, keeloq_normal_learning, keeloq_secure_learning,
|
||||||
|
reverse_key,
|
||||||
|
};
|
||||||
|
use crate::keystore;
|
||||||
|
use crate::radio::demodulator::LevelDuration;
|
||||||
|
|
||||||
|
const TE_SHORT: u32 = 400;
|
||||||
|
const TE_LONG: u32 = 800;
|
||||||
|
const TE_DELTA: u32 = 140;
|
||||||
|
const MIN_COUNT_BIT: usize = 64;
|
||||||
|
|
||||||
|
fn duration_diff(a: u32, b: u32) -> u32 {
|
||||||
|
if a < b {
|
||||||
|
b - a
|
||||||
|
} else {
|
||||||
|
a - b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum Step {
|
||||||
|
Reset,
|
||||||
|
CheckPreamble,
|
||||||
|
SaveDuration,
|
||||||
|
CheckDuration,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to decrypt (fix, hop) with all keystore keys and learning types.
|
||||||
|
/// Returns (manufacture_name, serial, counter, button, key_for_encode) on success.
|
||||||
|
/// key_for_encode is Some(mf_key) when a keystore key was used, None for AN-Motors/HCS101.
|
||||||
|
fn try_keeloq_decrypt(
|
||||||
|
fix: u32,
|
||||||
|
hop: u32,
|
||||||
|
seed: u32,
|
||||||
|
) -> Option<(String, u32, u16, u8, Option<u64>)> {
|
||||||
|
let end_serial = (fix & 0xFF) as u8;
|
||||||
|
let btn = (fix >> 28) as u8;
|
||||||
|
|
||||||
|
fn check_decrypt(decrypt: u32, btn: u8, end_serial: u8) -> bool {
|
||||||
|
(decrypt >> 28) as u8 == btn
|
||||||
|
&& (((decrypt >> 16) & 0xFF) as u8 == end_serial || ((decrypt >> 16) & 0xFF) == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
let keys = keystore::keeloq_mf_keys_with_names();
|
||||||
|
for (name, mf_key) in keys {
|
||||||
|
for key in [mf_key, mf_key.swap_bytes()] {
|
||||||
|
if key == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Simple
|
||||||
|
let decrypt = keeloq_decrypt(hop, key);
|
||||||
|
if check_decrypt(decrypt, btn, end_serial) {
|
||||||
|
let cnt = (decrypt & 0xFFFF) as u16;
|
||||||
|
let serial = fix & 0x0FFFFFFF;
|
||||||
|
return Some((name, serial, cnt, btn, Some(mf_key)));
|
||||||
|
}
|
||||||
|
// Normal
|
||||||
|
let man = keeloq_normal_learning(fix, key);
|
||||||
|
let decrypt = keeloq_decrypt(hop, man);
|
||||||
|
if check_decrypt(decrypt, btn, end_serial) {
|
||||||
|
let cnt = (decrypt & 0xFFFF) as u16;
|
||||||
|
let serial = fix & 0x0FFFFFFF;
|
||||||
|
return Some((name, serial, cnt, btn, Some(mf_key)));
|
||||||
|
}
|
||||||
|
// Secure (seed 0 and seed = fix for BFT-style)
|
||||||
|
for s in [0u32, seed] {
|
||||||
|
let man = keeloq_secure_learning(fix, s, key);
|
||||||
|
let decrypt = keeloq_decrypt(hop, man);
|
||||||
|
if check_decrypt(decrypt, btn, end_serial) {
|
||||||
|
let cnt = (decrypt & 0xFFFF) as u16;
|
||||||
|
let serial = fix & 0x0FFFFFFF;
|
||||||
|
return Some((name, serial, cnt, btn, Some(mf_key)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Magic XOR type1
|
||||||
|
let man = keeloq_magic_xor_type1_learning(fix, key);
|
||||||
|
let decrypt = keeloq_decrypt(hop, man);
|
||||||
|
if check_decrypt(decrypt, btn, end_serial) {
|
||||||
|
let cnt = (decrypt & 0xFFFF) as u16;
|
||||||
|
let serial = fix & 0x0FFFFFFF;
|
||||||
|
return Some((name, serial, cnt, btn, Some(mf_key)));
|
||||||
|
}
|
||||||
|
// Magic serial type 1/2/3
|
||||||
|
for man in [
|
||||||
|
keeloq_magic_serial_type1_learning(fix, key),
|
||||||
|
keeloq_magic_serial_type2_learning(fix, key),
|
||||||
|
keeloq_magic_serial_type3_learning(fix & 0xFFFFFF, key),
|
||||||
|
] {
|
||||||
|
let decrypt = keeloq_decrypt(hop, man);
|
||||||
|
if check_decrypt(decrypt, btn, end_serial) {
|
||||||
|
let cnt = (decrypt & 0xFFFF) as u16;
|
||||||
|
let serial = fix & 0x0FFFFFFF;
|
||||||
|
return Some((name, serial, cnt, btn, Some(mf_key)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AN-Motors / HCS101 special cases (no decrypt, no key for encode)
|
||||||
|
if (hop >> 24) == ((hop >> 16) & 0xFF)
|
||||||
|
&& (fix >> 28) == ((hop >> 12) & 0x0F)
|
||||||
|
&& (hop & 0xFFF) == 0x404
|
||||||
|
{
|
||||||
|
return Some((
|
||||||
|
"AN-Motors".to_string(),
|
||||||
|
fix & 0x0FFFFFFF,
|
||||||
|
(hop >> 16) as u16,
|
||||||
|
btn,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (hop & 0xFFF) == 0 && (fix >> 28) == ((hop >> 12) & 0x0F) {
|
||||||
|
return Some((
|
||||||
|
"HCS101".to_string(),
|
||||||
|
fix & 0x0FFFFFFF,
|
||||||
|
(hop >> 16) as u16,
|
||||||
|
btn,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KeeloqDecoder {
|
||||||
|
step: Step,
|
||||||
|
header_count: u16,
|
||||||
|
te_last: u32,
|
||||||
|
decode_data: u64,
|
||||||
|
decode_count_bit: usize,
|
||||||
|
/// For secure learning when we don't have a prior decode (use fix as seed hint)
|
||||||
|
seed: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeeloqDecoder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
step: Step::Reset,
|
||||||
|
header_count: 0,
|
||||||
|
te_last: 0,
|
||||||
|
decode_data: 0,
|
||||||
|
decode_count_bit: 0,
|
||||||
|
seed: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_bit(&mut self, bit: u8) {
|
||||||
|
self.decode_data = (self.decode_data << 1) | (bit as u64);
|
||||||
|
self.decode_count_bit += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_to(&mut self, step: Step) {
|
||||||
|
self.step = step;
|
||||||
|
if step == Step::Reset {
|
||||||
|
self.header_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for KeeloqDecoder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::ProtocolDecoder for KeeloqDecoder {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"KeeLoq"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timing(&self) -> super::ProtocolTiming {
|
||||||
|
super::ProtocolTiming {
|
||||||
|
te_short: TE_SHORT,
|
||||||
|
te_long: TE_LONG,
|
||||||
|
te_delta: TE_DELTA,
|
||||||
|
min_count_bit: MIN_COUNT_BIT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_frequencies(&self) -> &[u32] {
|
||||||
|
&[315_000_000, 433_920_000, 868_350_000]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.step = Step::Reset;
|
||||||
|
self.header_count = 0;
|
||||||
|
self.te_last = 0;
|
||||||
|
self.decode_data = 0;
|
||||||
|
self.decode_count_bit = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn feed(&mut self, level: bool, duration_us: u32) -> Option<DecodedSignal> {
|
||||||
|
match self.step {
|
||||||
|
Step::Reset => {
|
||||||
|
if level && duration_diff(duration_us, TE_SHORT) < TE_DELTA {
|
||||||
|
self.step = Step::CheckPreamble;
|
||||||
|
self.header_count = self.header_count.saturating_add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Step::CheckPreamble => {
|
||||||
|
if !level && duration_diff(duration_us, TE_SHORT) < TE_DELTA {
|
||||||
|
self.step = Step::Reset;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if self.header_count > 2
|
||||||
|
&& duration_diff(duration_us, TE_SHORT * 10) < TE_DELTA * 10
|
||||||
|
{
|
||||||
|
self.step = Step::SaveDuration;
|
||||||
|
self.decode_data = 0;
|
||||||
|
self.decode_count_bit = 0;
|
||||||
|
} else {
|
||||||
|
self.step = Step::Reset;
|
||||||
|
self.header_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Step::SaveDuration => {
|
||||||
|
if level {
|
||||||
|
self.te_last = duration_us;
|
||||||
|
self.step = Step::CheckDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Step::CheckDuration => {
|
||||||
|
if !level {
|
||||||
|
let end_threshold = TE_SHORT * 2 + TE_DELTA;
|
||||||
|
if duration_us >= end_threshold {
|
||||||
|
self.step = Step::Reset;
|
||||||
|
if self.decode_count_bit >= MIN_COUNT_BIT
|
||||||
|
&& self.decode_count_bit <= MIN_COUNT_BIT + 2
|
||||||
|
{
|
||||||
|
let raw_data = self.decode_data;
|
||||||
|
let reversed = reverse_key(raw_data, MIN_COUNT_BIT);
|
||||||
|
let key_fix = (reversed >> 32) as u32;
|
||||||
|
let key_hop = (reversed & 0xFFFFFFFF) as u32;
|
||||||
|
if let Some((_mf_name, serial, cnt, btn, key_for_encode)) =
|
||||||
|
try_keeloq_decrypt(key_fix, key_hop, self.seed)
|
||||||
|
{
|
||||||
|
if self.seed == 0 {
|
||||||
|
self.seed = key_fix & 0x0FFFFFFF;
|
||||||
|
}
|
||||||
|
self.decode_data = 0;
|
||||||
|
self.decode_count_bit = 0;
|
||||||
|
self.header_count = 0;
|
||||||
|
return Some(DecodedSignal {
|
||||||
|
serial: Some(serial),
|
||||||
|
button: Some(btn),
|
||||||
|
counter: Some(cnt),
|
||||||
|
crc_valid: true,
|
||||||
|
data: raw_data,
|
||||||
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
|
encoder_capable: true,
|
||||||
|
extra: key_for_encode,
|
||||||
|
protocol_display_name: Some(format!("KeeLoq ({})", _mf_name)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.decode_data = 0;
|
||||||
|
self.decode_count_bit = 0;
|
||||||
|
self.header_count = 0;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// Bit 1: te_last short, duration long
|
||||||
|
if duration_diff(self.te_last, TE_SHORT) < TE_DELTA
|
||||||
|
&& duration_diff(duration_us, TE_LONG) < TE_DELTA * 2
|
||||||
|
{
|
||||||
|
if self.decode_count_bit < MIN_COUNT_BIT {
|
||||||
|
self.add_bit(1);
|
||||||
|
} else {
|
||||||
|
self.decode_count_bit += 1;
|
||||||
|
}
|
||||||
|
self.step = Step::SaveDuration;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// Bit 0: te_last long, duration short
|
||||||
|
if duration_diff(self.te_last, TE_LONG) < TE_DELTA * 2
|
||||||
|
&& duration_diff(duration_us, TE_SHORT) < TE_DELTA
|
||||||
|
{
|
||||||
|
if self.decode_count_bit < MIN_COUNT_BIT {
|
||||||
|
self.add_bit(0);
|
||||||
|
} else {
|
||||||
|
self.decode_count_bit += 1;
|
||||||
|
}
|
||||||
|
self.step = Step::SaveDuration;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.reset_to(Step::Reset);
|
||||||
|
} else {
|
||||||
|
self.reset_to(Step::Reset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supports_encoding(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>> {
|
||||||
|
let serial = decoded.serial?;
|
||||||
|
let counter = decoded.counter.unwrap_or(0).wrapping_add(1);
|
||||||
|
let key = decoded.extra;
|
||||||
|
let fix = ((button as u32) << 28) | (serial & 0x0FFFFFFF);
|
||||||
|
let plaintext = ((button as u32) << 28)
|
||||||
|
| ((serial & 0x3FF) << 16)
|
||||||
|
| (counter as u32);
|
||||||
|
let hop = if let Some(k) = key {
|
||||||
|
keeloq_encrypt(plaintext, k)
|
||||||
|
} else {
|
||||||
|
let reversed = reverse_key(decoded.data, MIN_COUNT_BIT);
|
||||||
|
(reversed & 0xFFFFFFFF) as u32
|
||||||
|
};
|
||||||
|
let yek = ((fix as u64) << 32) | (hop as u64);
|
||||||
|
let data = reverse_key(yek, MIN_COUNT_BIT);
|
||||||
|
|
||||||
|
let mut signal = Vec::with_capacity(256);
|
||||||
|
for _ in 0..11 {
|
||||||
|
signal.push(LevelDuration::new(true, TE_SHORT));
|
||||||
|
signal.push(LevelDuration::new(false, TE_SHORT));
|
||||||
|
}
|
||||||
|
signal.push(LevelDuration::new(true, TE_SHORT));
|
||||||
|
signal.push(LevelDuration::new(false, TE_SHORT * 10));
|
||||||
|
|
||||||
|
for i in (0..MIN_COUNT_BIT).rev() {
|
||||||
|
if (data >> i) & 1 == 1 {
|
||||||
|
signal.push(LevelDuration::new(true, TE_SHORT));
|
||||||
|
signal.push(LevelDuration::new(false, TE_LONG));
|
||||||
|
} else {
|
||||||
|
signal.push(LevelDuration::new(true, TE_LONG));
|
||||||
|
signal.push(LevelDuration::new(false, TE_SHORT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signal.push(LevelDuration::new(true, TE_SHORT));
|
||||||
|
signal.push(LevelDuration::new(false, TE_LONG));
|
||||||
|
signal.push(LevelDuration::new(true, TE_SHORT));
|
||||||
|
signal.push(LevelDuration::new(false, TE_SHORT * 40));
|
||||||
|
|
||||||
|
Some(signal)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -105,6 +105,7 @@ fn try_kia_v3_v4_format(
|
|||||||
data_count_bit: KIA_V3_V4_BITS,
|
data_count_bit: KIA_V3_V4_BITS,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -148,6 +149,7 @@ fn try_star_line_format(
|
|||||||
data_count_bit: STAR_LINE_BITS,
|
data_count_bit: STAR_LINE_BITS,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -169,6 +171,7 @@ fn try_star_line_format(
|
|||||||
data_count_bit: STAR_LINE_BITS,
|
data_count_bit: STAR_LINE_BITS,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ impl KiaV0Decoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ impl KiaV1Decoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ impl KiaV2Decoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ impl KiaV3V4Decoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ impl KiaV5Decoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: false, // V5 is decode-only
|
encoder_capable: false, // V5 is decode-only
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -539,6 +539,7 @@ impl ProtocolDecoder for KiaV6Decoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: false,
|
encoder_capable: false,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-3
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
pub mod keeloq_common;
|
pub mod keeloq_common;
|
||||||
|
mod keeloq;
|
||||||
mod keeloq_generic;
|
mod keeloq_generic;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub mod aut64;
|
pub mod aut64;
|
||||||
@@ -102,6 +103,7 @@ impl ProtocolRegistry {
|
|||||||
Box::new(suzuki::SuzukiDecoder::new()),
|
Box::new(suzuki::SuzukiDecoder::new()),
|
||||||
Box::new(scher_khan::ScherKhanDecoder::new()),
|
Box::new(scher_khan::ScherKhanDecoder::new()),
|
||||||
Box::new(star_line::StarLineDecoder::new()),
|
Box::new(star_line::StarLineDecoder::new()),
|
||||||
|
Box::new(keeloq::KeeloqDecoder::new()),
|
||||||
Box::new(psa::PsaDecoder::new()),
|
Box::new(psa::PsaDecoder::new()),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -171,7 +173,11 @@ impl ProtocolRegistry {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(decoded) = decoder.feed(level, duration_us) {
|
if let Some(decoded) = decoder.feed(level, duration_us) {
|
||||||
hit = Some((decoder.name().to_string(), decoded));
|
let name = decoded
|
||||||
|
.protocol_display_name
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or_else(|| decoder.name());
|
||||||
|
hit = Some((name.to_string(), decoded));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,7 +226,11 @@ impl ProtocolRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(decoded) = decoder.feed(level, duration_us) {
|
if let Some(decoded) = decoder.feed(level, duration_us) {
|
||||||
return Some((decoder.name().to_string(), decoded));
|
let name = decoded
|
||||||
|
.protocol_display_name
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or_else(|| decoder.name());
|
||||||
|
return Some((name.to_string(), decoded));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,9 +256,14 @@ impl ProtocolRegistry {
|
|||||||
|
|
||||||
/// Get a decoder by name
|
/// Get a decoder by name
|
||||||
pub fn get(&self, name: &str) -> Option<&dyn ProtocolDecoder> {
|
pub fn get(&self, name: &str) -> Option<&dyn ProtocolDecoder> {
|
||||||
|
let lookup = if name.starts_with("KeeLoq") {
|
||||||
|
"KeeLoq"
|
||||||
|
} else {
|
||||||
|
name
|
||||||
|
};
|
||||||
self.decoders
|
self.decoders
|
||||||
.iter()
|
.iter()
|
||||||
.find(|d| d.name().eq_ignore_ascii_case(name))
|
.find(|d| d.name().eq_ignore_ascii_case(lookup))
|
||||||
.map(|d| d.as_ref())
|
.map(|d| d.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ impl PsaDecoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DecodedSignal {
|
DecodedSignal {
|
||||||
@@ -274,6 +275,7 @@ impl PsaDecoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: false,
|
encoder_capable: false,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ impl ScherKhanDecoder {
|
|||||||
data_count_bit: bit_count,
|
data_count_bit: bit_count,
|
||||||
encoder_capable: false,
|
encoder_capable: false,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ impl StarLineDecoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ impl SubaruDecoder {
|
|||||||
data_count_bit: 64,
|
data_count_bit: 64,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ impl SuzukiDecoder {
|
|||||||
data_count_bit: MIN_COUNT_BIT,
|
data_count_bit: MIN_COUNT_BIT,
|
||||||
encoder_capable: true,
|
encoder_capable: true,
|
||||||
extra: None,
|
extra: None,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -795,6 +795,7 @@ impl VagDecoder {
|
|||||||
data_count_bit: self.data_count_bit,
|
data_count_bit: self.data_count_bit,
|
||||||
encoder_capable: self.decrypted,
|
encoder_capable: self.decrypted,
|
||||||
extra,
|
extra,
|
||||||
|
protocol_display_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ pub fn render_captures_list(frame: &mut Frame, area: Rect, app: &App) {
|
|||||||
/// Render the compact signal table
|
/// Render the compact signal table
|
||||||
fn render_table(frame: &mut Frame, area: Rect, app: &App) {
|
fn render_table(frame: &mut Frame, area: Rect, app: &App) {
|
||||||
let header_cells = [
|
let header_cells = [
|
||||||
"ID", "Time", "Protocol", "Freq", "Serial", "Btn", "Cnt", "Mod", "CRC", "Status",
|
"ID", "Time", "Protocol", "Freq", "Serial", "Btn", "Cnt", "Modulation", "CRC", "Status",
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|h| Cell::from(*h).style(Style::default().add_modifier(Modifier::BOLD)));
|
.map(|h| Cell::from(*h).style(Style::default().add_modifier(Modifier::BOLD)));
|
||||||
@@ -99,12 +99,12 @@ fn render_table(frame: &mut Frame, area: Rect, app: &App) {
|
|||||||
let widths = [
|
let widths = [
|
||||||
Constraint::Length(4), // ID
|
Constraint::Length(4), // ID
|
||||||
Constraint::Length(9), // Time
|
Constraint::Length(9), // Time
|
||||||
Constraint::Length(10), // Protocol
|
Constraint::Length(24), // Protocol (e.g. KeeLoq (DoorHan))
|
||||||
Constraint::Length(11), // Freq
|
Constraint::Length(11), // Freq
|
||||||
Constraint::Length(9), // Serial
|
Constraint::Length(9), // Serial
|
||||||
Constraint::Length(6), // Btn
|
Constraint::Length(6), // Btn
|
||||||
Constraint::Length(6), // Cnt
|
Constraint::Length(6), // Cnt
|
||||||
Constraint::Length(7), // Mod
|
Constraint::Length(12), // Modulation
|
||||||
Constraint::Length(5), // CRC
|
Constraint::Length(5), // CRC
|
||||||
Constraint::Length(10), // Status
|
Constraint::Length(10), // Status
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user