Files
ProtoPirate/protocols/protocol_items.c
T

341 lines
13 KiB
C

#include "protocol_items.h"
#include <furi.h>
#include <string.h>
#include "../defines.h"
#define TAG "ProtoPirateCatalog"
#define PROTOPIRATE_CC1101_REG_MDMCFG2 0x12U
#define PROTOPIRATE_CC1101_MOD_FORMAT_MASK 0x70U
#define PROTOPIRATE_CC1101_MOD_FORMAT_2FSK 0x00U
#define PROTOPIRATE_CC1101_MOD_FORMAT_GFSK 0x10U
#define PROTOPIRATE_CC1101_MOD_FORMAT_ASK_OOK 0x30U
#define PROTOPIRATE_CC1101_MOD_FORMAT_4FSK 0x40U
#define PROTOPIRATE_CC1101_MOD_FORMAT_MSK 0x70U
#define PROTOPIRATE_VAG_FREQUENCY_MIN 434190000UL
#define PROTOPIRATE_VAG_FREQUENCY_MAX 434450000UL
#define PROTOPIRATE_COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
#ifdef ENABLE_EMULATE_FEATURE
#define PROTOPIRATE_TX_KEY(key) key
#else
#define PROTOPIRATE_TX_KEY(key) NULL
#endif
typedef enum {
ProtoPirateProtocolCatalogModulationAM = 0,
ProtoPirateProtocolCatalogModulationFM,
} ProtoPirateProtocolCatalogModulation;
typedef struct {
const char* alias;
const char* canonical_name;
} ProtoPirateProtocolCatalogAlias;
static const ProtoPirateProtocolCatalogEntry protopirate_protocol_catalog[] = {
{"Chrysler V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("chrysler_v0")},
{"Fiat V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("fiat_v0")},
{"Fiat V1", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("fiat_v1")},
{"Fiat V2", ProtoPirateProtocolCatalogRouteAMDefault, NULL},
{"Ford V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("ford_v0")},
{"Ford V1", ProtoPirateProtocolCatalogRouteFMF4, PROTOPIRATE_TX_KEY("ford_v1")},
{"Ford V2", ProtoPirateProtocolCatalogRouteFMF4, PROTOPIRATE_TX_KEY("ford_v2")},
{"Ford V3", ProtoPirateProtocolCatalogRouteFMF4, NULL},
{"Honda Static", ProtoPirateProtocolCatalogRouteFMHonda1, PROTOPIRATE_TX_KEY("honda_static")},
{"Honda V1", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("honda_v1")},
{"Kia V0", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v0")},
{"Kia V1", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("kia_v1")},
{"Kia V2", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("kia_v2")},
{"Kia V3/V4", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v3_v4")},
{"Kia V5", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v5")},
{"Kia V6", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v6")},
{"Kia V7", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v7")},
{"Honda V2", ProtoPirateProtocolCatalogRouteFMF4, PROTOPIRATE_TX_KEY("honda_v2")},
{"Mazda V0", ProtoPirateProtocolCatalogRouteByModulation, PROTOPIRATE_TX_KEY("mazda_v0")},
{"Mitsubishi V0", ProtoPirateProtocolCatalogRouteFMDefault, NULL},
{"Porsche Touareg", ProtoPirateProtocolCatalogRouteAMDefault, NULL},
{"PSA", ProtoPirateProtocolCatalogRouteByModulation, PROTOPIRATE_TX_KEY("psa")},
{"Renault V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("renault_v0")},
{"Scher-Khan", ProtoPirateProtocolCatalogRouteFMDefault, NULL},
{"Star Line", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("star_line")},
{"Subaru", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("subaru")},
{"VAG", ProtoPirateProtocolCatalogRouteAMVag, PROTOPIRATE_TX_KEY("vag")},
};
static const ProtoPirateProtocolCatalogAlias protopirate_protocol_catalog_aliases[] = {
{"StarLine", "Star Line"},
{"Kia V3", "Kia V3/V4"},
{"Kia V4", "Kia V3/V4"},
{"KIA/HYU V3", "Kia V3/V4"},
{"KIA/HYU V4", "Kia V3/V4"},
{"Suzuki", "Kia V0"},
{"Suzuki V0", "Kia V0"},
{"Honda V0", "Kia V0"},
{"Land Rover V0", "Honda V2"},
{"VW", "VAG"},
};
static bool protopirate_catalog_string_equal(const char* a, const char* b) {
return a && b && strcmp(a, b) == 0;
}
static bool protopirate_catalog_string_contains(const char* haystack, const char* needle) {
return haystack && needle && strstr(haystack, needle) != NULL;
}
static const ProtoPirateProtocolCatalogEntry*
protopirate_protocol_catalog_find_canonical(const char* canonical_name) {
if(!canonical_name || canonical_name[0] == '\0') {
return NULL;
}
for(size_t i = 0; i < PROTOPIRATE_COUNT_OF(protopirate_protocol_catalog); i++) {
if(protopirate_catalog_string_equal(
canonical_name, protopirate_protocol_catalog[i].canonical_name)) {
return &protopirate_protocol_catalog[i];
}
}
return NULL;
}
static const char* protopirate_protocol_catalog_alias_to_canonical(const char* protocol_name) {
if(!protocol_name || protocol_name[0] == '\0') {
return NULL;
}
for(size_t i = 0; i < PROTOPIRATE_COUNT_OF(protopirate_protocol_catalog_aliases); i++) {
if(protopirate_catalog_string_equal(
protocol_name, protopirate_protocol_catalog_aliases[i].alias)) {
return protopirate_protocol_catalog_aliases[i].canonical_name;
}
}
return NULL;
}
static const ProtoPirateProtocolCatalogEntry*
protopirate_protocol_catalog_find_substring(const char* protocol_name) {
if(!protocol_name || protocol_name[0] == '\0') {
return NULL;
}
for(size_t i = 0; i < PROTOPIRATE_COUNT_OF(protopirate_protocol_catalog); i++) {
if(protopirate_catalog_string_contains(
protocol_name, protopirate_protocol_catalog[i].canonical_name)) {
return &protopirate_protocol_catalog[i];
}
}
for(size_t i = 0; i < PROTOPIRATE_COUNT_OF(protopirate_protocol_catalog_aliases); i++) {
if(protopirate_catalog_string_contains(
protocol_name, protopirate_protocol_catalog_aliases[i].alias)) {
return protopirate_protocol_catalog_find_canonical(
protopirate_protocol_catalog_aliases[i].canonical_name);
}
}
return NULL;
}
const ProtoPirateProtocolCatalogEntry*
protopirate_protocol_catalog_find(const char* protocol_name) {
if(!protocol_name || protocol_name[0] == '\0') {
return NULL;
}
const ProtoPirateProtocolCatalogEntry* entry =
protopirate_protocol_catalog_find_canonical(protocol_name);
if(entry) {
return entry;
}
const char* canonical_name = protopirate_protocol_catalog_alias_to_canonical(protocol_name);
if(canonical_name) {
return protopirate_protocol_catalog_find_canonical(canonical_name);
}
return NULL;
}
const char* protopirate_protocol_catalog_canonical_name(const char* protocol_name) {
const ProtoPirateProtocolCatalogEntry* entry = protopirate_protocol_catalog_find(protocol_name);
return entry ? entry->canonical_name : protocol_name;
}
bool protopirate_protocol_catalog_can_tx(const char* protocol_name) {
return protopirate_protocol_catalog_tx_key(protocol_name) != NULL;
}
const char* protopirate_protocol_catalog_tx_key(const char* protocol_name) {
const ProtoPirateProtocolCatalogEntry* entry = protopirate_protocol_catalog_find(protocol_name);
return entry ? entry->tx_key : NULL;
}
const char*
protopirate_protocol_catalog_display_name(const char* protocol_name, uint32_t protocol_type) {
if(!protocol_name) {
return NULL;
}
if(protopirate_catalog_string_equal(protocol_name, "Suzuki") ||
protopirate_catalog_string_equal(protocol_name, "Suzuki V0")) {
return "Suzuki V0";
}
if(protopirate_catalog_string_equal(protocol_name, "Honda V0")) {
return "Honda V0";
}
const char* canonical_name = protopirate_protocol_catalog_canonical_name(protocol_name);
if(protopirate_catalog_string_equal(canonical_name, "Kia V0")) {
if(protocol_type == 2U) {
return "Suzuki V0";
}
if(protocol_type == 3U) {
return "Honda V0";
}
}
return canonical_name;
}
static bool protopirate_preset_try_get_register(
const uint8_t* preset_data,
size_t preset_data_size,
uint8_t reg,
uint8_t* value) {
if(!preset_data || !value || (preset_data_size < 2U)) {
return false;
}
for(size_t i = 0; i + 1U < preset_data_size; i += 2U) {
const uint8_t address = preset_data[i];
const uint8_t data = preset_data[i + 1U];
if((address == 0x00U) && (data == 0x00U)) {
break;
}
if(address == reg) {
*value = data;
return true;
}
}
return false;
}
static ProtoPirateProtocolCatalogModulation protopirate_protocol_catalog_get_modulation(
const uint8_t* preset_data,
size_t preset_data_size) {
uint8_t mdmcfg2 = 0U;
if(!protopirate_preset_try_get_register(
preset_data, preset_data_size, PROTOPIRATE_CC1101_REG_MDMCFG2, &mdmcfg2)) {
FURI_LOG_W(TAG, "Preset missing MDMCFG2, defaulting to AM registry");
return ProtoPirateProtocolCatalogModulationAM;
}
switch(mdmcfg2 & PROTOPIRATE_CC1101_MOD_FORMAT_MASK) {
case PROTOPIRATE_CC1101_MOD_FORMAT_ASK_OOK:
return ProtoPirateProtocolCatalogModulationAM;
case PROTOPIRATE_CC1101_MOD_FORMAT_2FSK:
case PROTOPIRATE_CC1101_MOD_FORMAT_GFSK:
case PROTOPIRATE_CC1101_MOD_FORMAT_4FSK:
case PROTOPIRATE_CC1101_MOD_FORMAT_MSK:
return ProtoPirateProtocolCatalogModulationFM;
default:
FURI_LOG_W(TAG, "Unknown MDMCFG2 0x%02X, defaulting to AM registry", mdmcfg2);
return ProtoPirateProtocolCatalogModulationAM;
}
}
static bool protopirate_frequency_in_vag_band(uint32_t frequency) {
return frequency >= PROTOPIRATE_VAG_FREQUENCY_MIN &&
frequency <= PROTOPIRATE_VAG_FREQUENCY_MAX;
}
static ProtoPirateProtocolRegistryRoute protopirate_catalog_route_from_policy(
ProtoPirateProtocolCatalogRoutePolicy policy,
ProtoPirateProtocolCatalogModulation modulation) {
switch(policy) {
case ProtoPirateProtocolCatalogRouteAMVag:
return ProtoPirateProtocolRegistryRouteAMVag;
case ProtoPirateProtocolCatalogRouteFMDefault:
return ProtoPirateProtocolRegistryRouteFMDefault;
case ProtoPirateProtocolCatalogRouteFMF4:
return ProtoPirateProtocolRegistryRouteFMF4;
case ProtoPirateProtocolCatalogRouteFMHonda1:
return ProtoPirateProtocolRegistryRouteFMHonda1;
case ProtoPirateProtocolCatalogRouteByModulation:
return (modulation == ProtoPirateProtocolCatalogModulationAM) ?
ProtoPirateProtocolRegistryRouteAMDefault :
ProtoPirateProtocolRegistryRouteFMDefault;
case ProtoPirateProtocolCatalogRouteAMDefault:
default:
return ProtoPirateProtocolRegistryRouteAMDefault;
}
}
ProtoPirateProtocolRegistryRoute protopirate_protocol_catalog_get_route(
const char* preset_name,
uint32_t frequency,
const uint8_t* preset_data,
size_t preset_data_size,
const char* protocol_name) {
const ProtoPirateProtocolCatalogEntry* entry =
protopirate_protocol_catalog_find(protocol_name);
if(!entry) {
entry = protopirate_protocol_catalog_find_substring(protocol_name);
}
if(entry && entry->route_policy != ProtoPirateProtocolCatalogRouteByModulation) {
return protopirate_catalog_route_from_policy(
entry->route_policy, ProtoPirateProtocolCatalogModulationAM);
}
const ProtoPirateProtocolCatalogModulation modulation =
protopirate_protocol_catalog_get_modulation(preset_data, preset_data_size);
if(entry) {
return protopirate_catalog_route_from_policy(entry->route_policy, modulation);
}
if(modulation == ProtoPirateProtocolCatalogModulationAM) {
if(protopirate_frequency_in_vag_band(frequency)) {
return ProtoPirateProtocolRegistryRouteAMVag;
}
return ProtoPirateProtocolRegistryRouteAMDefault;
}
if(protopirate_catalog_string_contains(preset_name, "F4")) {
return ProtoPirateProtocolRegistryRouteFMF4;
}
if(protopirate_catalog_string_contains(preset_name, "Honda1") ||
protopirate_catalog_string_contains(preset_name, "Honda 1")) {
return ProtoPirateProtocolRegistryRouteFMHonda1;
}
return ProtoPirateProtocolRegistryRouteFMDefault;
}
ProtoPirateProtocolRegistryRoute protopirate_get_protocol_registry_route(
const char* preset_name,
uint32_t frequency,
const uint8_t* preset_data,
size_t preset_data_size,
const char* protocol_name) {
return protopirate_protocol_catalog_get_route(
preset_name, frequency, preset_data, preset_data_size, protocol_name);
}
const char*
protopirate_get_protocol_registry_route_name(ProtoPirateProtocolRegistryRoute route) {
switch(route) {
case ProtoPirateProtocolRegistryRouteAMVag:
return "AM_VAG";
case ProtoPirateProtocolRegistryRouteFMDefault:
return "FM_DEFAULT";
case ProtoPirateProtocolRegistryRouteFMF4:
return "FM_F4";
case ProtoPirateProtocolRegistryRouteFMHonda1:
return "FM_HONDA1";
case ProtoPirateProtocolRegistryRouteAMDefault:
default:
return "AM_DEFAULT";
}
}