@xMasterX (MMX) --> Add StarLine and Scher-Khan protocol support plus encryptions and fixes

This commit is from @xMasterX (MMX) and introduces implementation files for StarLine and Scher-Khan SubGhz protocols, including protocol decoders, encoders, and keeloq key management. Adds common protocol utilities, keystore handling, and protocol-specific logic for key generation, serialization, and deserialization.
This commit is contained in:
RocketGod committed 2026-01-11 20:13:21 -08:00
1 parent 8a8b4ef9d1
commit a4937375ea
12 files changed
+1979 -43

No files matched your search

+4
View File
@@ -17,15 +17,19 @@ Decoders:
- KIA V6
- Fiat
- Ford V0
- Scher-Khan
- StarLine
- Subaru
- Suzuki
- Volkswagen (VW)
Encoders:
- Ford V0
- KIA V0
- KIA V1
- KIA V2
- StarLine
- More Coming Soon
## **Features**
+31
View File
@@ -0,0 +1,31 @@
Filetype: Flipper SubGhz Keystore File
Version: 0
Encryption: 1
IV: 4E 6F 20 70 72 65 79 2C 20 6E 6F 20 70 61 79 2E
EA7FE261D240CE6414D1D361E43AF320E38F6D922EB21E548D8D435464559BB7
29BA3E03BB2DB4C615050DFB7946828673099C4F6D86D09441161FB10CDB7B4D
18F800AA5B0332F2C0F4BCEB3D59686800BAB251D8F0DBC53B1F4EAC8A2E2387
7C097B02B18F69AE74596FB4DF6CE3351434CDAAE5412D231160BF91A1EDB0EF
08251F5CCE10B6BD15D9756FD52E67BCADBA41D15C995954BE029D44B5DB05AF
A5D615FF2348A9D96005C5E730633F4CC40D302BB2DF71FC64FD9E4E8196EAC9461F1D8F28E7B4A74226BD87D41B71C9
07F37E323DA69045B0A1638CAD103026116C79C4E7E4C722D9F2174EE9E329AF
37ED45AA9A2F5A9417D37810EC3119B2F20DACF5CD844E5DEEED456A71FE7C4E
46186986D6729A519FE9625C1ADDE6DFE29C0CE839B02FDCE35D6D5FEA364DBC
AE4A7B44CA6BFBFF8F95B31CE369FE72558AD42382DCF0FD80760E0714B45995
943C2F4CB7ACAA63D21B94AF50C2E0DB718E519B0021B2D8D7AF5769F6399F78
252941621E705A49F60DF319A66D1C59A545029EDE8492A858659DC692B3429685D7042BCA24CBD281179C2CD73156A3
8CE09F0770B9BA239E45F30A49EC136DAA0E4AC613B3E315B494DA424C7E5FE61F99FEF4DED839E4B2956FAA59657281
CD2F173EDEB6BFB8754583D1E1C8CCD65CC6E3E79B5A868A53AA350E419B6607
E49D6A039693731B6919CA29B416BE7067065D05F5B30BF9D8D1E0B8AC8EF364
026BD7A08BE3A3D0C3EBDA6FDD0206D83A2CFA891272FF1629169B03EA37E300
D9EE70D91292697EEAA6BA6564DB652182002B9A70848C3897C2CDBC129027DF
31AB2CAE71DA2C317EC216D90342B969660B7FE0956D24D4B3C8C9DFD8A69E7F4A36675B34EC109CDBB88C044985CA23
9C3A04D257748CC33FB013977E675EEBCECA502A3D10F4D2D7A28871A14A9D95
543D8FBCFA879F2DBB023AA07D80FB0BDAABA864811AB7E38FC9A425A04C48EF
DB8D083B1F209874629B046CC27E2DED5A251801C395CDF4A6940B4F05E13FA29CFC470020B217A3F94DF94D0C9C9EA4
7FD6DD318E2D9E3BB137247BC70D42D9B5ED18C93ED07154B932A8EDD107F012AB65283CE6668CA555EFADE608DDA79C
9E381D3E7D3D58FBECB3512C7D5DBDFB5E1708C79DC68E2EA6B13D82D3F9F851
EC148A795CA521C1EE70EA4FB1377DAE06CA957CA9DBDFB970314B95E4AD9A89
A2BE6299F878A91A60D3C0F4DD882A50F20EED7DB00C28AAC0A9E3B4609EAAC1
0889904CC32C152F3A5A6874E2C172075F46AB4E63C696E35211CC609953F8D7
E654760783674373EB889D80597AA35C416E8086FCE5DE4C02CA2EA42EBD569CA4A67F691ABC0209A6B208BAD5593CEE
+142
View File
@@ -0,0 +1,142 @@
#include "keeloq_common.h"
#include <furi.h>
#include <m-array.h>
#define bit(x, n) (((x) >> (n)) & 1)
#define g5(x, a, b, c, d, e) \
(bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16)
/** Simple Learning Encrypt
* @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
* @param key - manufacture (64bit)
* @return keeloq encrypt data
*/
inline uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key) {
uint32_t x = data, r;
for(r = 0; r < 528; r++)
x = (x >> 1) ^ ((bit(x, 0) ^ bit(x, 16) ^ (uint32_t)bit(key, r & 63) ^
bit(KEELOQ_NLF, g5(x, 1, 9, 20, 26, 31)))
<< 31);
return x;
}
/** Simple Learning Decrypt
* @param data - keeloq encrypt data
* @param key - manufacture (64bit)
* @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
*/
inline uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key) {
uint32_t x = data, r;
for(r = 0; r < 528; r++)
x = (x << 1) ^ bit(x, 31) ^ bit(x, 15) ^ (uint32_t)bit(key, (15 - r) & 63) ^
bit(KEELOQ_NLF, g5(x, 0, 8, 19, 25, 30));
return x;
}
/** Normal Learning
* @param data - serial number (28bit)
* @param key - manufacture (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key) {
uint32_t k1, k2;
data &= 0x0FFFFFFF;
data |= 0x20000000;
k1 = subghz_protocol_keeloq_common_decrypt(data, key);
data &= 0x0FFFFFFF;
data |= 0x60000000;
k2 = subghz_protocol_keeloq_common_decrypt(data, key);
return ((uint64_t)k2 << 32) | k1; // key - shifrovanoya
}
/** Secure Learning
* @param data - serial number (28bit)
* @param seed - seed number (32bit)
* @param key - manufacture (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t subghz_protocol_keeloq_common_secure_learning(
uint32_t data,
uint32_t seed,
const uint64_t key) {
uint32_t k1, k2;
data &= 0x0FFFFFFF;
k1 = subghz_protocol_keeloq_common_decrypt(data, key);
k2 = subghz_protocol_keeloq_common_decrypt(seed, key);
return ((uint64_t)k1 << 32) | k2;
}
/** Magic_xor_type1 Learning
* @param data - serial number (28bit)
* @param xor - magic xor (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t
subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor) {
data &= 0x0FFFFFFF;
return (((uint64_t)data << 32) | data) ^ xor;
}
/** Faac SLH (Spa) Learning
* @param seed - seed number (32bit)
* @param key - mfkey (64bit)
* @return man_learning for this seed number (64bit)
*/
inline uint64_t
subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const uint64_t key) {
uint16_t hs = seed >> 16;
const uint16_t ending = 0x544D;
uint32_t lsb = (uint32_t)hs << 16 | ending;
uint64_t man = (uint64_t)subghz_protocol_keeloq_common_encrypt(seed, key) << 32 |
subghz_protocol_keeloq_common_encrypt(lsb, key);
return man;
}
/** Magic_serial_type1 Learning
* @param data - serial number (28bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) {
return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) |
((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32);
}
/** Magic_serial_type2 Learning
* @param data - btn+serial number (32bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) {
uint8_t* p = (uint8_t*)&data;
uint8_t* m = (uint8_t*)&man;
m[7] = p[0];
m[6] = p[1];
m[5] = p[2];
m[4] = p[3];
return man;
}
/** Magic_serial_type3 Learning
* @param data - serial number (24bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) {
return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF);
}
+115
View File
@@ -0,0 +1,115 @@
#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 <lib/toolbox/manchester_decoder.h>
#include <m-array.h>
struct SubGhzKeystore {
SubGhzKeyArray_t data;
const char* mfname;
uint8_t kl_type;
};
/*
* Keeloq
* https://ru.wikipedia.org/wiki/KeeLoq
* https://phreakerclub.com/forum/showthread.php?t=1094
*
*/
#define KEELOQ_NLF 0x3A5C742E
/*
* KeeLoq learning types
* https://phreakerclub.com/forum/showthread.php?t=67
*/
#define KEELOQ_LEARNING_UNKNOWN 0u
#define KEELOQ_LEARNING_SIMPLE 1u
#define KEELOQ_LEARNING_NORMAL 2u
// #define KEELOQ_LEARNING_SECURE 3u
#define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u
// #define KEELOQ_LEARNING_FAAC 5u
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 6u
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 7u
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 8u
/**
* Simple Learning Encrypt
* @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
* @param key - manufacture (64bit)
* @return keeloq encrypt data
*/
uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key);
/**
* Simple Learning Decrypt
* @param data - keeloq encrypt data
* @param key - manufacture (64bit)
* @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
*/
uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key);
/**
* Normal Learning
* @param data - serial number (28bit)
* @param key - manufacture (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key);
/**
* Secure Learning
* @param data - serial number (28bit)
* @param seed - seed number (32bit)
* @param key - manufacture (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t
subghz_protocol_keeloq_common_secure_learning(uint32_t data, uint32_t seed, const uint64_t key);
/**
* Magic_xor_type1 Learning
* @param data - serial number (28bit)
* @param xor - magic xor (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor);
/** Faac SLH (Spa) Learning
* @param seed - seed number (32bit)
* @param key - mfkey (64bit)
* @return man_learning for this fix number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const uint64_t key);
/** Magic_serial_type1 Learning
* @param data - serial number (28bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man);
/** Magic_serial_type2 Learning
* @param data - btn+serial number (32bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man);
/** Magic_serial_type3 Learning
* @param data - btn+serial number (32bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man);
+40
View File
@@ -0,0 +1,40 @@
#include "keys.h"
#define KIA_KEY1 10u
#define KIA_KEY2 11u
#define KIA_KEY3 12u
uint64_t kia_mf_key = 0;
uint64_t kia_v6_a_key = 0;
uint64_t kia_v6_b_key = 0;
void protopirate_keys_load(SubGhzEnvironment* environment) {
SubGhzKeystore* keystore = subghz_environment_get_keystore(environment);
// Load keys from secure keystore
for
M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) {
switch(manufacture_code->type) {
case KIA_KEY1:
kia_mf_key = manufacture_code->key;
break;
case KIA_KEY2:
kia_v6_a_key = manufacture_code->key;
break;
case KIA_KEY3:
kia_v6_b_key = manufacture_code->key;
break;
}
}
}
uint64_t get_kia_mf_key() {
return kia_mf_key;
}
uint64_t get_kia_v6_keystore_a() {
return kia_v6_a_key;
}
uint64_t get_kia_v6_keystore_b() {
return kia_v6_b_key;
}
+18
View File
@@ -0,0 +1,18 @@
#include "protocols_common.h"
const char* protopirate_get_short_preset_name(const char* preset_name) {
if(!strcmp(preset_name, "FuriHalSubGhzPresetOok270Async")) {
return "AM270";
} else if(!strcmp(preset_name, "FuriHalSubGhzPresetOok650Async")) {
return "AM650";
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev238Async")) {
return "FM238";
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev12KAsync")) {
return "FM12K";
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev476Async")) {
return "FM476";
} else if(!strcmp(preset_name, "FuriHalSubGhzPresetCustom")) {
return "CUSTOM";
}
return preset_name;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <lib/subghz/types.h>
const char* protopirate_get_short_preset_name(const char* preset_name);
+361
View File
@@ -0,0 +1,361 @@
#include "scher_khan.h"
#include "protocols_common.h"
//https://phreakerclub.com/72
//https://phreakerclub.com/forum/showthread.php?t=7&page=2
//https://phreakerclub.com/forum/showthread.php?t=274&highlight=magicar
//!!! https://phreakerclub.com/forum/showthread.php?t=489&highlight=magicar&page=5
#define TAG "SubGhzProtocolScherKhan"
static const SubGhzBlockConst subghz_protocol_scher_khan_const = {
.te_short = 750,
.te_long = 1100,
.te_delta = 180,
.min_count_bit_for_found = 35,
};
struct SubGhzProtocolDecoderScherKhan {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint16_t header_count;
const char* protocol_name;
};
struct SubGhzProtocolEncoderScherKhan {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
};
typedef enum {
ScherKhanDecoderStepReset = 0,
ScherKhanDecoderStepCheckPreambula,
ScherKhanDecoderStepSaveDuration,
ScherKhanDecoderStepCheckDuration,
} ScherKhanDecoderStep;
const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder = {
.alloc = subghz_protocol_decoder_scher_khan_alloc,
.free = subghz_protocol_decoder_scher_khan_free,
.feed = subghz_protocol_decoder_scher_khan_feed,
.reset = subghz_protocol_decoder_scher_khan_reset,
.get_hash_data = subghz_protocol_decoder_scher_khan_get_hash_data,
.serialize = subghz_protocol_decoder_scher_khan_serialize,
.deserialize = subghz_protocol_decoder_scher_khan_deserialize,
.get_string = subghz_protocol_decoder_scher_khan_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol subghz_protocol_scher_khan = {
.name = SUBGHZ_PROTOCOL_SCHER_KHAN_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,
.decoder = &subghz_protocol_scher_khan_decoder,
.encoder = &subghz_protocol_scher_khan_encoder,
};
void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderScherKhan* instance = malloc(sizeof(SubGhzProtocolDecoderScherKhan));
instance->base.protocol = &subghz_protocol_scher_khan;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_scher_khan_free(void* context) {
furi_assert(context);
SubGhzProtocolDecoderScherKhan* instance = context;
free(instance);
}
void subghz_protocol_decoder_scher_khan_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderScherKhan* instance = context;
instance->decoder.parser_step = ScherKhanDecoderStepReset;
}
void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
SubGhzProtocolDecoderScherKhan* instance = context;
switch(instance->decoder.parser_step) {
case ScherKhanDecoderStepReset:
if((level) && (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) <
subghz_protocol_scher_khan_const.te_delta)) {
instance->decoder.parser_step = ScherKhanDecoderStepCheckPreambula;
instance->decoder.te_last = duration;
instance->header_count = 0;
}
break;
case ScherKhanDecoderStepCheckPreambula:
if(level) {
if((DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) <
subghz_protocol_scher_khan_const.te_delta) ||
(DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) <
subghz_protocol_scher_khan_const.te_delta)) {
instance->decoder.te_last = duration;
} else {
instance->decoder.parser_step = ScherKhanDecoderStepReset;
}
} else if(
(DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) <
subghz_protocol_scher_khan_const.te_delta) ||
(DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) <
subghz_protocol_scher_khan_const.te_delta)) {
if(DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short * 2) <
subghz_protocol_scher_khan_const.te_delta) {
// Found header
instance->header_count++;
break;
} else if(
DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) <
subghz_protocol_scher_khan_const.te_delta) {
// Found start bit
if(instance->header_count >= 2) {
instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 1;
} else {
instance->decoder.parser_step = ScherKhanDecoderStepReset;
}
} else {
instance->decoder.parser_step = ScherKhanDecoderStepReset;
}
} else {
instance->decoder.parser_step = ScherKhanDecoderStepReset;
}
break;
case ScherKhanDecoderStepSaveDuration:
if(level) {
if(duration >= (subghz_protocol_scher_khan_const.te_delta * 2UL +
subghz_protocol_scher_khan_const.te_long)) {
//Found stop bit
instance->decoder.parser_step = ScherKhanDecoderStepReset;
if(instance->decoder.decode_count_bit >=
subghz_protocol_scher_khan_const.min_count_bit_for_found) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
break;
} else {
instance->decoder.te_last = duration;
instance->decoder.parser_step = ScherKhanDecoderStepCheckDuration;
}
} else {
instance->decoder.parser_step = ScherKhanDecoderStepReset;
}
break;
case ScherKhanDecoderStepCheckDuration:
if(!level) {
if((DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) <
subghz_protocol_scher_khan_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) <
subghz_protocol_scher_khan_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration;
} else if(
(DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_scher_khan_const.te_long) <
subghz_protocol_scher_khan_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_long) <
subghz_protocol_scher_khan_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration;
} else {
instance->decoder.parser_step = ScherKhanDecoderStepReset;
}
} else {
instance->decoder.parser_step = ScherKhanDecoderStepReset;
}
break;
}
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
* @param protocol_name
*/
static void subghz_protocol_scher_khan_check_remote_controller(
SubGhzBlockGeneric* instance,
const char** protocol_name) {
/*
* MAGICAR 51 bit 00000001A99121DE83C3 MAGIC CODE, Dynamic
* 0E8C1619E830C -> 000011101000110000010110 0001 1001 1110 1000001100001100
* 0E8C1629D830D -> 000011101000110000010110 0010 1001 1101 1000001100001101
* 0E8C1649B830E -> 000011101000110000010110 0100 1001 1011 1000001100001110
* 0E8C16897830F -> 000011101000110000010110 1000 1001 0111 1000001100001111
* Serial Key Ser ~Key CNT
*/
switch(instance->data_count_bit) {
case 35: //MAGIC CODE, Static
*protocol_name = "MAGIC CODE, Static";
instance->serial = 0;
instance->btn = 0;
instance->cnt = 0;
break;
case 51: //MAGIC CODE, Dynamic
*protocol_name = "MAGIC CODE, Dynamic";
instance->serial = ((instance->data >> 24) & 0xFFFFFF0) | ((instance->data >> 20) & 0x0F);
instance->btn = (instance->data >> 24) & 0x0F;
instance->cnt = instance->data & 0xFFFF;
break;
case 57: //MAGIC CODE PRO / PRO2
*protocol_name = "MAGIC CODE PRO/PRO2";
instance->serial = 0;
instance->btn = 0;
instance->cnt = 0;
break;
case 63: //MAGIC CODE, Dynamic Response
*protocol_name = "MAGIC CODE, Response";
instance->serial = 0;
instance->btn = 0;
instance->cnt = 0;
break;
case 64: //MAGICAR, Response ???
*protocol_name = "MAGICAR, Response";
instance->serial = 0;
instance->btn = 0;
instance->cnt = 0;
break;
case 81: // MAGIC CODE PRO / PRO2 Response ???
case 82: // MAGIC CODE PRO / PRO2 Response ???
*protocol_name = "MAGIC CODE PRO,\n Response";
instance->serial = 0;
instance->btn = 0;
instance->cnt = 0;
break;
default:
*protocol_name = "Unknown";
instance->serial = 0;
instance->btn = 0;
instance->cnt = 0;
break;
}
}
uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderScherKhan* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_scher_khan_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
SubGhzProtocolDecoderScherKhan* instance = context;
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
do {
if(preset != NULL) {
if(!flipper_format_insert_or_update_uint32(
flipper_format, "Frequency", &preset->frequency, 1)) {
break;
}
const char* preset_name = furi_string_get_cstr(preset->name);
const char* short_preset = protopirate_get_short_preset_name(preset_name);
if(!flipper_format_insert_or_update_string_cstr(
flipper_format, "Preset", short_preset)) {
break;
}
}
if(!flipper_format_insert_or_update_string_cstr(
flipper_format, "Protocol", instance->generic.protocol_name)) {
break;
}
uint32_t bits = instance->generic.data_count_bit;
if(!flipper_format_insert_or_update_uint32(flipper_format, "Bit", &bits, 1)) {
break;
}
char key_str[20];
snprintf(key_str, sizeof(key_str), "%016llX", instance->generic.data);
if(!flipper_format_insert_or_update_string_cstr(flipper_format, "Key", key_str)) {
break;
}
if(!flipper_format_insert_or_update_uint32(
flipper_format, "Serial", &instance->generic.serial, 1)) {
break;
}
uint32_t temp = instance->generic.btn;
if(!flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp, 1)) {
break;
}
if(!flipper_format_insert_or_update_uint32(
flipper_format, "Cnt", &instance->generic.cnt, 1)) {
break;
}
ret = SubGhzProtocolStatusOk;
} while(false);
return ret;
}
SubGhzProtocolStatus
subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderScherKhan* instance = context;
return subghz_block_generic_deserialize(&instance->generic, flipper_format);
}
void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output) {
furi_assert(context);
SubGhzProtocolDecoderScherKhan* instance = context;
subghz_protocol_scher_khan_check_remote_controller(
&instance->generic, &instance->protocol_name);
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Key:0x%lX%08lX\r\n"
"Sn:%07lX Btn:%X\r\n"
"Cntr:%04lX\r\n"
"Pt: %s\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32),
(uint32_t)instance->generic.data,
instance->generic.serial,
instance->generic.btn,
instance->generic.cnt,
instance->protocol_name);
}
+82
View File
@@ -0,0 +1,82 @@
#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 <lib/toolbox/manchester_decoder.h>
#include <flipper_format/flipper_format.h>
#define SUBGHZ_PROTOCOL_SCHER_KHAN_NAME "Scher-Khan"
typedef struct SubGhzProtocolDecoderScherKhan SubGhzProtocolDecoderScherKhan;
typedef struct SubGhzProtocolEncoderScherKhan SubGhzProtocolEncoderScherKhan;
extern const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder;
extern const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder;
extern const SubGhzProtocol subghz_protocol_scher_khan;
/**
* Allocate SubGhzProtocolDecoderScherKhan.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolDecoderScherKhan* pointer to a SubGhzProtocolDecoderScherKhan instance
*/
void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolDecoderScherKhan.
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
*/
void subghz_protocol_decoder_scher_khan_free(void* context);
/**
* Reset decoder SubGhzProtocolDecoderScherKhan.
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
*/
void subghz_protocol_decoder_scher_khan_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
* @return hash Hash sum
*/
uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context);
/**
* Serialize data SubGhzProtocolDecoderScherKhan.
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return status
*/
SubGhzProtocolStatus subghz_protocol_decoder_scher_khan_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data SubGhzProtocolDecoderScherKhan.
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
* @param output Resulting text
*/
void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output);
File diff suppressed because it is too large. Load diff
+117
View File
@@ -0,0 +1,117 @@
#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 <lib/toolbox/manchester_decoder.h>
#include <flipper_format/flipper_format.h>
#define SUBGHZ_PROTOCOL_STAR_LINE_NAME "Star Line"
typedef struct SubGhzProtocolDecoderStarLine SubGhzProtocolDecoderStarLine;
typedef struct SubGhzProtocolEncoderStarLine SubGhzProtocolEncoderStarLine;
extern const SubGhzProtocolDecoder subghz_protocol_star_line_decoder;
extern const SubGhzProtocolEncoder subghz_protocol_star_line_encoder;
extern const SubGhzProtocol subghz_protocol_star_line;
/**
* Allocate SubGhzProtocolEncoderStarLine.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolEncoderStarLine* pointer to a SubGhzProtocolEncoderStarLine instance
*/
void* subghz_protocol_encoder_star_line_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolEncoderStarLine.
* @param context Pointer to a SubGhzProtocolEncoderStarLine instance
*/
void subghz_protocol_encoder_star_line_free(void* context);
/**
* Deserialize and generating an upload to send.
* @param context Pointer to a SubGhzProtocolEncoderStarLine instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
SubGhzProtocolStatus
subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Forced transmission stop.
* @param context Pointer to a SubGhzProtocolEncoderStarLine instance
*/
void subghz_protocol_encoder_star_line_stop(void* context);
/**
* Getting the level and duration of the upload to be loaded into DMA.
* @param context Pointer to a SubGhzProtocolEncoderStarLine instance
* @return LevelDuration
*/
LevelDuration subghz_protocol_encoder_star_line_yield(void* context);
/**
* Allocate SubGhzProtocolDecoderStarLine.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolDecoderStarLine* pointer to a SubGhzProtocolDecoderStarLine instance
*/
void* subghz_protocol_decoder_star_line_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolDecoderStarLine.
* @param context Pointer to a SubGhzProtocolDecoderStarLine instance
*/
void subghz_protocol_decoder_star_line_free(void* context);
/**
* Reset decoder SubGhzProtocolDecoderStarLine.
* @param context Pointer to a SubGhzProtocolDecoderStarLine instance
*/
void subghz_protocol_decoder_star_line_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a SubGhzProtocolDecoderStarLine instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a SubGhzProtocolDecoderStarLine instance
* @return hash Hash sum
*/
uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context);
/**
* Serialize data SubGhzProtocolDecoderStarLine.
* @param context Pointer to a SubGhzProtocolDecoderStarLine instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return status
*/
SubGhzProtocolStatus subghz_protocol_decoder_star_line_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data SubGhzProtocolDecoderStarLine.
* @param context Pointer to a SubGhzProtocolDecoderStarLine instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a SubGhzProtocolDecoderStarLine instance
* @param output Resulting text
*/
void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output);
+55 -43
View File
@@ -5,34 +5,31 @@
#include <furi_hal.h>
#include "protocols/protocol_items.h"
#include "helpers/protopirate_settings.h"
#include "helpers/protopirate_storage.h"
#include "helpers/protopirate_storage.h"
#include "protocols/keys.h"
#define TAG "ProtoPirateApp"
static bool protopirate_app_custom_event_callback(void *context, uint32_t event)
{
static bool protopirate_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
ProtoPirateApp *app = context;
ProtoPirateApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool protopirate_app_back_event_callback(void *context)
{
static bool protopirate_app_back_event_callback(void* context) {
furi_assert(context);
ProtoPirateApp *app = context;
ProtoPirateApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void protopirate_app_tick_event_callback(void *context)
{
static void protopirate_app_tick_event_callback(void* context) {
furi_assert(context);
ProtoPirateApp *app = context;
ProtoPirateApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
ProtoPirateApp *protopirate_app_alloc()
{
ProtoPirateApp *app = malloc(sizeof(ProtoPirateApp));
ProtoPirateApp* protopirate_app_alloc() {
ProtoPirateApp* app = malloc(sizeof(ProtoPirateApp));
FURI_LOG_I(TAG, "Allocating ProtoPirate Decoder App");
@@ -99,7 +96,7 @@ ProtoPirateApp *protopirate_app_alloc()
// Load saved settings
ProtoPirateSettings settings;
protopirate_settings_load(&settings);
// Apply auto-save setting
app->auto_save = settings.auto_save;
@@ -114,7 +111,7 @@ ProtoPirateApp *protopirate_app_alloc()
// Apply loaded frequency and preset, with validation
uint32_t frequency = settings.frequency;
uint8_t preset_index = settings.preset_index;
// Validate frequency - check if it exists in settings
bool frequency_valid = false;
for(size_t i = 0; i < subghz_setting_get_frequency_count(app->setting); i++) {
@@ -127,26 +124,31 @@ ProtoPirateApp *protopirate_app_alloc()
frequency = subghz_setting_get_default_frequency(app->setting);
FURI_LOG_W(TAG, "Saved frequency invalid, using default: %lu", frequency);
}
// Validate preset index
if(preset_index >= subghz_setting_get_preset_count(app->setting)) {
preset_index = 0;
FURI_LOG_W(TAG, "Saved preset index invalid, using default");
}
// Get preset name and data
const char* preset_name = subghz_setting_get_preset_name(app->setting, preset_index);
uint8_t* preset_data = subghz_setting_get_preset_data(app->setting, preset_index);
size_t preset_data_size = subghz_setting_get_preset_data_size(app->setting, preset_index);
FURI_LOG_I(TAG, "Applying settings: freq=%lu, preset=%s, auto_save=%d, hopping=%d",
frequency, preset_name, settings.auto_save, settings.hopping_enabled);
FURI_LOG_I(
TAG,
"Applying settings: freq=%lu, preset=%s, auto_save=%d, hopping=%d",
frequency,
preset_name,
settings.auto_save,
settings.hopping_enabled);
protopirate_preset_init(app, preset_name, frequency, preset_data, preset_data_size);
// Apply hopping state from settings
app->txrx->hopper_state = settings.hopping_enabled ?
ProtoPirateHopperStateRunning : ProtoPirateHopperStateOFF;
app->txrx->hopper_state = settings.hopping_enabled ? ProtoPirateHopperStateRunning :
ProtoPirateHopperStateOFF;
app->txrx->hopper_idx_frequency = 0;
app->txrx->hopper_timeout = 0;
app->txrx->idx_menu_chosen = 0;
@@ -157,9 +159,17 @@ ProtoPirateApp *protopirate_app_alloc()
// Create environment with our custom protocols
app->txrx->environment = subghz_environment_alloc();
// Load keystores
subghz_environment_load_keystore(app->txrx->environment, PROTOPIRATE_KEYSTORE_DIR_NAME);
subghz_environment_load_keystore(app->txrx->environment, SUBGHZ_KEYSTORE_DIR_USER_NAME);
FURI_LOG_I(TAG, "Registering %zu ProtoPirate protocols", protopirate_protocol_registry.size);
subghz_environment_set_protocol_registry(
app->txrx->environment, (void *)&protopirate_protocol_registry);
app->txrx->environment, (void*)&protopirate_protocol_registry);
// Load ProtoPirate specific keys
protopirate_keys_load(app->txrx->environment);
FURI_LOG_I(TAG, "Loaded ProtoPirate secure keys");
// Create receiver
app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment);
@@ -168,15 +178,16 @@ ProtoPirateApp *protopirate_app_alloc()
subghz_devices_init();
// Try external CC1101 first, fallback to internal
app->txrx->radio_device =
radio_device_loader_set(NULL, SubGhzRadioDeviceTypeExternalCC1101);
app->txrx->radio_device = radio_device_loader_set(NULL, SubGhzRadioDeviceTypeExternalCC1101);
if(!app->txrx->radio_device) {
FURI_LOG_E(TAG, "Failed to initialize any radio device!");
} else {
const char* device_name = subghz_devices_get_name(app->txrx->radio_device);
bool is_external = device_name && strstr(device_name, "ext");
FURI_LOG_I(TAG, "Radio device initialized: %s (%s)",
FURI_LOG_I(
TAG,
"Radio device initialized: %s (%s)",
device_name ? device_name : "unknown",
is_external ? "external" : "internal");
}
@@ -201,21 +212,20 @@ ProtoPirateApp *protopirate_app_alloc()
return app;
}
void protopirate_app_free(ProtoPirateApp *app)
{
void protopirate_app_free(ProtoPirateApp* app) {
furi_assert(app);
FURI_LOG_I(TAG, "Freeing ProtoPirate Decoder App");
// Free the storage file list cache
protopirate_storage_free_file_list();
protopirate_storage_free_file_list();
// Save settings before exiting
ProtoPirateSettings settings;
settings.frequency = app->txrx->preset->frequency;
settings.auto_save = app->auto_save;
settings.hopping_enabled = (app->txrx->hopper_state != ProtoPirateHopperStateOFF);
// Find current preset index
settings.preset_index = 0;
const char* current_preset = furi_string_get_cstr(app->txrx->preset->name);
@@ -225,21 +235,24 @@ void protopirate_app_free(ProtoPirateApp *app)
break;
}
}
FURI_LOG_I(TAG, "Saving settings: freq=%lu, preset=%u, auto_save=%d, hopping=%d",
settings.frequency, settings.preset_index, settings.auto_save, settings.hopping_enabled);
FURI_LOG_I(
TAG,
"Saving settings: freq=%lu, preset=%u, auto_save=%d, hopping=%d",
settings.frequency,
settings.preset_index,
settings.auto_save,
settings.hopping_enabled);
protopirate_settings_save(&settings);
// Make sure we're not receiving
if (app->txrx->txrx_state == ProtoPirateTxRxStateRx)
{
if(app->txrx->txrx_state == ProtoPirateTxRxStateRx) {
subghz_worker_stop(app->txrx->worker);
subghz_devices_stop_async_rx(app->txrx->radio_device);
}
if (app->loaded_file_path)
{
if(app->loaded_file_path) {
furi_string_free(app->loaded_file_path);
}
@@ -300,14 +313,13 @@ void protopirate_app_free(ProtoPirateApp *app)
free(app);
}
int32_t protopirate_app(void *p)
{
int32_t protopirate_app(void* p) {
UNUSED(p);
ProtoPirateApp *protopirate_app = protopirate_app_alloc();
ProtoPirateApp* protopirate_app = protopirate_app_alloc();
view_dispatcher_run(protopirate_app->view_dispatcher);
protopirate_app_free(protopirate_app);
return 0;
}
}