mirror of
https://github.com/D4C1-Labs/Flipper-ARF.git
synced 2026-07-02 15:21:37 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 46d7e1263c |
@@ -29,12 +29,16 @@ Protocols are split into **AM** and **FM** registries. The active registry is ch
|
||||
| ------------------------ | ------- | ------- | --------------- | ---------- | ------------------------------ | ------------ | --------------- |
|
||||
| Chrysler V0 | ✅ | ✅ | PWM | AM650 | Rolling Code | Checksum | 315.00 / 433.92 |
|
||||
| Fiat V0 | ✅ | ✅ | Manchester | AM650 | Rolling Code (static emu only) | ❌ | 315.00 / 433.92 |
|
||||
| Fiat V1 | ✅ | ❌ | Manchester | AM650 | Rolling Code | CRC8 | 315.00 / 433.92 |
|
||||
| Fiat V1 | ✅ | ✅ | Manchester | AM650 | HITAG2 | XOR8 | 315.00 / 433.92 |
|
||||
| Fiat V2 | ✅ | ❌ | Manchester | AM650 | Rolling Code | ❌ | 315.00 / 433.92 |
|
||||
| Ford V0 | ✅ | ✅ | Manchester | AM650 | Rolling Code | ✅ + Checksum | 315.00 / 433.92 |
|
||||
| Ford V3 | ✅ | ❌ | Manchester | AM650 | Rolling Code | ❌ | 434.25 |
|
||||
| Honda V1 | ✅ | ✅ | Manchester | AM650 | Rolling Code | CRC4 | 315.00 / 433.92 |
|
||||
| Kia V1 | ✅ | ✅ | Manchester | AM650 | Rolling Code | CRC4 | 315.00 / 433.92 |
|
||||
| Mazda V0 | ✅ | ✅ | Manchester | AM650 | Rolling Code | Checksum | 315.00 / 433.92 |
|
||||
| Porsche Touareg | ✅ | ❌ | PWM | AM650 | Rolling Code | ❌ | 315.00 / 433.92 |
|
||||
| PSA (Peugeot/Citroen) | ✅ | ✅ | Manchester | AM650 | XTEA/XOR | CRC8 | 315.00 / 433.92 |
|
||||
| Renault V0 | ✅ | ✅ | Manchester | AM650 | Rolling Code / Replay | Type/IC | 315.00 / 433.92 |
|
||||
| StarLine | ✅ | ✅ | PWM | AM650 | KeeLoq | ❌ | 315.00 / 433.92 |
|
||||
| Subaru | ✅ | ✅ | PPM | AM650 | Rolling Code | ❌ | 315.00 / 433.92 |
|
||||
| VAG (VW/Audi/Seat/Skoda) | ✅ | ✅ | Manchester | AM650 | AUT64/XTEA | ❌ | 434.42 |
|
||||
@@ -55,7 +59,7 @@ Protocols are split into **AM** and **FM** registries. The active registry is ch
|
||||
| Kia V5 | ✅ | ✅ | PWM | FM476 | Rolling Code | ✅ | 315.00 / 433.92 |
|
||||
| Kia V6 | ✅ | ✅ | Manchester | FM476 | AES128 | CRC8 | 315.00 / 433.92 |
|
||||
| Kia V7 | ✅ | ✅ | Manchester | FM476 | Rolling Code | CRC8 | 315.00 / 433.92 |
|
||||
| Land Rover V0 | ✅ | ✅ | PWM | F4 | Rolling Code | Check+Tail | 315.00 / 433.92 |
|
||||
| Honda V2 | ✅ | ✅ | PWM | F4 | Rolling Code | Check+Tail | 315.00 / 433.92 |
|
||||
| Mazda V0 | ✅ | ✅ | Manchester | FM (F2?) | Rolling Code | Checksum | 315.00 / 433.92 |
|
||||
| Mitsubishi V0 | ✅ | ❌ | PWM | FM476 | Rolling Code | ❌ | 315.00 / 433.92 |
|
||||
| PSA (Peugeot/Citroen) | ✅ | ✅ | Manchester | FM (F3?) | XTEA/XOR | CRC8 | 315.00 / 433.92 |
|
||||
|
||||
@@ -1,3 +1,35 @@
|
||||
# --- Main app ---
|
||||
|
||||
import os
|
||||
|
||||
def ProtoPirateDefineEnabled(name, app_manifest_path=app_manifest_path):
|
||||
defines_path = os.path.join(os.path.dirname(app_manifest_path), "defines.h")
|
||||
with open(defines_path) as defines_file:
|
||||
for line in defines_file:
|
||||
parts = line.strip().split()
|
||||
if len(parts) >= 2 and parts[0] == "#define" and parts[1] == name:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
_ENABLE_TIMING_TUNER = ProtoPirateDefineEnabled("ENABLE_TIMING_TUNER_SCENE")
|
||||
|
||||
_MAIN_APP_SOURCES = [
|
||||
"*.c*",
|
||||
"!protocols/plugins",
|
||||
"!scenes/plugins",
|
||||
"!protocols",
|
||||
"protocols/protocol_items.c",
|
||||
"protocols/protocols_common.c",
|
||||
"!raw_file_reader.c",
|
||||
]
|
||||
|
||||
if not _ENABLE_TIMING_TUNER:
|
||||
_MAIN_APP_SOURCES += [
|
||||
"!protopirate_scene_timing_tuner.c",
|
||||
"!protocol_timings.c",
|
||||
]
|
||||
|
||||
App(
|
||||
appid="proto_pirate",
|
||||
name="ProtoPirate",
|
||||
@@ -12,39 +44,62 @@ App(
|
||||
fap_category="Sub-GHz",
|
||||
fap_icon_assets="images",
|
||||
fap_file_assets="keystore",
|
||||
cdefines=["PROTOPIRATE_PROTOCOL_RX_ONLY=1"],
|
||||
sources=_MAIN_APP_SOURCES,
|
||||
)
|
||||
|
||||
# --- RX protocol plugins ---
|
||||
|
||||
App(
|
||||
appid="protopirate_am_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="protopirate_am_plugin_ep",
|
||||
requires=["proto_pirate"],
|
||||
cdefines=["PROTOPIRATE_PROTOCOL_RX_ONLY=1"],
|
||||
sources=[
|
||||
"protocols/plugins/protopirate_am_plugin.c",
|
||||
"protocols/protocols_common.c",
|
||||
"protocols/aut64.c",
|
||||
"protocols/chrysler_v0.c",
|
||||
"protocols/fiat_v0.c",
|
||||
"protocols/fiat_v1.c",
|
||||
"protocols/fiat_v2.c",
|
||||
"protocols/ford_v0.c",
|
||||
"protocols/ford_v3.c",
|
||||
"protocols/honda_v1.c",
|
||||
"protocols/kia_v1.c",
|
||||
"protocols/kia_v2.c",
|
||||
"protocols/mazda_v0.c",
|
||||
"protocols/porsche_touareg.c",
|
||||
"protocols/psa.c",
|
||||
"protocols/psa_crypto.c",
|
||||
"protocols/renault_v0.c",
|
||||
"protocols/subaru.c",
|
||||
"protocols/vag.c",
|
||||
"protocols/star_line.c",
|
||||
],
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="protopirate_am_vag_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="protopirate_am_vag_plugin_ep",
|
||||
requires=["proto_pirate"],
|
||||
cdefines=["PROTOPIRATE_PROTOCOL_RX_ONLY=1"],
|
||||
sources=[
|
||||
"protocols/plugins/protopirate_am_vag_plugin.c",
|
||||
"protocols/protocols_common.c",
|
||||
"protocols/aut64.c",
|
||||
"protocols/vag.c",
|
||||
],
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="protopirate_fm_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="protopirate_fm_plugin_ep",
|
||||
requires=["proto_pirate"],
|
||||
cdefines=["PROTOPIRATE_PROTOCOL_RX_ONLY=1"],
|
||||
sources=[
|
||||
"protocols/plugins/protopirate_fm_plugin.c",
|
||||
"protocols/protocols_common.c",
|
||||
@@ -55,11 +110,6 @@ App(
|
||||
"protocols/kia_v5.c",
|
||||
"protocols/kia_v6.c",
|
||||
"protocols/kia_v7.c",
|
||||
"protocols/ford_v1.c",
|
||||
"protocols/ford_v2.c",
|
||||
"protocols/ford_v3.c",
|
||||
"protocols/honda_static.c",
|
||||
"protocols/land_rover_v0.c",
|
||||
"protocols/mazda_v0.c",
|
||||
"protocols/mitsubishi_v0.c",
|
||||
"protocols/psa.c",
|
||||
@@ -68,6 +118,258 @@ App(
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="protopirate_fm_f4_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="protopirate_fm_f4_plugin_ep",
|
||||
requires=["proto_pirate"],
|
||||
cdefines=["PROTOPIRATE_PROTOCOL_RX_ONLY=1"],
|
||||
sources=[
|
||||
"protocols/plugins/protopirate_fm_f4_plugin.c",
|
||||
"protocols/protocols_common.c",
|
||||
"protocols/ford_v1.c",
|
||||
"protocols/ford_v2.c",
|
||||
"protocols/ford_v3.c",
|
||||
"protocols/honda_v2.c",
|
||||
],
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="protopirate_fm_honda1_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="protopirate_fm_honda1_plugin_ep",
|
||||
requires=["proto_pirate"],
|
||||
cdefines=["PROTOPIRATE_PROTOCOL_RX_ONLY=1"],
|
||||
sources=[
|
||||
"protocols/plugins/protopirate_fm_honda1_plugin.c",
|
||||
"protocols/protocols_common.c",
|
||||
"protocols/honda_static.c",
|
||||
],
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
# --- TX protocol plugins ---
|
||||
|
||||
_TX_PLUGIN_SOURCES = [
|
||||
"protocols/plugins/protopirate_tx_protocol_plugin.c",
|
||||
"protocols/protocols_common.c",
|
||||
]
|
||||
|
||||
def ProtoPirateTxProtocolPlugin(
|
||||
key,
|
||||
protocol_header,
|
||||
protocol_name,
|
||||
protocol_item,
|
||||
protocol_sources,
|
||||
App=App,
|
||||
FlipperAppType=FlipperAppType,
|
||||
tx_plugin_sources=_TX_PLUGIN_SOURCES,
|
||||
):
|
||||
App(
|
||||
appid=f"protopirate_tx_{key}_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="protopirate_tx_protocol_plugin_ep",
|
||||
requires=["proto_pirate"],
|
||||
cdefines=[
|
||||
"PROTOPIRATE_PROTOCOL_TX_ONLY=1",
|
||||
f"PP_TX_PROTOCOL_HEADER=\\\"../{protocol_header}\\\"",
|
||||
f"PP_TX_PROTOCOL_NAME={protocol_name}",
|
||||
f"PP_TX_PROTOCOL_ITEM={protocol_item}",
|
||||
],
|
||||
sources=tx_plugin_sources + [f"protocols/{source}" for source in protocol_sources],
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"chrysler_v0",
|
||||
"chrysler_v0.h",
|
||||
"CHRYSLER_PROTOCOL_V0_NAME",
|
||||
"chrysler_protocol_v0",
|
||||
["chrysler_v0.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"fiat_v0",
|
||||
"fiat_v0.h",
|
||||
"FIAT_PROTOCOL_V0_NAME",
|
||||
"fiat_protocol_v0",
|
||||
["fiat_v0.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"fiat_v1",
|
||||
"fiat_v1.h",
|
||||
"FIAT_V1_PROTOCOL_NAME",
|
||||
"fiat_v1_protocol",
|
||||
["fiat_v1.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"ford_v0",
|
||||
"ford_v0.h",
|
||||
"FORD_PROTOCOL_V0_NAME",
|
||||
"ford_protocol_v0",
|
||||
["ford_v0.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"ford_v1",
|
||||
"ford_v1.h",
|
||||
"FORD_PROTOCOL_V1_NAME",
|
||||
"ford_protocol_v1",
|
||||
["ford_v1.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"ford_v2",
|
||||
"ford_v2.h",
|
||||
"FORD_PROTOCOL_V2_NAME",
|
||||
"ford_protocol_v2",
|
||||
["ford_v2.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"honda_static",
|
||||
"honda_static.h",
|
||||
"HONDA_STATIC_PROTOCOL_NAME",
|
||||
"honda_static_protocol",
|
||||
["honda_static.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"honda_v1",
|
||||
"honda_v1.h",
|
||||
"HONDA_V1_PROTOCOL_NAME",
|
||||
"honda_v1_protocol",
|
||||
["honda_v1.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"kia_v0",
|
||||
"kia_v0.h",
|
||||
"KIA_PROTOCOL_V0_NAME",
|
||||
"kia_protocol_v0",
|
||||
["kia_v0.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"kia_v1",
|
||||
"kia_v1.h",
|
||||
"KIA_PROTOCOL_V1_NAME",
|
||||
"kia_protocol_v1",
|
||||
["kia_v1.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"kia_v2",
|
||||
"kia_v2.h",
|
||||
"KIA_PROTOCOL_V2_NAME",
|
||||
"kia_protocol_v2",
|
||||
["kia_v2.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"kia_v3_v4",
|
||||
"kia_v3_v4.h",
|
||||
"KIA_PROTOCOL_V3_V4_NAME",
|
||||
"kia_protocol_v3_v4",
|
||||
["keys.c", "kia_v3_v4.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"kia_v5",
|
||||
"kia_v5.h",
|
||||
"KIA_PROTOCOL_V5_NAME",
|
||||
"kia_protocol_v5",
|
||||
["keys.c", "kia_v5.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"kia_v6",
|
||||
"kia_v6.h",
|
||||
"KIA_PROTOCOL_V6_NAME",
|
||||
"kia_protocol_v6",
|
||||
["keys.c", "kia_v6.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"kia_v7",
|
||||
"kia_v7.h",
|
||||
"KIA_PROTOCOL_V7_NAME",
|
||||
"kia_protocol_v7",
|
||||
["kia_v7.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"honda_v2",
|
||||
"honda_v2.h",
|
||||
"HONDA_V2_PROTOCOL_NAME",
|
||||
"honda_v2_protocol",
|
||||
["honda_v2.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"mazda_v0",
|
||||
"mazda_v0.h",
|
||||
"MAZDA_PROTOCOL_V0_NAME",
|
||||
"mazda_v0_protocol",
|
||||
["mazda_v0.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"psa",
|
||||
"psa.h",
|
||||
"PSA_PROTOCOL_NAME",
|
||||
"psa_protocol",
|
||||
["psa.c", "psa_crypto.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"renault_v0",
|
||||
"renault_v0.h",
|
||||
"RENAULT_PROTOCOL_V0_NAME",
|
||||
"renault_v0_protocol",
|
||||
["renault_v0.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"star_line",
|
||||
"star_line.h",
|
||||
"SUBGHZ_PROTOCOL_STAR_LINE_NAME",
|
||||
"subghz_protocol_star_line",
|
||||
["star_line.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"subaru",
|
||||
"subaru.h",
|
||||
"SUBARU_PROTOCOL_NAME",
|
||||
"subaru_protocol",
|
||||
["subaru.c"],
|
||||
)
|
||||
ProtoPirateTxProtocolPlugin(
|
||||
"vag",
|
||||
"vag.h",
|
||||
"VAG_PROTOCOL_NAME",
|
||||
"vag_protocol",
|
||||
["aut64.c", "vag.c"],
|
||||
)
|
||||
|
||||
# --- Tool / scene plugins ---
|
||||
|
||||
App(
|
||||
appid="protopirate_sub_decode_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="protopirate_sub_decode_plugin_ep",
|
||||
requires=["proto_pirate"],
|
||||
cdefines=["PROTOPIRATE_SUB_DECODE_PLUGIN_BUILD=1"],
|
||||
sources=[
|
||||
"scenes/protopirate_scene_sub_decode.c",
|
||||
"helpers/raw_file_reader.c",
|
||||
"protopirate_history.c",
|
||||
"helpers/protopirate_storage.c",
|
||||
"protocols/protocol_items.c",
|
||||
"protocols/protocols_common.c",
|
||||
],
|
||||
fap_icon_assets="images",
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
if _ENABLE_TIMING_TUNER:
|
||||
App(
|
||||
appid="protopirate_timing_tuner_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="protopirate_timing_tuner_plugin_ep",
|
||||
requires=["proto_pirate"],
|
||||
cdefines=["PROTOPIRATE_TIMING_TUNER_PLUGIN_BUILD=1"],
|
||||
sources=[
|
||||
"scenes/protopirate_scene_timing_tuner.c",
|
||||
"protocols/protocol_timings.c",
|
||||
],
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="protopirate_emulate_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
@@ -75,6 +377,7 @@ App(
|
||||
requires=["proto_pirate"],
|
||||
sources=[
|
||||
"scenes/plugins/protopirate_emulate_plugin.c",
|
||||
"protocols/protocol_items.c",
|
||||
],
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
//#define ENABLE_TIMING_TUNER_SCENE
|
||||
//#define ENABLE_SUB_DECODE_SCENE
|
||||
|
||||
// #define ENABLE_TIMING_TUNER_SCENE
|
||||
#define ENABLE_SUB_DECODE_SCENE
|
||||
#define ENABLE_EMULATE_FEATURE
|
||||
|
||||
#if defined(ENABLE_EMULATE_FEATURE) && !defined(PROTOPIRATE_PROTOCOL_RX_ONLY)
|
||||
#define PROTOPIRATE_WITH_ENCODER 1
|
||||
#else
|
||||
#define PROTOPIRATE_WITH_ENCODER 0
|
||||
#endif
|
||||
|
||||
#ifndef PROTOPIRATE_PROTOCOL_TX_ONLY
|
||||
#define PROTOPIRATE_WITH_DECODER 1
|
||||
#else
|
||||
#define PROTOPIRATE_WITH_DECODER 0
|
||||
#endif
|
||||
|
||||
#define REMOVE_LOGS
|
||||
|
||||
#ifdef REMOVE_LOGS
|
||||
|
||||
@@ -0,0 +1,447 @@
|
||||
#include "../protopirate_app_i.h"
|
||||
#include "protopirate_txrx.h"
|
||||
#include "../protocols/protocol_items.h"
|
||||
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "ProtoPirateProtocolPlugin"
|
||||
#define PROTOPIRATE_TX_PLUGIN_PATH_MAX 160U
|
||||
|
||||
static const char* protopirate_get_registry_plugin_path(ProtoPirateProtocolRegistryRoute route) {
|
||||
switch(route) {
|
||||
case ProtoPirateProtocolRegistryRouteAMVag:
|
||||
return APP_ASSETS_PATH("plugins/protopirate_am_vag_plugin.fal");
|
||||
case ProtoPirateProtocolRegistryRouteFMDefault:
|
||||
return APP_ASSETS_PATH("plugins/protopirate_fm_plugin.fal");
|
||||
case ProtoPirateProtocolRegistryRouteFMF4:
|
||||
return APP_ASSETS_PATH("plugins/protopirate_fm_f4_plugin.fal");
|
||||
case ProtoPirateProtocolRegistryRouteFMHonda1:
|
||||
return APP_ASSETS_PATH("plugins/protopirate_fm_honda1_plugin.fal");
|
||||
case ProtoPirateProtocolRegistryRouteAMDefault:
|
||||
default:
|
||||
return APP_ASSETS_PATH("plugins/protopirate_am_plugin.fal");
|
||||
}
|
||||
}
|
||||
|
||||
static bool protopirate_build_tx_protocol_plugin_path(
|
||||
const char* tx_key,
|
||||
char* plugin_path,
|
||||
size_t plugin_path_size) {
|
||||
if(!tx_key || !plugin_path || plugin_path_size == 0U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int written = snprintf(
|
||||
plugin_path,
|
||||
plugin_path_size,
|
||||
APP_ASSETS_PATH("plugins/protopirate_tx_%s_plugin.fal"),
|
||||
tx_key);
|
||||
return (written > 0) && ((size_t)written < plugin_path_size);
|
||||
}
|
||||
|
||||
static const SubGhzProtocolRegistry protopirate_empty_protocol_registry = {
|
||||
.items = NULL,
|
||||
.size = 0,
|
||||
};
|
||||
|
||||
void protopirate_unload_protocol_plugin(ProtoPirateTxRx* txrx) {
|
||||
furi_check(txrx);
|
||||
|
||||
if(txrx->environment) {
|
||||
subghz_environment_set_protocol_registry(
|
||||
txrx->environment, &protopirate_empty_protocol_registry);
|
||||
}
|
||||
|
||||
txrx->protocol_registry = NULL;
|
||||
|
||||
if(txrx->protocol_plugin && txrx->protocol_plugin->release) {
|
||||
txrx->protocol_plugin->release();
|
||||
}
|
||||
txrx->protocol_plugin = NULL;
|
||||
|
||||
if(txrx->protocol_plugin_manager) {
|
||||
plugin_manager_free(txrx->protocol_plugin_manager);
|
||||
txrx->protocol_plugin_manager = NULL;
|
||||
}
|
||||
|
||||
if(txrx->plugin_resolver) {
|
||||
composite_api_resolver_free(txrx->plugin_resolver);
|
||||
txrx->plugin_resolver = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool protopirate_ensure_protocol_registry_plugin(
|
||||
ProtoPirateApp* app,
|
||||
ProtoPirateProtocolRegistryRoute route,
|
||||
const SubGhzProtocolRegistry** registry) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
furi_check(registry);
|
||||
|
||||
*registry = NULL;
|
||||
|
||||
if(!app->txrx->environment) {
|
||||
FURI_LOG_E(TAG, "Cannot load protocol plugin without radio environment");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_plugin &&
|
||||
app->txrx->protocol_plugin->kind == ProtoPirateProtocolPluginKindRx &&
|
||||
app->txrx->protocol_plugin->registry && app->txrx->protocol_registry_route == route) {
|
||||
*registry = app->txrx->protocol_plugin->registry;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_plugin || app->txrx->protocol_plugin_manager ||
|
||||
app->txrx->plugin_resolver) {
|
||||
protopirate_unload_protocol_plugin(app->txrx);
|
||||
}
|
||||
|
||||
CompositeApiResolver* resolver = composite_api_resolver_alloc();
|
||||
if(!resolver) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate protocol plugin resolver");
|
||||
return false;
|
||||
}
|
||||
composite_api_resolver_add(resolver, firmware_api_interface);
|
||||
|
||||
PluginManager* manager = plugin_manager_alloc(
|
||||
PROTOPIRATE_PROTOCOL_PLUGIN_APP_ID,
|
||||
PROTOPIRATE_PROTOCOL_PLUGIN_API_VERSION,
|
||||
composite_api_resolver_get(resolver));
|
||||
if(!manager) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate protocol plugin manager");
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* plugin_path = protopirate_get_registry_plugin_path(route);
|
||||
PluginManagerError error = plugin_manager_load_single(manager, plugin_path);
|
||||
if(error != PluginManagerErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to load protocol plugin %s: %d", plugin_path, (int)error);
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
const ProtoPirateProtocolPlugin* plugin = plugin_manager_get_ep(manager, 0U);
|
||||
if(!plugin || !plugin->registry) {
|
||||
FURI_LOG_E(TAG, "Protocol plugin entry point is invalid");
|
||||
if(plugin && plugin->release) {
|
||||
plugin->release();
|
||||
}
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(plugin->kind != ProtoPirateProtocolPluginKindRx) {
|
||||
FURI_LOG_E(TAG, "Protocol plugin kind mismatch for RX route");
|
||||
if(plugin->release) {
|
||||
plugin->release();
|
||||
}
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(plugin->route != route) {
|
||||
FURI_LOG_E(
|
||||
TAG, "Protocol plugin route mismatch (expected %d got %d)", route, plugin->route);
|
||||
if(plugin->release) {
|
||||
plugin->release();
|
||||
}
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
app->txrx->plugin_resolver = resolver;
|
||||
app->txrx->protocol_plugin_manager = manager;
|
||||
app->txrx->protocol_plugin = plugin;
|
||||
app->txrx->protocol_registry_route = route;
|
||||
*registry = plugin->registry;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool protopirate_ensure_tx_protocol_plugin(
|
||||
ProtoPirateApp* app,
|
||||
const char* protocol_name,
|
||||
const SubGhzProtocolRegistry** registry) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
furi_check(registry);
|
||||
|
||||
*registry = NULL;
|
||||
|
||||
if(!app->txrx->environment) {
|
||||
FURI_LOG_E(TAG, "Cannot load TX protocol plugin without radio environment");
|
||||
return false;
|
||||
}
|
||||
|
||||
const ProtoPirateProtocolCatalogEntry* catalog_entry =
|
||||
protopirate_protocol_catalog_find(protocol_name);
|
||||
const char* registry_name = protopirate_protocol_catalog_canonical_name(protocol_name);
|
||||
const char* tx_key = protopirate_protocol_catalog_tx_key(protocol_name);
|
||||
char plugin_path[PROTOPIRATE_TX_PLUGIN_PATH_MAX];
|
||||
if(catalog_entry && !tx_key) {
|
||||
FURI_LOG_W(TAG, "TX disabled for %s: protocol catalog has no tx_key", registry_name);
|
||||
return false;
|
||||
}
|
||||
if(!registry_name || !tx_key ||
|
||||
!protopirate_build_tx_protocol_plugin_path(tx_key, plugin_path, sizeof(plugin_path))) {
|
||||
FURI_LOG_E(TAG, "No TX protocol plugin for %s", protocol_name ? protocol_name : "?");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_plugin &&
|
||||
app->txrx->protocol_plugin->kind == ProtoPirateProtocolPluginKindTx &&
|
||||
app->txrx->protocol_plugin->registry && app->txrx->protocol_plugin->protocol_name &&
|
||||
strcmp(app->txrx->protocol_plugin->protocol_name, registry_name) == 0) {
|
||||
*registry = app->txrx->protocol_plugin->registry;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_plugin || app->txrx->protocol_plugin_manager ||
|
||||
app->txrx->plugin_resolver) {
|
||||
protopirate_unload_protocol_plugin(app->txrx);
|
||||
}
|
||||
|
||||
CompositeApiResolver* resolver = composite_api_resolver_alloc();
|
||||
if(!resolver) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate TX protocol plugin resolver");
|
||||
return false;
|
||||
}
|
||||
composite_api_resolver_add(resolver, firmware_api_interface);
|
||||
|
||||
PluginManager* manager = plugin_manager_alloc(
|
||||
PROTOPIRATE_PROTOCOL_PLUGIN_APP_ID,
|
||||
PROTOPIRATE_PROTOCOL_PLUGIN_API_VERSION,
|
||||
composite_api_resolver_get(resolver));
|
||||
if(!manager) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate TX protocol plugin manager");
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginManagerError error = plugin_manager_load_single(manager, plugin_path);
|
||||
if(error != PluginManagerErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to load TX protocol plugin %s: %d", plugin_path, (int)error);
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
const ProtoPirateProtocolPlugin* plugin = plugin_manager_get_ep(manager, 0U);
|
||||
if(!plugin || plugin->kind != ProtoPirateProtocolPluginKindTx || !plugin->registry ||
|
||||
plugin->registry->size == 0U || !plugin->protocol_name ||
|
||||
strcmp(plugin->protocol_name, registry_name) != 0) {
|
||||
FURI_LOG_E(TAG, "TX protocol plugin entry point is invalid for %s", registry_name);
|
||||
if(plugin && plugin->release) {
|
||||
plugin->release();
|
||||
}
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
const SubGhzProtocol* tx_protocol = plugin->registry->items[0];
|
||||
if(!tx_protocol || !tx_protocol->encoder || !tx_protocol->encoder->alloc ||
|
||||
!tx_protocol->encoder->deserialize || !tx_protocol->encoder->yield) {
|
||||
FURI_LOG_E(TAG, "TX protocol plugin for %s has no encoder", registry_name);
|
||||
if(plugin->release) {
|
||||
plugin->release();
|
||||
}
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
app->txrx->plugin_resolver = resolver;
|
||||
app->txrx->protocol_plugin_manager = manager;
|
||||
app->txrx->protocol_plugin = plugin;
|
||||
*registry = plugin->registry;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_refresh_protocol_registry(ProtoPirateApp* app, bool ensure_receiver_ready) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
if(!app->txrx->environment || !app->txrx->preset) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* preset_name = furi_string_get_cstr(app->txrx->preset->name);
|
||||
ProtoPirateProtocolRegistryRoute route = protopirate_get_protocol_registry_route(
|
||||
preset_name,
|
||||
app->txrx->preset->frequency,
|
||||
app->txrx->preset->data,
|
||||
app->txrx->preset->data_size,
|
||||
NULL);
|
||||
bool route_changed = !app->txrx->protocol_plugin ||
|
||||
(app->txrx->protocol_plugin->kind != ProtoPirateProtocolPluginKindRx) ||
|
||||
(app->txrx->protocol_registry_route != route);
|
||||
|
||||
if(route_changed) {
|
||||
protopirate_rx_stack_teardown_for_registry_switch(app);
|
||||
} else if(ensure_receiver_ready && !app->txrx->receiver) {
|
||||
protopirate_rx_stack_teardown_for_registry_switch(app);
|
||||
}
|
||||
|
||||
const SubGhzProtocolRegistry* registry = NULL;
|
||||
if(!protopirate_ensure_protocol_registry_plugin(app, route, ®istry) || !registry) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Failed to resolve %s protocol registry plugin",
|
||||
protopirate_get_protocol_registry_route_name(route));
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool registry_already_bound = (app->txrx->protocol_registry == registry);
|
||||
if(!registry_already_bound) {
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Using %s protocol registry (%zu protocols)",
|
||||
protopirate_get_protocol_registry_route_name(route),
|
||||
registry->size);
|
||||
subghz_environment_set_protocol_registry(app->txrx->environment, registry);
|
||||
app->txrx->protocol_registry = registry;
|
||||
}
|
||||
|
||||
if(!ensure_receiver_ready) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(app->txrx->receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment);
|
||||
if(!app->txrx->receiver) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Failed to allocate receiver for %s registry",
|
||||
protopirate_get_protocol_registry_route_name(route));
|
||||
return false;
|
||||
}
|
||||
|
||||
subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool protopirate_ensure_receiver_allocated(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
if(app->txrx->receiver) {
|
||||
return true;
|
||||
}
|
||||
if(!app->txrx->environment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment);
|
||||
if(!app->txrx->receiver) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate receiver after registry restore");
|
||||
return false;
|
||||
}
|
||||
|
||||
subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_apply_protocol_registry_for_context(
|
||||
ProtoPirateApp* app,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
const uint8_t* preset_data,
|
||||
size_t preset_data_size,
|
||||
const char* protocol_name) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
if(!app->txrx->environment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!preset_name && app->txrx->preset && app->txrx->preset->name) {
|
||||
preset_name = furi_string_get_cstr(app->txrx->preset->name);
|
||||
}
|
||||
if(frequency == 0U && app->txrx->preset) {
|
||||
frequency = app->txrx->preset->frequency;
|
||||
}
|
||||
if((!preset_data || preset_data_size == 0U) && app->txrx->preset) {
|
||||
preset_data = app->txrx->preset->data;
|
||||
preset_data_size = app->txrx->preset->data_size;
|
||||
}
|
||||
|
||||
if(protocol_name && protocol_name[0] != '\0') {
|
||||
const char* registry_name = protopirate_protocol_catalog_canonical_name(protocol_name);
|
||||
if(!registry_name || !protopirate_protocol_catalog_can_tx(protocol_name)) {
|
||||
FURI_LOG_E(TAG, "No TX protocol plugin for %s", protocol_name);
|
||||
return false;
|
||||
}
|
||||
bool tx_changed = !app->txrx->protocol_plugin ||
|
||||
(app->txrx->protocol_plugin->kind != ProtoPirateProtocolPluginKindTx) ||
|
||||
!app->txrx->protocol_plugin->protocol_name ||
|
||||
strcmp(app->txrx->protocol_plugin->protocol_name, registry_name) != 0;
|
||||
|
||||
if(tx_changed) {
|
||||
protopirate_rx_stack_teardown_for_registry_switch(app);
|
||||
}
|
||||
|
||||
const SubGhzProtocolRegistry* tx_registry = NULL;
|
||||
if(!protopirate_ensure_tx_protocol_plugin(app, registry_name, &tx_registry) ||
|
||||
!tx_registry) {
|
||||
FURI_LOG_E(TAG, "Failed to resolve TX protocol plugin for %s", protocol_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_registry == tx_registry) {
|
||||
return true;
|
||||
}
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Switching active protocol registry to TX %s",
|
||||
registry_name ? registry_name : "?");
|
||||
subghz_environment_set_protocol_registry(app->txrx->environment, tx_registry);
|
||||
app->txrx->protocol_registry = tx_registry;
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoPirateProtocolRegistryRoute route = protopirate_get_protocol_registry_route(
|
||||
preset_name, frequency, preset_data, preset_data_size, NULL);
|
||||
|
||||
bool route_changed = !app->txrx->protocol_plugin ||
|
||||
(app->txrx->protocol_plugin->kind != ProtoPirateProtocolPluginKindRx) ||
|
||||
(app->txrx->protocol_registry_route != route);
|
||||
|
||||
if(route_changed) {
|
||||
protopirate_rx_stack_teardown_for_registry_switch(app);
|
||||
}
|
||||
|
||||
const SubGhzProtocolRegistry* registry = NULL;
|
||||
if(!protopirate_ensure_protocol_registry_plugin(app, route, ®istry) || !registry) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Failed to resolve %s registry plugin for preset apply",
|
||||
protopirate_get_protocol_registry_route_name(route));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_registry == registry) {
|
||||
return protopirate_ensure_receiver_allocated(app);
|
||||
}
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Switching active protocol registry to %s (%zu protocols)",
|
||||
protopirate_get_protocol_registry_route_name(route),
|
||||
registry->size);
|
||||
subghz_environment_set_protocol_registry(app->txrx->environment, registry);
|
||||
app->txrx->protocol_registry = registry;
|
||||
return protopirate_ensure_receiver_allocated(app);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct ProtoPirateApp ProtoPirateApp;
|
||||
typedef struct ProtoPirateTxRx ProtoPirateTxRx;
|
||||
|
||||
void protopirate_unload_protocol_plugin(ProtoPirateTxRx* txrx);
|
||||
bool protopirate_refresh_protocol_registry(ProtoPirateApp* app, bool ensure_receiver_ready);
|
||||
bool protopirate_apply_protocol_registry_for_context(
|
||||
ProtoPirateApp* app,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
const uint8_t* preset_data,
|
||||
size_t preset_data_size,
|
||||
const char* protocol_name);
|
||||
@@ -80,15 +80,9 @@ static void host_receiver_info_rebuild_widget(void* app) {
|
||||
protopirate_receiver_info_rebuild_normal_widget(app);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SUB_DECODE_SCENE
|
||||
static void host_subdecode_signal_info_refresh(void* app) {
|
||||
protopirate_subdecode_psa_bf_complete_refresh(app);
|
||||
host_send_custom_event(app, ProtoPirateCustomEventSubDecodeUpdate);
|
||||
}
|
||||
#else
|
||||
static void host_subdecode_signal_info_refresh(void* app) {
|
||||
UNUSED(app);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void host_scene_previous(void* app) {
|
||||
ProtoPirateApp* a = (ProtoPirateApp*)app;
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
#include "protopirate_radio.h"
|
||||
#include "../protopirate_app_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "ProtoPirateRadio"
|
||||
|
||||
static void protopirate_radio_free_receiver(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
if(!app->txrx->receiver) {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Receiver was NULL, skipping free");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Freeing receiver %p", app->txrx->receiver);
|
||||
#endif
|
||||
subghz_receiver_free(app->txrx->receiver);
|
||||
app->txrx->receiver = NULL;
|
||||
}
|
||||
|
||||
static void protopirate_radio_free_environment(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
if(!app->txrx->environment) {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Environment was NULL, skipping free");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Freeing environment %p", app->txrx->environment);
|
||||
#endif
|
||||
subghz_environment_free(app->txrx->environment);
|
||||
app->txrx->environment = NULL;
|
||||
app->txrx->protocol_registry = NULL;
|
||||
}
|
||||
|
||||
static void protopirate_radio_end_device(ProtoPirateApp* app, bool sleep_before_end) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
if(!app->txrx->radio_device) {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Radio device was NULL, skipping sleep/end");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Putting radio device to %s and ending: %p",
|
||||
sleep_before_end ? "sleep" : "idle",
|
||||
app->txrx->radio_device);
|
||||
#endif
|
||||
|
||||
if(sleep_before_end) {
|
||||
subghz_devices_sleep(app->txrx->radio_device);
|
||||
} else {
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
}
|
||||
|
||||
radio_device_loader_end(app->txrx->radio_device);
|
||||
app->txrx->radio_device = NULL;
|
||||
}
|
||||
|
||||
static void protopirate_radio_reset_state(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
app->txrx->protocol_registry = NULL;
|
||||
app->txrx->protocol_plugin = NULL;
|
||||
app->txrx->protocol_registry_route = ProtoPirateProtocolRegistryRouteAMDefault;
|
||||
app->txrx->txrx_state = ProtoPirateTxRxStateIDLE;
|
||||
app->radio_initialized = false;
|
||||
}
|
||||
|
||||
static void protopirate_radio_init_cleanup(ProtoPirateApp* app, bool devices_initialized) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
protopirate_radio_free_receiver(app);
|
||||
protopirate_radio_end_device(app, false);
|
||||
protopirate_radio_free_environment(app);
|
||||
protopirate_unload_protocol_plugin(app->txrx);
|
||||
|
||||
if(devices_initialized) {
|
||||
subghz_devices_deinit();
|
||||
}
|
||||
|
||||
protopirate_radio_reset_state(app);
|
||||
}
|
||||
|
||||
bool protopirate_radio_init(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
FURI_LOG_I(TAG, "=== protopirate_radio_init called ===");
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "State: radio_initialized=%d", app->radio_initialized);
|
||||
#endif
|
||||
|
||||
if(app->radio_initialized) {
|
||||
const bool radio_ready = (app->txrx->environment != NULL) &&
|
||||
(app->txrx->radio_device != NULL);
|
||||
if(radio_ready) {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Radio already initialized, returning true");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
"Radio marked initialized but resources missing (env=%p device=%p), repairing",
|
||||
app->txrx->environment,
|
||||
app->txrx->radio_device);
|
||||
protopirate_radio_deinit(app);
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Fresh radio init - allocating all components");
|
||||
|
||||
app->txrx->environment = subghz_environment_alloc();
|
||||
if(!app->txrx->environment) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate environment!");
|
||||
protopirate_radio_init_cleanup(app, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
app->txrx->protocol_registry = NULL;
|
||||
|
||||
if(!protopirate_refresh_protocol_registry(app, false)) {
|
||||
FURI_LOG_E(TAG, "Failed to configure protocol registry");
|
||||
protopirate_radio_init_cleanup(app, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
subghz_environment_load_keystore(app->txrx->environment, PROTOPIRATE_KEYSTORE_DIR_NAME);
|
||||
|
||||
subghz_devices_init();
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "SubGhz devices initialized");
|
||||
#endif
|
||||
|
||||
app->txrx->radio_device = radio_device_loader_set(NULL, SubGhzRadioDeviceTypeExternalCC1101);
|
||||
|
||||
if(!app->txrx->radio_device) {
|
||||
FURI_LOG_W(TAG, "External CC1101 not found, trying internal radio");
|
||||
app->txrx->radio_device = radio_device_loader_set(NULL, SubGhzRadioDeviceTypeInternal);
|
||||
}
|
||||
|
||||
if(!app->txrx->radio_device) {
|
||||
FURI_LOG_E(TAG, "Failed to initialize any radio device!");
|
||||
protopirate_radio_init_cleanup(app, true);
|
||||
return false;
|
||||
}
|
||||
#ifndef REMOVE_LOGS
|
||||
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)",
|
||||
device_name ? device_name : "unknown",
|
||||
is_external ? "external" : "internal");
|
||||
#endif
|
||||
subghz_devices_reset(app->txrx->radio_device);
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
|
||||
app->radio_initialized = true;
|
||||
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Final state: radio_initialized=%d", app->radio_initialized);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void protopirate_radio_deinit(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
FURI_LOG_I(TAG, "=== protopirate_radio_deinit called ===");
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "State: radio_initialized=%d", app->radio_initialized);
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Pointers: worker=%p, environment=%p, receiver=%p, history=%p, radio_device=%p",
|
||||
app->txrx->worker,
|
||||
app->txrx->environment,
|
||||
app->txrx->receiver,
|
||||
app->txrx->history,
|
||||
app->txrx->radio_device);
|
||||
#endif
|
||||
|
||||
bool has_radio_resources = app->radio_initialized || app->txrx->worker ||
|
||||
app->txrx->environment || app->txrx->receiver ||
|
||||
app->txrx->history || app->txrx->radio_device ||
|
||||
app->txrx->protocol_plugin_manager ||
|
||||
app->txrx->plugin_resolver || app->txrx->protocol_plugin;
|
||||
if(!has_radio_resources) {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Radio resources were not initialized, returning");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
bool devices_initialized = app->radio_initialized || (app->txrx->radio_device != NULL);
|
||||
|
||||
if(app->txrx->worker && app->txrx->txrx_state == ProtoPirateTxRxStateRx) {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Stopping active RX, state=%d", app->txrx->txrx_state);
|
||||
#endif
|
||||
subghz_worker_stop(app->txrx->worker);
|
||||
if(app->txrx->radio_device) {
|
||||
subghz_devices_stop_async_rx(app->txrx->radio_device);
|
||||
}
|
||||
}
|
||||
|
||||
protopirate_radio_end_device(app, true);
|
||||
|
||||
if(devices_initialized) {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Calling subghz_devices_deinit");
|
||||
#endif
|
||||
subghz_devices_deinit();
|
||||
}
|
||||
|
||||
protopirate_radio_free_receiver(app);
|
||||
protopirate_radio_free_environment(app);
|
||||
protopirate_unload_protocol_plugin(app->txrx);
|
||||
|
||||
if(app->txrx->history) {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Freeing history %p", app->txrx->history);
|
||||
#endif
|
||||
protopirate_history_free(app->txrx->history);
|
||||
app->txrx->history = NULL;
|
||||
} else {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "History was NULL, skipping free");
|
||||
#endif
|
||||
}
|
||||
|
||||
if(app->txrx->worker) {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Freeing worker %p", app->txrx->worker);
|
||||
#endif
|
||||
subghz_worker_free(app->txrx->worker);
|
||||
app->txrx->worker = NULL;
|
||||
} else {
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Worker was NULL, skipping free");
|
||||
#endif
|
||||
}
|
||||
|
||||
protopirate_radio_reset_state(app);
|
||||
|
||||
#ifndef REMOVE_LOGS
|
||||
FURI_LOG_D(TAG, "Final state: radio_initialized=%d", app->radio_initialized);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct ProtoPirateApp ProtoPirateApp;
|
||||
|
||||
bool protopirate_radio_init(ProtoPirateApp* app);
|
||||
void protopirate_radio_deinit(ProtoPirateApp* app);
|
||||
@@ -0,0 +1,166 @@
|
||||
#include "protopirate_saved_match.h"
|
||||
#include "protopirate_storage.h"
|
||||
#include "../protocols/protocols_common.h"
|
||||
#include <storage/storage.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "ProtoPirateMatch"
|
||||
#define CNT_MATCH_MARGIN 50
|
||||
|
||||
bool protopirate_saved_match_signal(
|
||||
FlipperFormat* received_ff,
|
||||
FuriString* out_matched_name,
|
||||
FuriString* out_matched_path) {
|
||||
furi_check(received_ff);
|
||||
furi_check(out_matched_name);
|
||||
furi_check(out_matched_path);
|
||||
|
||||
FuriString* rx_protocol = furi_string_alloc();
|
||||
FuriString* rx_key = furi_string_alloc();
|
||||
uint32_t rx_serial = 0;
|
||||
uint32_t rx_cnt = 0;
|
||||
bool rx_has_serial = false;
|
||||
bool rx_has_key = false;
|
||||
bool rx_has_cnt = false;
|
||||
|
||||
flipper_format_rewind(received_ff);
|
||||
if(!flipper_format_read_string(received_ff, FF_PROTOCOL, rx_protocol)) {
|
||||
furi_string_free(rx_protocol);
|
||||
furi_string_free(rx_key);
|
||||
return false;
|
||||
}
|
||||
|
||||
flipper_format_rewind(received_ff);
|
||||
if(flipper_format_read_uint32(received_ff, FF_SERIAL, &rx_serial, 1)) {
|
||||
rx_has_serial = true;
|
||||
}
|
||||
|
||||
if(!rx_has_serial) {
|
||||
flipper_format_rewind(received_ff);
|
||||
if(flipper_format_read_string(received_ff, FF_KEY, rx_key)) {
|
||||
rx_has_key = true;
|
||||
}
|
||||
}
|
||||
|
||||
flipper_format_rewind(received_ff);
|
||||
if(flipper_format_read_uint32(received_ff, FF_CNT, &rx_cnt, 1)) {
|
||||
rx_has_cnt = true;
|
||||
}
|
||||
|
||||
if(!rx_has_serial && !rx_has_key) {
|
||||
FURI_LOG_D(TAG, "No Serial or Key in received signal, skip match");
|
||||
furi_string_free(rx_protocol);
|
||||
furi_string_free(rx_key);
|
||||
return false;
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* dir = storage_file_alloc(storage);
|
||||
|
||||
bool found = false;
|
||||
|
||||
if(!storage_dir_open(dir, PROTOPIRATE_APP_FOLDER)) {
|
||||
FURI_LOG_D(TAG, "Cannot open saved/ folder");
|
||||
storage_file_free(dir);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_string_free(rx_protocol);
|
||||
furi_string_free(rx_key);
|
||||
return false;
|
||||
}
|
||||
|
||||
FileInfo file_info;
|
||||
char file_name[128];
|
||||
|
||||
while(storage_dir_read(dir, &file_info, file_name, sizeof(file_name))) {
|
||||
if(file_info.flags & FSF_DIRECTORY) continue;
|
||||
|
||||
size_t name_len = strlen(file_name);
|
||||
if(name_len < 5) continue;
|
||||
|
||||
if(strcmp(file_name + name_len - 4, PROTOPIRATE_APP_EXTENSION) != 0) continue;
|
||||
|
||||
if(strcmp(file_name, ".temp.psf") == 0) continue;
|
||||
|
||||
FuriString* saved_path =
|
||||
furi_string_alloc_printf("%s/%s", PROTOPIRATE_APP_FOLDER, file_name);
|
||||
|
||||
FlipperFormat* saved_ff = flipper_format_file_alloc(storage);
|
||||
if(!flipper_format_file_open_existing(saved_ff, furi_string_get_cstr(saved_path))) {
|
||||
flipper_format_free(saved_ff);
|
||||
furi_string_free(saved_path);
|
||||
continue;
|
||||
}
|
||||
|
||||
FuriString* saved_protocol = furi_string_alloc();
|
||||
flipper_format_rewind(saved_ff);
|
||||
bool protocol_match = flipper_format_read_string(saved_ff, FF_PROTOCOL, saved_protocol) &&
|
||||
furi_string_cmp(rx_protocol, saved_protocol) == 0;
|
||||
furi_string_free(saved_protocol);
|
||||
|
||||
if(!protocol_match) {
|
||||
flipper_format_free(saved_ff);
|
||||
furi_string_free(saved_path);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool identity_match = false;
|
||||
if(rx_has_serial) {
|
||||
uint32_t saved_serial = 0;
|
||||
flipper_format_rewind(saved_ff);
|
||||
if(flipper_format_read_uint32(saved_ff, FF_SERIAL, &saved_serial, 1)) {
|
||||
identity_match = (saved_serial == rx_serial);
|
||||
}
|
||||
} else {
|
||||
FuriString* saved_key = furi_string_alloc();
|
||||
flipper_format_rewind(saved_ff);
|
||||
if(flipper_format_read_string(saved_ff, FF_KEY, saved_key)) {
|
||||
identity_match = (furi_string_cmp(rx_key, saved_key) == 0);
|
||||
}
|
||||
furi_string_free(saved_key);
|
||||
}
|
||||
|
||||
if(!identity_match) {
|
||||
flipper_format_free(saved_ff);
|
||||
furi_string_free(saved_path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(rx_has_cnt) {
|
||||
uint32_t saved_cnt = 0;
|
||||
flipper_format_rewind(saved_ff);
|
||||
if(flipper_format_read_uint32(saved_ff, FF_CNT, &saved_cnt, 1)) {
|
||||
int64_t diff = (int64_t)rx_cnt - (int64_t)saved_cnt;
|
||||
if(diff < 0) diff = -diff;
|
||||
if(diff > CNT_MATCH_MARGIN) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Cnt diff %lld > %d, skip %s",
|
||||
(long long)diff,
|
||||
CNT_MATCH_MARGIN,
|
||||
file_name);
|
||||
flipper_format_free(saved_ff);
|
||||
furi_string_free(saved_path);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t ext_pos = name_len - 4;
|
||||
furi_string_set_strn(out_matched_name, file_name, ext_pos);
|
||||
furi_string_set(out_matched_path, saved_path);
|
||||
found = true;
|
||||
|
||||
flipper_format_free(saved_ff);
|
||||
furi_string_free(saved_path);
|
||||
break;
|
||||
}
|
||||
|
||||
storage_dir_close(dir);
|
||||
storage_file_free(dir);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
furi_string_free(rx_protocol);
|
||||
furi_string_free(rx_key);
|
||||
|
||||
return found;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
bool protopirate_saved_match_signal(
|
||||
FlipperFormat* received_ff,
|
||||
FuriString* out_matched_name,
|
||||
FuriString* out_matched_path);
|
||||
@@ -1,5 +1,6 @@
|
||||
// helpers/protopirate_settings.c
|
||||
#include "protopirate_settings.h"
|
||||
#include "protopirate_storage.h"
|
||||
#include <storage/storage.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <furi.h>
|
||||
@@ -18,6 +19,7 @@ void protopirate_settings_set_defaults(ProtoPirateSettings* settings) {
|
||||
settings->auto_save = false;
|
||||
settings->hopping_enabled = false;
|
||||
settings->emulate_feature_enabled = false;
|
||||
settings->check_saved = false;
|
||||
}
|
||||
|
||||
void protopirate_settings_load(ProtoPirateSettings* settings) {
|
||||
@@ -42,12 +44,6 @@ void protopirate_settings_load(ProtoPirateSettings* settings) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(version != SETTINGS_FILE_VERSION) {
|
||||
FURI_LOG_W(TAG, "Unsupported settings version %lu", (unsigned long)version);
|
||||
furi_string_free(header);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(header, SETTINGS_FILE_HEADER) != 0) {
|
||||
FURI_LOG_W(TAG, "Invalid settings file header");
|
||||
furi_string_free(header);
|
||||
@@ -56,6 +52,14 @@ void protopirate_settings_load(ProtoPirateSettings* settings) {
|
||||
|
||||
furi_string_free(header);
|
||||
|
||||
if(version != SETTINGS_FILE_VERSION) {
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Migrating settings from version %lu to %u",
|
||||
(unsigned long)version,
|
||||
SETTINGS_FILE_VERSION);
|
||||
}
|
||||
|
||||
// Read frequency
|
||||
if(!flipper_format_read_uint32(ff, FF_FREQUENCY, &settings->frequency, 1)) {
|
||||
FURI_LOG_W(TAG, "Failed to read frequency, using default");
|
||||
@@ -84,6 +88,11 @@ void protopirate_settings_load(ProtoPirateSettings* settings) {
|
||||
FURI_LOG_W(TAG, "Failed to read TXPower, using default");
|
||||
tx_power_temp = 0;
|
||||
}
|
||||
|
||||
if(tx_power_temp > PROTOPIRATE_TX_POWER_MAX_INDEX) {
|
||||
FURI_LOG_W(TAG, "TXPower %lu out of range, clamping", (unsigned long)tx_power_temp);
|
||||
tx_power_temp = PROTOPIRATE_TX_POWER_MAX_INDEX;
|
||||
}
|
||||
settings->tx_power = (uint8_t)tx_power_temp;
|
||||
|
||||
// Read hopping
|
||||
@@ -101,14 +110,21 @@ void protopirate_settings_load(ProtoPirateSettings* settings) {
|
||||
}
|
||||
settings->emulate_feature_enabled = (emulate_temp == 1);
|
||||
|
||||
uint32_t check_saved_temp = 0;
|
||||
if(!flipper_format_read_uint32(ff, "CheckSaved", &check_saved_temp, 1)) {
|
||||
check_saved_temp = 0;
|
||||
}
|
||||
settings->check_saved = (check_saved_temp == 1);
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Settings loaded: freq=%lu, preset=%u, auto_save=%d, hopping=%d, emulate=%d",
|
||||
"Settings loaded: freq=%lu, preset=%u, auto_save=%d, hopping=%d, emulate=%d, check_saved=%d",
|
||||
settings->frequency,
|
||||
settings->preset_index,
|
||||
settings->auto_save,
|
||||
settings->hopping_enabled,
|
||||
settings->emulate_feature_enabled);
|
||||
settings->emulate_feature_enabled,
|
||||
settings->check_saved);
|
||||
|
||||
} while(false);
|
||||
|
||||
@@ -120,12 +136,17 @@ void protopirate_settings_save(ProtoPirateSettings* settings) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
// Ensure directory exists
|
||||
storage_simply_mkdir(storage, PROTOPIRATE_SETTINGS_DIR);
|
||||
if(!storage_simply_mkdir(storage, PROTOPIRATE_SETTINGS_DIR)) {
|
||||
FURI_LOG_W(TAG, "Settings directory could not be created");
|
||||
}
|
||||
|
||||
FlipperFormat* ff = flipper_format_file_alloc(storage);
|
||||
bool write_ok = false;
|
||||
|
||||
const char* tmp_path = PROTOPIRATE_SETTINGS_FILE ".tmp";
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_always(ff, PROTOPIRATE_SETTINGS_FILE)) {
|
||||
if(!flipper_format_file_open_always(ff, tmp_path)) {
|
||||
FURI_LOG_E(TAG, "Failed to open settings file for writing");
|
||||
break;
|
||||
}
|
||||
@@ -170,17 +191,35 @@ void protopirate_settings_save(ProtoPirateSettings* settings) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t check_saved_temp = settings->check_saved ? 1 : 0;
|
||||
if(!flipper_format_write_uint32(ff, "CheckSaved", &check_saved_temp, 1)) {
|
||||
FURI_LOG_E(TAG, "Failed to write check saved");
|
||||
break;
|
||||
}
|
||||
|
||||
write_ok = true;
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Settings saved: freq=%lu, preset=%u, auto_save=%d, hopping=%d, emulate=%d",
|
||||
"Settings saved: freq=%lu, preset=%u, auto_save=%d, hopping=%d, emulate=%d, check_saved=%d",
|
||||
settings->frequency,
|
||||
settings->preset_index,
|
||||
settings->auto_save,
|
||||
settings->hopping_enabled,
|
||||
settings->emulate_feature_enabled);
|
||||
settings->emulate_feature_enabled,
|
||||
settings->check_saved);
|
||||
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(ff);
|
||||
|
||||
if(write_ok) {
|
||||
if(!protopirate_storage_commit_temp_file(storage, tmp_path, PROTOPIRATE_SETTINGS_FILE)) {
|
||||
FURI_LOG_E(TAG, "Failed to commit settings file");
|
||||
}
|
||||
} else if(storage_file_exists(storage, tmp_path)) {
|
||||
storage_simply_remove(storage, tmp_path);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#define PROTOPIRATE_SETTINGS_FILE APP_DATA_PATH("settings.txt")
|
||||
#define PROTOPIRATE_SETTINGS_DIR APP_DATA_PATH()
|
||||
|
||||
#define PROTOPIRATE_TX_POWER_MAX_INDEX 8U
|
||||
|
||||
typedef struct {
|
||||
uint32_t frequency;
|
||||
uint8_t preset_index;
|
||||
@@ -14,6 +16,7 @@ typedef struct {
|
||||
bool auto_save;
|
||||
bool hopping_enabled;
|
||||
bool emulate_feature_enabled;
|
||||
bool check_saved;
|
||||
} ProtoPirateSettings;
|
||||
|
||||
void protopirate_settings_load(ProtoPirateSettings* settings);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
// helpers/protopirate_storage.c
|
||||
#include "protopirate_storage.h"
|
||||
#include "../defines.h"
|
||||
#include "../protocols/protocol_items.h"
|
||||
#include "../protocols/protocols_common.h"
|
||||
#include <lib/flipper_format/flipper_format_i.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "ProtoPirateStorage"
|
||||
|
||||
@@ -12,21 +16,67 @@ bool protopirate_storage_init(void) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void protopirate_storage_wipe_history_cache(void) {
|
||||
bool protopirate_storage_commit_temp_file(
|
||||
Storage* storage,
|
||||
const char* tmp_path,
|
||||
const char* final_path) {
|
||||
furi_check(storage);
|
||||
furi_check(tmp_path);
|
||||
furi_check(final_path);
|
||||
|
||||
FuriString* backup_path = furi_string_alloc();
|
||||
furi_string_printf(backup_path, "%s.bak", final_path);
|
||||
const char* backup_cstr = furi_string_get_cstr(backup_path);
|
||||
|
||||
if(storage_file_exists(storage, backup_cstr)) {
|
||||
storage_simply_remove(storage, backup_cstr);
|
||||
}
|
||||
|
||||
if(storage_file_exists(storage, final_path)) {
|
||||
if(storage_common_rename(storage, final_path, backup_cstr) != FSE_OK) {
|
||||
FURI_LOG_E(TAG, "Failed to stage backup for %s", final_path);
|
||||
furi_string_free(backup_path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(storage_common_rename(storage, tmp_path, final_path) != FSE_OK) {
|
||||
FURI_LOG_E(TAG, "Failed to commit %s", final_path);
|
||||
if(storage_file_exists(storage, backup_cstr)) {
|
||||
if(storage_common_rename(storage, backup_cstr, final_path) != FSE_OK) {
|
||||
FURI_LOG_E(TAG, "Failed to restore backup for %s", final_path);
|
||||
}
|
||||
}
|
||||
if(storage_file_exists(storage, tmp_path)) {
|
||||
storage_simply_remove(storage, tmp_path);
|
||||
}
|
||||
furi_string_free(backup_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(storage_file_exists(storage, backup_cstr)) {
|
||||
storage_simply_remove(storage, backup_cstr);
|
||||
}
|
||||
|
||||
furi_string_free(backup_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void protopirate_storage_remove_history_folder(void) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(storage_dir_exists(storage, PROTOPIRATE_HISTORY_FOLDER)) {
|
||||
storage_simply_remove_recursive(storage, PROTOPIRATE_HISTORY_FOLDER);
|
||||
FURI_LOG_I(TAG, "Wiped history cache");
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
void protopirate_storage_wipe_history_cache(void) {
|
||||
protopirate_storage_remove_history_folder();
|
||||
FURI_LOG_I(TAG, "Wiped history cache");
|
||||
}
|
||||
|
||||
void protopirate_storage_purge_temp_history_at_startup(void) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(storage_dir_exists(storage, PROTOPIRATE_HISTORY_FOLDER)) {
|
||||
storage_simply_remove_recursive(storage, PROTOPIRATE_HISTORY_FOLDER);
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
protopirate_storage_remove_history_folder();
|
||||
}
|
||||
|
||||
bool protopirate_storage_ensure_history_folder(void) {
|
||||
@@ -34,8 +84,8 @@ bool protopirate_storage_ensure_history_folder(void) {
|
||||
return false;
|
||||
}
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_simply_mkdir(storage, PROTOPIRATE_CACHE_FOLDER);
|
||||
bool ok = storage_simply_mkdir(storage, PROTOPIRATE_HISTORY_FOLDER);
|
||||
bool ok = storage_simply_mkdir(storage, PROTOPIRATE_CACHE_FOLDER) &&
|
||||
storage_simply_mkdir(storage, PROTOPIRATE_HISTORY_FOLDER);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return ok;
|
||||
}
|
||||
@@ -122,6 +172,33 @@ bool protopirate_storage_get_next_filename(const char* protocol_name, FuriString
|
||||
return found;
|
||||
}
|
||||
|
||||
bool protopirate_storage_get_capture_display_protocol(
|
||||
FlipperFormat* flipper_format,
|
||||
FuriString* protocol_name) {
|
||||
furi_check(flipper_format);
|
||||
furi_check(protocol_name);
|
||||
|
||||
FuriString* raw_protocol = furi_string_alloc();
|
||||
bool have_protocol = false;
|
||||
uint32_t protocol_type = 0U;
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
have_protocol = flipper_format_read_string(flipper_format, FF_PROTOCOL, raw_protocol);
|
||||
if(!have_protocol) {
|
||||
furi_string_set(raw_protocol, "Unknown");
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_read_uint32(flipper_format, FF_TYPE, &protocol_type, 1);
|
||||
|
||||
const char* display_name = protopirate_protocol_catalog_display_name(
|
||||
furi_string_get_cstr(raw_protocol), protocol_type);
|
||||
furi_string_set(protocol_name, display_name ? display_name : "Unknown");
|
||||
|
||||
furi_string_free(raw_protocol);
|
||||
return have_protocol;
|
||||
}
|
||||
|
||||
static const char* const protopirate_storage_base_u32_fields[] = {
|
||||
"TE",
|
||||
FF_SERIAL,
|
||||
@@ -141,6 +218,7 @@ static const char* const protopirate_storage_tail_u32_fields[] = {
|
||||
"DataHi",
|
||||
"DataLo",
|
||||
"RawCnt",
|
||||
"Rolling",
|
||||
"Encrypted",
|
||||
"Decrypted",
|
||||
"KIAVersion",
|
||||
@@ -161,6 +239,89 @@ static bool
|
||||
return flipper_format_get_value_count(flipper_format, key, count) && (*count > 0);
|
||||
}
|
||||
|
||||
static bool protopirate_storage_stream_read_char(Stream* stream, char* out) {
|
||||
uint8_t value = 0;
|
||||
if(stream_read(stream, &value, 1U) != 1U) return false;
|
||||
*out = (char)value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool protopirate_storage_stream_write_char(Stream* stream, char value) {
|
||||
return stream_write_char(stream, value) == 1U;
|
||||
}
|
||||
|
||||
static bool protopirate_storage_copy_raw_value_line(
|
||||
Stream* out_stream,
|
||||
Stream* in_stream,
|
||||
const char* key) {
|
||||
const size_t key_len = strlen(key);
|
||||
if(!key_len || !stream_rewind(in_stream) || !stream_seek(out_stream, 0, StreamOffsetFromEnd)) {
|
||||
return protopirate_storage_fail("Stream", key);
|
||||
}
|
||||
|
||||
bool copied = false;
|
||||
|
||||
while(!stream_eof(in_stream)) {
|
||||
bool line_match = true;
|
||||
bool line_ended = false;
|
||||
|
||||
for(size_t i = 0; i < key_len; i++) {
|
||||
char c = '\0';
|
||||
if(!protopirate_storage_stream_read_char(in_stream, &c)) {
|
||||
return protopirate_storage_fail("Read", key);
|
||||
}
|
||||
if(c == '\n') {
|
||||
line_match = false;
|
||||
line_ended = true;
|
||||
break;
|
||||
}
|
||||
if(c != key[i]) {
|
||||
line_match = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(line_ended) continue;
|
||||
|
||||
char c = '\0';
|
||||
if(!protopirate_storage_stream_read_char(in_stream, &c)) {
|
||||
return protopirate_storage_fail("Read", key);
|
||||
}
|
||||
|
||||
if(c != ':') {
|
||||
line_match = false;
|
||||
}
|
||||
|
||||
if(line_match) {
|
||||
if(stream_write(out_stream, (const uint8_t*)key, key_len) != key_len ||
|
||||
!protopirate_storage_stream_write_char(out_stream, ':')) {
|
||||
return protopirate_storage_fail("Write", key);
|
||||
}
|
||||
|
||||
bool wrote_newline = false;
|
||||
while(protopirate_storage_stream_read_char(in_stream, &c)) {
|
||||
if(!protopirate_storage_stream_write_char(out_stream, c)) {
|
||||
return protopirate_storage_fail("Write", key);
|
||||
}
|
||||
if(c == '\n') {
|
||||
wrote_newline = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!wrote_newline && !protopirate_storage_stream_write_char(out_stream, '\n')) {
|
||||
return protopirate_storage_fail("Write", key);
|
||||
}
|
||||
copied = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
while(c != '\n' && protopirate_storage_stream_read_char(in_stream, &c)) {
|
||||
}
|
||||
}
|
||||
|
||||
return copied ? true : protopirate_storage_fail("Read", key);
|
||||
}
|
||||
|
||||
static bool protopirate_storage_copy_string_optional(
|
||||
FlipperFormat* save_file,
|
||||
FlipperFormat* flipper_format,
|
||||
@@ -253,29 +414,19 @@ static bool protopirate_storage_copy_u32_array(
|
||||
const char* key,
|
||||
uint32_t count,
|
||||
uint32_t max_count) {
|
||||
if(count >= max_count) {
|
||||
if(count > max_count) {
|
||||
FURI_LOG_E(TAG, "%s too large: %lu", key, (unsigned long)count);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t* data = malloc(sizeof(uint32_t) * count);
|
||||
if(!data) {
|
||||
FURI_LOG_E(TAG, "Malloc failed: %s (%lu u32)", key, (unsigned long)count);
|
||||
Stream* in_stream = flipper_format_get_raw_stream(flipper_format);
|
||||
Stream* out_stream = flipper_format_get_raw_stream(save_file);
|
||||
if(!in_stream || !out_stream) {
|
||||
FURI_LOG_E(TAG, "Raw stream missing: %s", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool status = false;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_read_uint32(flipper_format, key, data, count)) {
|
||||
protopirate_storage_fail("Read", key);
|
||||
} else if(!flipper_format_write_uint32(save_file, key, data, count)) {
|
||||
protopirate_storage_fail("Write", key);
|
||||
} else {
|
||||
status = true;
|
||||
}
|
||||
|
||||
free(data);
|
||||
return status;
|
||||
return protopirate_storage_copy_raw_value_line(out_stream, in_stream, key);
|
||||
}
|
||||
|
||||
static bool protopirate_storage_copy_u32_array_if_present(
|
||||
@@ -299,29 +450,19 @@ static bool protopirate_storage_copy_hex_array_if_present(
|
||||
if(!protopirate_storage_get_count(flipper_format, key, &count)) {
|
||||
return true;
|
||||
}
|
||||
if(count >= max_count) {
|
||||
if(count > max_count) {
|
||||
FURI_LOG_E(TAG, "%s too large: %lu", key, (unsigned long)count);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* data = malloc(count);
|
||||
if(!data) {
|
||||
FURI_LOG_E(TAG, "Malloc failed: %s (%lu bytes)", key, (unsigned long)count);
|
||||
Stream* in_stream = flipper_format_get_raw_stream(flipper_format);
|
||||
Stream* out_stream = flipper_format_get_raw_stream(save_file);
|
||||
if(!in_stream || !out_stream) {
|
||||
FURI_LOG_E(TAG, "Raw stream missing: %s", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool status = false;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_read_hex(flipper_format, key, data, count)) {
|
||||
protopirate_storage_fail("Read", key);
|
||||
} else if(!flipper_format_write_hex(save_file, key, data, count)) {
|
||||
protopirate_storage_fail("Write", key);
|
||||
} else {
|
||||
status = true;
|
||||
}
|
||||
|
||||
free(data);
|
||||
return status;
|
||||
return protopirate_storage_copy_raw_value_line(out_stream, in_stream, key);
|
||||
}
|
||||
|
||||
static bool protopirate_storage_copy_key(
|
||||
@@ -407,7 +548,7 @@ static bool protopirate_storage_write_capture_data(
|
||||
protopirate_storage_base_u32_fields,
|
||||
COUNT_OF(protopirate_storage_base_u32_fields)))
|
||||
break;
|
||||
if(!protopirate_storage_copy_hex_fixed(save_file, flipper_format, "Key2", 8, NULL)) break;
|
||||
if(!protopirate_storage_copy_hex_or_u32(save_file, flipper_format, "Key2", 4)) break;
|
||||
if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "KeyIdx")) break;
|
||||
if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "Seed")) break;
|
||||
if(!protopirate_storage_copy_hex_or_u32(save_file, flipper_format, "ValidationField", 2))
|
||||
@@ -418,6 +559,9 @@ static bool protopirate_storage_write_capture_data(
|
||||
if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "Fx")) break;
|
||||
if(!protopirate_storage_copy_hex_fixed(save_file, flipper_format, "Key1", 8, NULL)) break;
|
||||
if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "Check")) break;
|
||||
if(!protopirate_storage_copy_hex_fixed(save_file, flipper_format, "Hitag2 Key", 6, NULL))
|
||||
break;
|
||||
if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "Hitag2 Epoch")) break;
|
||||
if(!protopirate_storage_copy_u32_array_if_present(
|
||||
save_file, flipper_format, "RAW_Data", 4096))
|
||||
break;
|
||||
@@ -438,6 +582,73 @@ static bool protopirate_storage_write_capture_data(
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool protopirate_storage_write_capture_file(
|
||||
Storage* storage,
|
||||
FlipperFormat* flipper_format,
|
||||
const char* path) {
|
||||
FlipperFormat* save_file = flipper_format_file_alloc(storage);
|
||||
bool ok = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_new(save_file, path)) {
|
||||
FURI_LOG_E(TAG, "Failed to create file: %s", path);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_header_cstr(
|
||||
save_file, "Flipper SubGhz Key File", PROTOPIRATE_APP_FILE_VERSION)) {
|
||||
FURI_LOG_E(TAG, "Failed to write header");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!protopirate_storage_write_capture_data(save_file, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Failed to write capture data");
|
||||
break;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(save_file);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool protopirate_storage_save_capture_atomic(
|
||||
Storage* storage,
|
||||
FlipperFormat* flipper_format,
|
||||
const char* full_path) {
|
||||
FuriString* tmp_path = furi_string_alloc();
|
||||
furi_check(tmp_path);
|
||||
furi_string_printf(tmp_path, "%s.tmp", full_path);
|
||||
const char* tmp_cstr = furi_string_get_cstr(tmp_path);
|
||||
bool ok = false;
|
||||
bool write_ok = false;
|
||||
|
||||
do {
|
||||
if(storage_file_exists(storage, tmp_cstr)) {
|
||||
storage_simply_remove(storage, tmp_cstr);
|
||||
}
|
||||
|
||||
if(!protopirate_storage_write_capture_file(storage, flipper_format, tmp_cstr)) {
|
||||
break;
|
||||
}
|
||||
write_ok = true;
|
||||
|
||||
if(!protopirate_storage_commit_temp_file(storage, tmp_cstr, full_path)) {
|
||||
break;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
} while(false);
|
||||
|
||||
if(!write_ok && storage_file_exists(storage, tmp_cstr)) {
|
||||
storage_simply_remove(storage, tmp_cstr);
|
||||
}
|
||||
|
||||
furi_string_free(tmp_path);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool protopirate_storage_save_capture_to_path(FlipperFormat* flipper_format, const char* full_path) {
|
||||
furi_check(flipper_format);
|
||||
furi_check(full_path);
|
||||
@@ -448,37 +659,12 @@ bool protopirate_storage_save_capture_to_path(FlipperFormat* flipper_format, con
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* save_file = flipper_format_file_alloc(storage);
|
||||
bool result = false;
|
||||
|
||||
do {
|
||||
// Remove if it already exists (overwrite)
|
||||
if(storage_file_exists(storage, full_path)) {
|
||||
storage_simply_remove(storage, full_path);
|
||||
}
|
||||
|
||||
if(!flipper_format_file_open_new(save_file, full_path)) {
|
||||
FURI_LOG_E(TAG, "Failed to create file: %s", full_path);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_header_cstr(save_file, "Flipper SubGhz Key File", 1)) {
|
||||
FURI_LOG_E(TAG, "Failed to write header");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!protopirate_storage_write_capture_data(save_file, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Failed to write capture data");
|
||||
break;
|
||||
}
|
||||
|
||||
result = true;
|
||||
FURI_LOG_I(TAG, "Saved capture to %s", full_path);
|
||||
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(save_file);
|
||||
bool result = protopirate_storage_save_capture_atomic(storage, flipper_format, full_path);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "Saved capture to %s", full_path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -513,35 +699,16 @@ bool protopirate_storage_save_capture(
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* save_file = flipper_format_file_alloc(storage);
|
||||
bool result = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_new(save_file, furi_string_get_cstr(file_path))) {
|
||||
FURI_LOG_E(TAG, "Failed to create file");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_header_cstr(save_file, "Flipper SubGhz Key File", 1)) {
|
||||
FURI_LOG_E(TAG, "Failed to write header");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!protopirate_storage_write_capture_data(save_file, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Failed to write capture data");
|
||||
break;
|
||||
}
|
||||
|
||||
if(out_path) furi_string_set(out_path, file_path);
|
||||
|
||||
result = true;
|
||||
FURI_LOG_I(TAG, "Saved capture to %s", furi_string_get_cstr(file_path));
|
||||
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(save_file);
|
||||
furi_string_free(file_path);
|
||||
bool result = protopirate_storage_save_capture_atomic(
|
||||
storage, flipper_format, furi_string_get_cstr(file_path));
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
if(result) {
|
||||
if(out_path) furi_string_set(out_path, file_path);
|
||||
FURI_LOG_I(TAG, "Saved capture to %s", furi_string_get_cstr(file_path));
|
||||
}
|
||||
|
||||
furi_string_free(file_path);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
// Initialize storage (create folder if needed)
|
||||
bool protopirate_storage_init(void);
|
||||
|
||||
bool protopirate_storage_commit_temp_file(
|
||||
Storage* storage,
|
||||
const char* tmp_path,
|
||||
const char* final_path);
|
||||
|
||||
// Save a capture to a new file (auto-generated name)
|
||||
bool protopirate_storage_save_capture(
|
||||
FlipperFormat* flipper_format,
|
||||
@@ -24,27 +29,19 @@ bool protopirate_storage_save_capture(
|
||||
// Save a capture to a specific file path (user-chosen name)
|
||||
bool protopirate_storage_save_capture_to_path(FlipperFormat* flipper_format, const char* full_path);
|
||||
|
||||
// Save to temp file for emulation
|
||||
bool protopirate_storage_save_temp(FlipperFormat* flipper_format);
|
||||
|
||||
// Delete temp file
|
||||
void protopirate_storage_delete_temp(void);
|
||||
|
||||
// Get next available filename for a protocol
|
||||
bool protopirate_storage_get_next_filename(const char* protocol_name, FuriString* out_filename);
|
||||
|
||||
bool protopirate_storage_get_capture_display_protocol(
|
||||
FlipperFormat* flipper_format,
|
||||
FuriString* protocol_name);
|
||||
|
||||
// Delete a file
|
||||
bool protopirate_storage_delete_file(const char* file_path);
|
||||
|
||||
// Load a file (caller must close with protopirate_storage_close_file)
|
||||
FlipperFormat* protopirate_storage_load_file(const char* file_path);
|
||||
|
||||
// Close a loaded file (by protopirate_storage_load_file only)
|
||||
void protopirate_storage_close_file(FlipperFormat* flipper_format);
|
||||
|
||||
// Check if file exists
|
||||
bool protopirate_storage_file_exists(const char* file_path);
|
||||
|
||||
bool protopirate_storage_ensure_history_folder(void);
|
||||
|
||||
void protopirate_storage_purge_temp_history_at_startup(void);
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
#include "../protopirate_app_i.h"
|
||||
#include "protopirate_psa_bf_host.h"
|
||||
#include "radio_device_loader.h"
|
||||
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#define TAG "ProtoPirateToolScene"
|
||||
|
||||
#define SUB_DECODE_PLUGIN_PATH APP_ASSETS_PATH("plugins/protopirate_sub_decode_plugin.fal")
|
||||
#ifdef ENABLE_TIMING_TUNER_SCENE
|
||||
#define TIMING_TUNER_PLUGIN_PATH APP_ASSETS_PATH("plugins/protopirate_timing_tuner_plugin.fal")
|
||||
#endif
|
||||
|
||||
static const char*
|
||||
protopirate_tool_scene_plugin_path(ProtoPirateToolScenePluginKind kind) {
|
||||
switch(kind) {
|
||||
case ProtoPirateToolScenePluginKindSubDecode:
|
||||
return SUB_DECODE_PLUGIN_PATH;
|
||||
#ifdef ENABLE_TIMING_TUNER_SCENE
|
||||
case ProtoPirateToolScenePluginKindTimingTuner:
|
||||
return TIMING_TUNER_PLUGIN_PATH;
|
||||
#endif
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool host_ensure_receiver_view(void* app) {
|
||||
return protopirate_ensure_receiver_view((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static bool host_ensure_widget(void* app) {
|
||||
return protopirate_ensure_widget((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static bool host_ensure_view_about(void* app) {
|
||||
return protopirate_ensure_view_about((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static bool host_radio_init(void* app) {
|
||||
return protopirate_radio_init((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static void host_rx_stack_resume_after_tx(void* app) {
|
||||
protopirate_rx_stack_resume_after_tx((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static void host_preset_init(
|
||||
void* app,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint8_t* preset_data,
|
||||
size_t preset_data_size) {
|
||||
protopirate_preset_init(app, preset_name, frequency, preset_data, preset_data_size);
|
||||
}
|
||||
|
||||
static bool host_refresh_protocol_registry(void* app, bool ensure_receiver_ready) {
|
||||
return protopirate_refresh_protocol_registry((ProtoPirateApp*)app, ensure_receiver_ready);
|
||||
}
|
||||
|
||||
static bool host_apply_protocol_registry_for_context(
|
||||
void* app,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
const uint8_t* preset_data,
|
||||
size_t preset_data_size,
|
||||
const char* protocol_name) {
|
||||
return protopirate_apply_protocol_registry_for_context(
|
||||
(ProtoPirateApp*)app,
|
||||
preset_name,
|
||||
frequency,
|
||||
preset_data,
|
||||
preset_data_size,
|
||||
protocol_name);
|
||||
}
|
||||
|
||||
static void host_begin(void* app, uint8_t* preset_data) {
|
||||
protopirate_begin((ProtoPirateApp*)app, preset_data);
|
||||
}
|
||||
|
||||
static uint32_t host_rx(void* app, uint32_t frequency) {
|
||||
return protopirate_rx((ProtoPirateApp*)app, frequency);
|
||||
}
|
||||
|
||||
static void host_rx_end(void* app) {
|
||||
protopirate_rx_end((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static void host_get_frequency_modulation_str(
|
||||
void* app,
|
||||
char* frequency,
|
||||
size_t frequency_size,
|
||||
char* modulation,
|
||||
size_t modulation_size) {
|
||||
protopirate_get_frequency_modulation_str(
|
||||
(ProtoPirateApp*)app, frequency, frequency_size, modulation, modulation_size);
|
||||
}
|
||||
|
||||
static bool host_psa_bf_plugin_ensure_loaded(void* app) {
|
||||
return protopirate_psa_bf_plugin_ensure_loaded((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static void host_psa_bf_context_release(void* app) {
|
||||
protopirate_psa_bf_context_release((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static const ProtoPirateToolSceneHostApi protopirate_tool_scene_host_api = {
|
||||
.ensure_receiver_view = host_ensure_receiver_view,
|
||||
.ensure_widget = host_ensure_widget,
|
||||
.ensure_view_about = host_ensure_view_about,
|
||||
.radio_init = host_radio_init,
|
||||
.rx_stack_resume_after_tx = host_rx_stack_resume_after_tx,
|
||||
.preset_init = host_preset_init,
|
||||
.refresh_protocol_registry = host_refresh_protocol_registry,
|
||||
.apply_protocol_registry_for_context = host_apply_protocol_registry_for_context,
|
||||
.begin = host_begin,
|
||||
.rx = host_rx,
|
||||
.rx_end = host_rx_end,
|
||||
.get_frequency_modulation_str = host_get_frequency_modulation_str,
|
||||
.history_release_scratch = protopirate_history_release_scratch,
|
||||
.radio_device_is_external = radio_device_loader_is_external,
|
||||
.receiver_add_data_statusbar = protopirate_view_receiver_add_data_statusbar,
|
||||
.receiver_get_idx_menu = protopirate_view_receiver_get_idx_menu,
|
||||
.receiver_set_idx_menu = protopirate_view_receiver_set_idx_menu,
|
||||
.receiver_set_callback = protopirate_view_receiver_set_callback,
|
||||
.receiver_set_sub_decode_mode = protopirate_view_receiver_set_sub_decode_mode,
|
||||
.receiver_set_sub_decode_progress = protopirate_view_receiver_set_sub_decode_progress,
|
||||
.receiver_reset_menu = protopirate_view_receiver_reset_menu,
|
||||
.receiver_sync_menu_from_history = protopirate_view_receiver_sync_menu_from_history,
|
||||
.psa_bf_plugin_ensure_loaded = host_psa_bf_plugin_ensure_loaded,
|
||||
.psa_bf_context_release = host_psa_bf_context_release,
|
||||
};
|
||||
|
||||
static void protopirate_tool_scene_plugin_unload(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
|
||||
app->tool_scene_plugin = NULL;
|
||||
|
||||
if(app->tool_scene_plugin_manager) {
|
||||
plugin_manager_free(app->tool_scene_plugin_manager);
|
||||
app->tool_scene_plugin_manager = NULL;
|
||||
}
|
||||
|
||||
if(app->tool_scene_plugin_resolver) {
|
||||
composite_api_resolver_free(app->tool_scene_plugin_resolver);
|
||||
app->tool_scene_plugin_resolver = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool protopirate_tool_scene_plugin_ensure_loaded(
|
||||
ProtoPirateApp* app,
|
||||
ProtoPirateToolScenePluginKind kind) {
|
||||
furi_check(app);
|
||||
|
||||
if(app->tool_scene_plugin && app->tool_scene_plugin->kind == kind) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(app->tool_scene_plugin) {
|
||||
if(app->tool_scene_plugin->release) {
|
||||
app->tool_scene_plugin->release(app);
|
||||
}
|
||||
protopirate_tool_scene_plugin_unload(app);
|
||||
}
|
||||
|
||||
const char* plugin_path = protopirate_tool_scene_plugin_path(kind);
|
||||
if(!plugin_path) {
|
||||
FURI_LOG_E(TAG, "No tool scene plugin path for kind %d", (int)kind);
|
||||
return false;
|
||||
}
|
||||
|
||||
CompositeApiResolver* resolver = composite_api_resolver_alloc();
|
||||
if(!resolver) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate tool scene resolver");
|
||||
return false;
|
||||
}
|
||||
composite_api_resolver_add(resolver, firmware_api_interface);
|
||||
|
||||
PluginManager* manager = plugin_manager_alloc(
|
||||
PROTOPIRATE_TOOL_SCENE_PLUGIN_APP_ID,
|
||||
PROTOPIRATE_TOOL_SCENE_PLUGIN_API_VERSION,
|
||||
composite_api_resolver_get(resolver));
|
||||
if(!manager) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate tool scene plugin manager");
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginManagerError error = plugin_manager_load_single(manager, plugin_path);
|
||||
if(error != PluginManagerErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to load tool scene plugin %s: %d", plugin_path, (int)error);
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
const ProtoPirateToolScenePlugin* plugin = plugin_manager_get_ep(manager, 0U);
|
||||
if(!plugin || plugin->kind != kind || !plugin->set_host_api || !plugin->on_enter ||
|
||||
!plugin->on_event || !plugin->on_exit) {
|
||||
FURI_LOG_E(TAG, "Tool scene plugin entry point is invalid for kind %d", (int)kind);
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
app->tool_scene_plugin_resolver = resolver;
|
||||
app->tool_scene_plugin_manager = manager;
|
||||
app->tool_scene_plugin = plugin;
|
||||
app->tool_scene_plugin_kind = kind;
|
||||
plugin->set_host_api(&protopirate_tool_scene_host_api);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void protopirate_tool_scene_apply_pending_nav(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
|
||||
const uint8_t nav = app->tool_scene_nav_pending;
|
||||
if(nav == TOOL_SCENE_NAV_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t target = app->tool_scene_nav_target;
|
||||
app->tool_scene_nav_pending = TOOL_SCENE_NAV_NONE;
|
||||
app->tool_scene_nav_target = 0;
|
||||
|
||||
switch(nav) {
|
||||
case TOOL_SCENE_NAV_POP:
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
break;
|
||||
case TOOL_SCENE_NAV_NEXT:
|
||||
scene_manager_next_scene(app->scene_manager, target);
|
||||
break;
|
||||
case TOOL_SCENE_NAV_SEARCH_PREVIOUS:
|
||||
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, target);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool protopirate_tool_scene_on_enter(void* context, ProtoPirateToolScenePluginKind kind) {
|
||||
ProtoPirateApp* app = context;
|
||||
furi_check(app);
|
||||
|
||||
app->tool_scene_nav_pending = TOOL_SCENE_NAV_NONE;
|
||||
app->tool_scene_nav_target = 0;
|
||||
|
||||
if(!protopirate_tool_scene_plugin_ensure_loaded(app, kind) || !app->tool_scene_plugin) {
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
return false;
|
||||
}
|
||||
|
||||
app->tool_scene_plugin->on_enter(app);
|
||||
protopirate_tool_scene_apply_pending_nav(app);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_tool_scene_on_event(void* context, SceneManagerEvent event) {
|
||||
ProtoPirateApp* app = context;
|
||||
if(!app || !app->tool_scene_plugin || !app->tool_scene_plugin->on_event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool consumed = app->tool_scene_plugin->on_event(app, event);
|
||||
protopirate_tool_scene_apply_pending_nav(app);
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void protopirate_tool_scene_on_exit(void* context) {
|
||||
ProtoPirateApp* app = context;
|
||||
if(!app) return;
|
||||
|
||||
if(app->tool_scene_plugin) {
|
||||
if(app->tool_scene_plugin->on_exit) {
|
||||
app->tool_scene_plugin->on_exit(app);
|
||||
}
|
||||
if(app->tool_scene_plugin->release) {
|
||||
app->tool_scene_plugin->release(app);
|
||||
}
|
||||
}
|
||||
|
||||
protopirate_tool_scene_plugin_unload(app);
|
||||
}
|
||||
|
||||
void protopirate_tool_scene_plugin_release(ProtoPirateApp* app) {
|
||||
if(!app) return;
|
||||
|
||||
if(app->tool_scene_plugin && app->tool_scene_plugin->release) {
|
||||
app->tool_scene_plugin->release(app);
|
||||
}
|
||||
protopirate_tool_scene_plugin_unload(app);
|
||||
}
|
||||
+26
-217
@@ -1,35 +1,14 @@
|
||||
// protopirate_app_i.c
|
||||
#include "protopirate_app_i.h"
|
||||
#include "protocols/protocol_items.h"
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
#include "protopirate_txrx.h"
|
||||
#include "../protopirate_app_i.h"
|
||||
#include "protopirate_protocol_plugin_host.h"
|
||||
#include "protopirate_radio.h"
|
||||
#include "protopirate_views.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define TAG "ProtoPirateTxRx"
|
||||
|
||||
static const char* protopirate_get_registry_plugin_path(ProtoPirateProtocolRegistryFilter filter) {
|
||||
return (filter == ProtoPirateProtocolRegistryFilterFM) ?
|
||||
APP_ASSETS_PATH("plugins/protopirate_fm_plugin.fal") :
|
||||
APP_ASSETS_PATH("plugins/protopirate_am_plugin.fal");
|
||||
}
|
||||
|
||||
static void protopirate_unload_protocol_plugin(ProtoPirateTxRx* txrx) {
|
||||
furi_check(txrx);
|
||||
|
||||
txrx->protocol_plugin = NULL;
|
||||
txrx->protocol_registry = NULL;
|
||||
|
||||
if(txrx->protocol_plugin_manager) {
|
||||
plugin_manager_free(txrx->protocol_plugin_manager);
|
||||
txrx->protocol_plugin_manager = NULL;
|
||||
}
|
||||
|
||||
if(txrx->plugin_resolver) {
|
||||
composite_api_resolver_free(txrx->plugin_resolver);
|
||||
txrx->plugin_resolver = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void protopirate_teardown_receiver_stack_for_registry_switch(ProtoPirateApp* app) {
|
||||
void protopirate_rx_stack_teardown_for_registry_switch(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
@@ -57,186 +36,6 @@ static void protopirate_teardown_receiver_stack_for_registry_switch(ProtoPirateA
|
||||
}
|
||||
}
|
||||
|
||||
static bool protopirate_ensure_protocol_registry_plugin(
|
||||
ProtoPirateApp* app,
|
||||
ProtoPirateProtocolRegistryFilter filter,
|
||||
const SubGhzProtocolRegistry** registry) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
furi_check(registry);
|
||||
|
||||
*registry = NULL;
|
||||
|
||||
if(!app->txrx->environment) {
|
||||
FURI_LOG_E(TAG, "Cannot load protocol plugin without radio environment");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_plugin && app->txrx->protocol_plugin->registry &&
|
||||
app->txrx->protocol_registry_filter == filter) {
|
||||
*registry = app->txrx->protocol_plugin->registry;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_plugin || app->txrx->protocol_plugin_manager ||
|
||||
app->txrx->plugin_resolver) {
|
||||
protopirate_unload_protocol_plugin(app->txrx);
|
||||
}
|
||||
|
||||
CompositeApiResolver* resolver = composite_api_resolver_alloc();
|
||||
if(!resolver) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate protocol plugin resolver");
|
||||
return false;
|
||||
}
|
||||
composite_api_resolver_add(resolver, firmware_api_interface);
|
||||
|
||||
PluginManager* manager = plugin_manager_alloc(
|
||||
PROTOPIRATE_PROTOCOL_PLUGIN_APP_ID,
|
||||
PROTOPIRATE_PROTOCOL_PLUGIN_API_VERSION,
|
||||
composite_api_resolver_get(resolver));
|
||||
if(!manager) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate protocol plugin manager");
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* plugin_path = protopirate_get_registry_plugin_path(filter);
|
||||
PluginManagerError error = plugin_manager_load_single(manager, plugin_path);
|
||||
if(error != PluginManagerErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to load protocol plugin %s: %d", plugin_path, (int)error);
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
const ProtoPirateProtocolPlugin* plugin = plugin_manager_get_ep(manager, 0U);
|
||||
if(!plugin || !plugin->registry) {
|
||||
FURI_LOG_E(TAG, "Protocol plugin entry point is invalid");
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(plugin->filter != filter) {
|
||||
FURI_LOG_E(
|
||||
TAG, "Protocol plugin filter mismatch (expected %d got %d)", filter, plugin->filter);
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
return false;
|
||||
}
|
||||
|
||||
app->txrx->plugin_resolver = resolver;
|
||||
app->txrx->protocol_plugin_manager = manager;
|
||||
app->txrx->protocol_plugin = plugin;
|
||||
app->txrx->protocol_registry_filter = filter;
|
||||
*registry = plugin->registry;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_refresh_protocol_registry(ProtoPirateApp* app, bool ensure_receiver_ready) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
if(!app->txrx->environment || !app->txrx->preset) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoPirateProtocolRegistryFilter filter = protopirate_get_protocol_registry_filter_for_preset(
|
||||
app->txrx->preset->data, app->txrx->preset->data_size);
|
||||
bool filter_changed = !app->txrx->protocol_plugin ||
|
||||
(app->txrx->protocol_registry_filter != filter);
|
||||
|
||||
if(filter_changed) {
|
||||
protopirate_teardown_receiver_stack_for_registry_switch(app);
|
||||
} else if(ensure_receiver_ready && !app->txrx->receiver) {
|
||||
protopirate_teardown_receiver_stack_for_registry_switch(app);
|
||||
}
|
||||
|
||||
const SubGhzProtocolRegistry* registry = NULL;
|
||||
if(!protopirate_ensure_protocol_registry_plugin(app, filter, ®istry) || !registry) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Failed to resolve %s protocol registry plugin",
|
||||
protopirate_get_protocol_registry_filter_name(filter));
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool registry_already_bound = (app->txrx->protocol_registry == registry);
|
||||
if(!registry_already_bound) {
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Using %s protocol registry (%zu protocols)",
|
||||
protopirate_get_protocol_registry_filter_name(filter),
|
||||
registry->size);
|
||||
subghz_environment_set_protocol_registry(app->txrx->environment, registry);
|
||||
app->txrx->protocol_registry = registry;
|
||||
}
|
||||
|
||||
if(!ensure_receiver_ready) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(app->txrx->receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment);
|
||||
if(!app->txrx->receiver) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Failed to allocate receiver for %s registry",
|
||||
protopirate_get_protocol_registry_filter_name(filter));
|
||||
return false;
|
||||
}
|
||||
|
||||
subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_apply_protocol_registry_for_preset_data(
|
||||
ProtoPirateApp* app,
|
||||
const uint8_t* preset_data,
|
||||
size_t preset_data_size) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
if(!app->txrx->environment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProtoPirateProtocolRegistryFilter filter =
|
||||
protopirate_get_protocol_registry_filter_for_preset(preset_data, preset_data_size);
|
||||
|
||||
bool filter_changed = !app->txrx->protocol_plugin ||
|
||||
(app->txrx->protocol_registry_filter != filter);
|
||||
|
||||
if(filter_changed) {
|
||||
protopirate_teardown_receiver_stack_for_registry_switch(app);
|
||||
}
|
||||
|
||||
const SubGhzProtocolRegistry* registry = NULL;
|
||||
if(!protopirate_ensure_protocol_registry_plugin(app, filter, ®istry) || !registry) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Failed to resolve %s registry plugin for preset apply",
|
||||
protopirate_get_protocol_registry_filter_name(filter));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_registry == registry) {
|
||||
return true;
|
||||
}
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Switching active protocol registry to %s (%zu protocols)",
|
||||
protopirate_get_protocol_registry_filter_name(filter),
|
||||
registry->size);
|
||||
subghz_environment_set_protocol_registry(app->txrx->environment, registry);
|
||||
app->txrx->protocol_registry = registry;
|
||||
return true;
|
||||
}
|
||||
|
||||
void protopirate_preset_init(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
@@ -318,7 +117,12 @@ uint32_t protopirate_rx(ProtoPirateApp* app, uint32_t frequency) {
|
||||
}
|
||||
|
||||
if(!subghz_devices_is_frequency_valid(app->txrx->radio_device, frequency)) {
|
||||
furi_crash("ProtoPirate: Incorrect RX frequency.");
|
||||
FURI_LOG_E(TAG, "RX start rejected: invalid frequency %lu", frequency);
|
||||
if(app->notifications) {
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
}
|
||||
app->txrx->txrx_state = ProtoPirateTxRxStateIDLE;
|
||||
return 0;
|
||||
}
|
||||
if(app->txrx->txrx_state == ProtoPirateTxRxStateRx ||
|
||||
app->txrx->txrx_state == ProtoPirateTxRxStateSleep) {
|
||||
@@ -418,17 +222,17 @@ void protopirate_rx_stack_resume_after_tx(ProtoPirateApp* app) {
|
||||
}
|
||||
}
|
||||
|
||||
void protopirate_hopper_update(ProtoPirateApp* app) {
|
||||
bool protopirate_hopper_update(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
|
||||
switch(app->txrx->hopper_state) {
|
||||
case ProtoPirateHopperStateOFF:
|
||||
case ProtoPirateHopperStatePause:
|
||||
return;
|
||||
return false;
|
||||
case ProtoPirateHopperStateRSSITimeOut:
|
||||
if(app->txrx->hopper_timeout != 0) {
|
||||
app->txrx->hopper_timeout--;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -441,7 +245,7 @@ void protopirate_hopper_update(ProtoPirateApp* app) {
|
||||
if(rssi > -90.0f) {
|
||||
app->txrx->hopper_timeout = 10;
|
||||
app->txrx->hopper_state = ProtoPirateHopperStateRSSITimeOut;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
app->txrx->hopper_state = ProtoPirateHopperStateRunning;
|
||||
@@ -451,7 +255,7 @@ void protopirate_hopper_update(ProtoPirateApp* app) {
|
||||
if(hopper_count == 0) {
|
||||
app->txrx->hopper_state = ProtoPirateHopperStateOFF;
|
||||
app->txrx->hopper_idx_frequency = 0;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if(app->txrx->hopper_idx_frequency < hopper_count - 1) {
|
||||
app->txrx->hopper_idx_frequency++;
|
||||
@@ -462,12 +266,17 @@ void protopirate_hopper_update(ProtoPirateApp* app) {
|
||||
if(app->txrx->txrx_state == ProtoPirateTxRxStateRx) {
|
||||
protopirate_rx_end(app);
|
||||
}
|
||||
if(app->txrx->txrx_state == ProtoPirateTxRxStateIDLE && app->txrx->receiver) {
|
||||
subghz_receiver_reset(app->txrx->receiver);
|
||||
if(app->txrx->txrx_state == ProtoPirateTxRxStateIDLE) {
|
||||
app->txrx->preset->frequency =
|
||||
subghz_setting_get_hopper_frequency(app->setting, app->txrx->hopper_idx_frequency);
|
||||
protopirate_rx(app, app->txrx->preset->frequency);
|
||||
if(!protopirate_refresh_protocol_registry(app, true) || !app->txrx->receiver) {
|
||||
FURI_LOG_E(TAG, "Failed to refresh registry while hopping");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void protopirate_tx(ProtoPirateApp* app, uint32_t frequency) {
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <core/string.h>
|
||||
|
||||
typedef struct ProtoPirateApp ProtoPirateApp;
|
||||
|
||||
void protopirate_rx_stack_teardown_for_registry_switch(ProtoPirateApp* app);
|
||||
|
||||
void protopirate_preset_init(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint8_t* preset_data,
|
||||
size_t preset_data_size);
|
||||
|
||||
void protopirate_get_frequency_modulation(
|
||||
ProtoPirateApp* app,
|
||||
FuriString* frequency,
|
||||
FuriString* modulation);
|
||||
void protopirate_get_frequency_modulation_str(
|
||||
ProtoPirateApp* app,
|
||||
char* frequency,
|
||||
size_t frequency_size,
|
||||
char* modulation,
|
||||
size_t modulation_size);
|
||||
|
||||
void protopirate_begin(ProtoPirateApp* app, uint8_t* preset_data);
|
||||
uint32_t protopirate_rx(ProtoPirateApp* app, uint32_t frequency);
|
||||
void protopirate_idle(ProtoPirateApp* app);
|
||||
void protopirate_rx_end(ProtoPirateApp* app);
|
||||
void protopirate_sleep(ProtoPirateApp* app);
|
||||
bool protopirate_hopper_update(ProtoPirateApp* app);
|
||||
void protopirate_tx(ProtoPirateApp* app, uint32_t frequency);
|
||||
void protopirate_tx_stop(ProtoPirateApp* app);
|
||||
void protopirate_release_shared_radio_state(ProtoPirateApp* app);
|
||||
void protopirate_rx_stack_suspend_for_tx(ProtoPirateApp* app);
|
||||
void protopirate_rx_stack_resume_after_tx(ProtoPirateApp* app);
|
||||
@@ -28,6 +28,7 @@ typedef enum {
|
||||
// File management
|
||||
ProtoPirateCustomEventReceiverInfoSave,
|
||||
ProtoPirateCustomEventReceiverInfoSaveConfirm,
|
||||
ProtoPirateCustomEventReceiverInfoUpdate,
|
||||
ProtoPirateCustomEventReceiverInfoEmulate,
|
||||
ProtoPirateCustomEventReceiverInfoBruteforceStart,
|
||||
ProtoPirateCustomEventReceiverInfoBruteforceCancel,
|
||||
@@ -40,6 +41,7 @@ typedef enum {
|
||||
// Sub decode
|
||||
ProtoPirateCustomEventSubDecodeUpdate,
|
||||
ProtoPirateCustomEventSubDecodeSave,
|
||||
ProtoPirateCustomEventSubDecodeEmulate,
|
||||
ProtoPirateCustomEventSubDecodeBruteforceStart,
|
||||
ProtoPirateCustomEventPsaBruteforceComplete,
|
||||
// File Browser
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "protopirate_views.h"
|
||||
#include "../protopirate_app_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "ProtoPirateViews"
|
||||
|
||||
bool protopirate_ensure_variable_item_list(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(app->variable_item_list) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->variable_item_list = variable_item_list_alloc();
|
||||
if(!app->variable_item_list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
ProtoPirateViewVariableItemList,
|
||||
variable_item_list_get_view(app->variable_item_list));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_ensure_widget(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(app->widget) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->widget = widget_alloc();
|
||||
if(!app->widget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, ProtoPirateViewWidget, widget_get_view(app->widget));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_ensure_text_input(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(app->text_input) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->text_input = text_input_alloc();
|
||||
if(!app->text_input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, ProtoPirateViewTextInput, text_input_get_view(app->text_input));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_ensure_view_about(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(app->view_about) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->view_about = view_alloc();
|
||||
if(!app->view_about) {
|
||||
return false;
|
||||
}
|
||||
|
||||
view_dispatcher_add_view(app->view_dispatcher, ProtoPirateViewAbout, app->view_about);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_ensure_receiver_view(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(app->protopirate_receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->protopirate_receiver = protopirate_view_receiver_alloc(app->auto_save);
|
||||
if(!app->protopirate_receiver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
ProtoPirateViewReceiver,
|
||||
protopirate_view_receiver_get_view(app->protopirate_receiver));
|
||||
return true;
|
||||
}
|
||||
|
||||
void protopirate_views_free(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
|
||||
if(app->submenu) {
|
||||
FURI_LOG_D(TAG, "Removing submenu view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
app->submenu = NULL;
|
||||
}
|
||||
|
||||
if(app->variable_item_list) {
|
||||
FURI_LOG_D(TAG, "Removing variable_item_list view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewVariableItemList);
|
||||
variable_item_list_free(app->variable_item_list);
|
||||
app->variable_item_list = NULL;
|
||||
}
|
||||
|
||||
if(app->view_about) {
|
||||
FURI_LOG_D(TAG, "Removing about view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewAbout);
|
||||
view_free(app->view_about);
|
||||
app->view_about = NULL;
|
||||
}
|
||||
|
||||
if(app->widget) {
|
||||
FURI_LOG_D(TAG, "Removing widget view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewWidget);
|
||||
widget_free(app->widget);
|
||||
app->widget = NULL;
|
||||
}
|
||||
|
||||
if(app->text_input) {
|
||||
FURI_LOG_D(TAG, "Removing text_input view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewTextInput);
|
||||
text_input_free(app->text_input);
|
||||
app->text_input = NULL;
|
||||
}
|
||||
|
||||
if(app->protopirate_receiver) {
|
||||
FURI_LOG_D(TAG, "Removing receiver view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewReceiver);
|
||||
protopirate_view_receiver_free(app->protopirate_receiver);
|
||||
app->protopirate_receiver = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct ProtoPirateApp ProtoPirateApp;
|
||||
|
||||
bool protopirate_ensure_variable_item_list(ProtoPirateApp* app);
|
||||
bool protopirate_ensure_widget(ProtoPirateApp* app);
|
||||
bool protopirate_ensure_text_input(ProtoPirateApp* app);
|
||||
bool protopirate_ensure_view_about(ProtoPirateApp* app);
|
||||
bool protopirate_ensure_receiver_view(ProtoPirateApp* app);
|
||||
void protopirate_views_free(ProtoPirateApp* app);
|
||||
@@ -2,25 +2,26 @@
|
||||
|
||||
#ifdef ENABLE_SUB_DECODE_SCENE
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#include <lib/flipper_format/flipper_format_i.h>
|
||||
#include "../protocols/protocols_common.h"
|
||||
|
||||
#define TAG "RawFileReader"
|
||||
|
||||
#define RAW_READER_MAX_TOKEN_LEN 64U
|
||||
#define RAW_READER_KEY "RAW_Data:"
|
||||
|
||||
static const char local_flipper_format_delimiter = ':';
|
||||
static const char local_flipper_format_comment = '#';
|
||||
static const char local_flipper_format_eoln = '\n';
|
||||
static const char local_flipper_format_eolr = '\r';
|
||||
|
||||
struct FlipperFormat {
|
||||
Stream* stream;
|
||||
bool strict_mode;
|
||||
};
|
||||
|
||||
RawFileReader* raw_file_reader_alloc(void) {
|
||||
RawFileReader* reader = malloc(sizeof(RawFileReader));
|
||||
furi_check(reader);
|
||||
if(!reader) return NULL;
|
||||
memset(reader, 0, sizeof(RawFileReader));
|
||||
return reader;
|
||||
}
|
||||
@@ -31,84 +32,6 @@ void raw_file_reader_free(RawFileReader* reader) {
|
||||
free(reader);
|
||||
}
|
||||
|
||||
static inline bool local_flipper_format_stream_is_space(char c) {
|
||||
return c == ' ' || c == '\t' || c == local_flipper_format_eolr;
|
||||
}
|
||||
|
||||
static bool local_flipper_format_stream_read_value(Stream* stream, FuriString* value, bool* last) {
|
||||
enum {
|
||||
LeadingSpace,
|
||||
ReadValue,
|
||||
TrailingSpace
|
||||
} state = LeadingSpace;
|
||||
const size_t buffer_size = 32;
|
||||
uint8_t buffer[buffer_size];
|
||||
bool result = false;
|
||||
bool error = false;
|
||||
|
||||
furi_string_reset(value);
|
||||
|
||||
while(true) {
|
||||
size_t was_read = stream_read(stream, buffer, buffer_size);
|
||||
|
||||
if(was_read == 0) {
|
||||
if(state != LeadingSpace && stream_eof(stream)) {
|
||||
result = true;
|
||||
*last = true;
|
||||
} else {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < was_read; i++) {
|
||||
const uint8_t data = buffer[i];
|
||||
|
||||
if(state == LeadingSpace) {
|
||||
if(local_flipper_format_stream_is_space(data)) {
|
||||
continue;
|
||||
} else if(data == local_flipper_format_eoln) {
|
||||
stream_seek(stream, (int32_t)i - (int32_t)was_read, StreamOffsetFromCurrent);
|
||||
error = true;
|
||||
break;
|
||||
} else {
|
||||
state = ReadValue;
|
||||
furi_string_push_back(value, data);
|
||||
}
|
||||
} else if(state == ReadValue) {
|
||||
if(local_flipper_format_stream_is_space(data)) {
|
||||
state = TrailingSpace;
|
||||
} else if(data == local_flipper_format_eoln) {
|
||||
if(!stream_seek(
|
||||
stream, (int32_t)i - (int32_t)was_read, StreamOffsetFromCurrent)) {
|
||||
error = true;
|
||||
} else {
|
||||
result = true;
|
||||
*last = true;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
furi_string_push_back(value, data);
|
||||
}
|
||||
} else if(state == TrailingSpace) {
|
||||
if(local_flipper_format_stream_is_space(data)) {
|
||||
continue;
|
||||
} else if(!stream_seek(
|
||||
stream, (int32_t)i - (int32_t)was_read, StreamOffsetFromCurrent)) {
|
||||
error = true;
|
||||
} else {
|
||||
*last = (data == local_flipper_format_eoln);
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(error || result) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool local_flipper_format_stream_read_valid_key(Stream* stream, FuriString* key) {
|
||||
furi_string_reset(key);
|
||||
const size_t buffer_size = 32;
|
||||
@@ -165,8 +88,12 @@ static bool local_flipper_format_stream_read_valid_key(Stream* stream, FuriStrin
|
||||
// just new symbol, reset the new_line flag
|
||||
new_line = false;
|
||||
if(accumulate) {
|
||||
// and accumulate data if we want
|
||||
furi_string_push_back(key, data);
|
||||
|
||||
if(furi_string_size(key) >= RAW_READER_MAX_TOKEN_LEN) {
|
||||
accumulate = false;
|
||||
} else {
|
||||
furi_string_push_back(key, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,36 +129,125 @@ static bool
|
||||
return found;
|
||||
}
|
||||
|
||||
static bool local_flipper_format_stream_get_value_count(
|
||||
Stream* stream,
|
||||
const char* key,
|
||||
uint32_t* count,
|
||||
bool strict_mode) {
|
||||
bool result = false;
|
||||
bool last = false;
|
||||
static bool raw_file_reader_stream_read_char(RawFileReader* reader, char* out) {
|
||||
if(!reader || !reader->stream || !out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FuriString* value;
|
||||
value = furi_string_alloc();
|
||||
if(reader->buffer_index >= reader->buffer_count) {
|
||||
const size_t was_read =
|
||||
stream_read(reader->stream, reader->buffer, RAW_READER_BUFFER_SIZE);
|
||||
if(was_read == 0U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
if(!local_flipper_format_stream_seek_to_key(stream, key, strict_mode)) break;
|
||||
*count = 0;
|
||||
reader->buffer_count = (uint16_t)was_read;
|
||||
reader->buffer_index = 0U;
|
||||
}
|
||||
|
||||
result = true;
|
||||
while(true) {
|
||||
if(!local_flipper_format_stream_read_value(stream, value, &last)) {
|
||||
result = false;
|
||||
const uint8_t value = reader->buffer[reader->buffer_index++];
|
||||
if(reader->stream_pos < UINT64_MAX) {
|
||||
reader->stream_pos++;
|
||||
}
|
||||
*out = (char)value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool raw_file_reader_at_logical_eof(const RawFileReader* reader) {
|
||||
return reader && reader->stream && reader->buffer_index >= reader->buffer_count &&
|
||||
stream_eof(reader->stream);
|
||||
}
|
||||
|
||||
static void raw_file_reader_mark_finished(RawFileReader* reader) {
|
||||
reader->file_finished = true;
|
||||
reader->stream_pos = reader->file_size > 0U ? reader->file_size : reader->stream_pos;
|
||||
}
|
||||
|
||||
static bool raw_file_reader_seek_next_values(RawFileReader* reader) {
|
||||
const char* key = RAW_READER_KEY;
|
||||
size_t match = 0U;
|
||||
char c = '\0';
|
||||
|
||||
while(raw_file_reader_stream_read_char(reader, &c)) {
|
||||
if(c == key[match]) {
|
||||
match++;
|
||||
if(key[match] == '\0') {
|
||||
reader->in_line = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
match = (c == key[0]) ? 1U : 0U;
|
||||
}
|
||||
}
|
||||
|
||||
raw_file_reader_mark_finished(reader);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool raw_file_reader_stream_next_int(RawFileReader* reader, int32_t* out) {
|
||||
if(!reader || !out || !reader->stream) return false;
|
||||
|
||||
while(true) {
|
||||
if(!reader->in_line && !raw_file_reader_seek_next_values(reader)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool negative = false;
|
||||
bool sign_seen = false;
|
||||
bool have_digits = false;
|
||||
int32_t value = 0;
|
||||
char c = '\0';
|
||||
|
||||
while(raw_file_reader_stream_read_char(reader, &c)) {
|
||||
if(c == '\r') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == '\n') {
|
||||
reader->in_line = false;
|
||||
if(have_digits) {
|
||||
*out = negative ? -value : value;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
*count = *count + 1;
|
||||
if(last) break;
|
||||
if(!sign_seen && !have_digits && (c == ' ' || c == '\t' || c == ',')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!sign_seen && !have_digits && (c == '-' || c == '+')) {
|
||||
negative = (c == '-');
|
||||
sign_seen = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c >= '0' && c <= '9') {
|
||||
have_digits = true;
|
||||
value = (value * 10) + (c - '0');
|
||||
continue;
|
||||
}
|
||||
|
||||
if(have_digits) {
|
||||
*out = negative ? -value : value;
|
||||
return true;
|
||||
}
|
||||
|
||||
negative = false;
|
||||
sign_seen = false;
|
||||
}
|
||||
|
||||
} while(false);
|
||||
if(have_digits) {
|
||||
raw_file_reader_mark_finished(reader);
|
||||
*out = negative ? -value : value;
|
||||
return true;
|
||||
}
|
||||
|
||||
furi_string_free(value);
|
||||
return result;
|
||||
if(raw_file_reader_at_logical_eof(reader)) {
|
||||
raw_file_reader_mark_finished(reader);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool raw_file_reader_open(RawFileReader* reader, const char* file_path) {
|
||||
@@ -287,26 +303,43 @@ bool raw_file_reader_open(RawFileReader* reader, const char* file_path) {
|
||||
reader->buffer_count = 0;
|
||||
reader->buffer_index = 0;
|
||||
reader->file_finished = false;
|
||||
reader->current_level = true;
|
||||
reader->in_line = false;
|
||||
|
||||
FURI_LOG_I(TAG, "Opened RAW file: %s", file_path);
|
||||
|
||||
reader->count = 0;
|
||||
uint32_t temp_count = 0;
|
||||
|
||||
while(local_flipper_format_stream_get_value_count(
|
||||
reader->ff->stream, "RAW_Data", &temp_count, reader->ff->strict_mode)) {
|
||||
//reader->file_finished = true;
|
||||
reader->count += temp_count;
|
||||
reader->stream = flipper_format_get_raw_stream(reader->ff);
|
||||
if(!reader->stream) {
|
||||
FURI_LOG_E(TAG, "Missing raw stream");
|
||||
raw_file_reader_close(reader);
|
||||
return false;
|
||||
}
|
||||
flipper_format_rewind(reader->ff);
|
||||
|
||||
if(!local_flipper_format_stream_seek_to_key(reader->stream, "RAW_Data", false)) {
|
||||
FURI_LOG_E(TAG, "RAW file has no samples");
|
||||
raw_file_reader_close(reader);
|
||||
return false;
|
||||
}
|
||||
reader->in_line = true;
|
||||
reader->data_start_offset = stream_tell(reader->stream);
|
||||
reader->stream_pos = reader->data_start_offset;
|
||||
reader->file_size = stream_size(reader->stream);
|
||||
if(reader->file_size == 0) {
|
||||
FileInfo file_info = {0};
|
||||
if(storage_common_stat(reader->storage, file_path, &file_info) == FSE_OK) {
|
||||
reader->file_size = file_info.size;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Opened RAW file for streaming decode: %s",
|
||||
file_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
void raw_file_reader_close(RawFileReader* reader) {
|
||||
if(!reader) return;
|
||||
|
||||
reader->stream = NULL;
|
||||
|
||||
if(reader->ff) {
|
||||
flipper_format_free(reader->ff);
|
||||
reader->ff = NULL;
|
||||
@@ -320,44 +353,26 @@ void raw_file_reader_close(RawFileReader* reader) {
|
||||
reader->storage = NULL;
|
||||
reader->buffer_count = 0;
|
||||
reader->buffer_index = 0;
|
||||
reader->count = 0;
|
||||
reader->in_line = false;
|
||||
reader->file_finished = false;
|
||||
}
|
||||
|
||||
static bool raw_file_reader_load_chunk(RawFileReader* reader) {
|
||||
if(reader->file_finished) return false;
|
||||
|
||||
size_t to_read = (reader->count < RAW_READER_BUFFER_SIZE) ? reader->count :
|
||||
RAW_READER_BUFFER_SIZE;
|
||||
|
||||
if(!flipper_format_read_int32(reader->ff, "RAW_Data", reader->buffer, to_read)) {
|
||||
reader->file_finished = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
reader->buffer_count = to_read;
|
||||
reader->buffer_index = 0;
|
||||
reader->count -= to_read;
|
||||
|
||||
return true;
|
||||
reader->file_size = 0;
|
||||
reader->data_start_offset = 0;
|
||||
reader->stream_pos = 0;
|
||||
}
|
||||
|
||||
bool raw_file_reader_get_next(RawFileReader* reader, bool* level, uint32_t* duration) {
|
||||
if(!reader || !level || !duration) return false;
|
||||
|
||||
if(memmgr_get_free_heap() < 1024) {
|
||||
if(reader->buffer_index >= reader->buffer_count && memmgr_get_free_heap() < 1024) {
|
||||
FURI_LOG_E(TAG, "Not enough memory to continue reading");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(reader->buffer_index >= reader->buffer_count) {
|
||||
if(!raw_file_reader_load_chunk(reader)) {
|
||||
return false;
|
||||
}
|
||||
int32_t value = 0;
|
||||
if(!raw_file_reader_stream_next_int(reader, &value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t value = reader->buffer[reader->buffer_index++];
|
||||
|
||||
if(value >= 0) {
|
||||
*level = true;
|
||||
*duration = (uint32_t)value;
|
||||
@@ -371,6 +386,22 @@ bool raw_file_reader_get_next(RawFileReader* reader, bool* level, uint32_t* dura
|
||||
|
||||
bool raw_file_reader_is_finished(RawFileReader* reader) {
|
||||
if(!reader) return true;
|
||||
return reader->file_finished && (reader->buffer_index >= reader->buffer_count);
|
||||
return reader->file_finished && reader->buffer_index >= reader->buffer_count;
|
||||
}
|
||||
|
||||
uint8_t raw_file_reader_get_progress(const RawFileReader* reader) {
|
||||
if(!reader) return 0;
|
||||
if(reader->file_finished && reader->buffer_index >= reader->buffer_count) return 100;
|
||||
if(reader->file_size <= reader->data_start_offset) return 0;
|
||||
|
||||
const uint64_t total = reader->file_size - reader->data_start_offset;
|
||||
const uint64_t current_pos = reader->stream_pos;
|
||||
uint64_t done = 0;
|
||||
if(current_pos > reader->data_start_offset) {
|
||||
done = current_pos - reader->data_start_offset;
|
||||
}
|
||||
if(done >= total) return 100;
|
||||
|
||||
return (uint8_t)((done * 100ULL) / total);
|
||||
}
|
||||
#endif // ENABLE_SUB_DECODE_SCENE
|
||||
|
||||
@@ -5,19 +5,23 @@
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
|
||||
#define RAW_READER_BUFFER_SIZE 512
|
||||
#define RAW_READER_BUFFER_SIZE 2048U
|
||||
|
||||
typedef struct {
|
||||
Storage* storage;
|
||||
FlipperFormat* ff;
|
||||
int32_t buffer[RAW_READER_BUFFER_SIZE];
|
||||
size_t buffer_count;
|
||||
size_t buffer_index;
|
||||
uint32_t count;
|
||||
Stream* stream;
|
||||
uint8_t buffer[RAW_READER_BUFFER_SIZE];
|
||||
uint16_t buffer_count;
|
||||
uint16_t buffer_index;
|
||||
bool in_line;
|
||||
bool file_finished;
|
||||
bool current_level;
|
||||
bool storage_opened;
|
||||
uint64_t file_size;
|
||||
uint64_t data_start_offset;
|
||||
uint64_t stream_pos;
|
||||
} RawFileReader;
|
||||
|
||||
RawFileReader* raw_file_reader_alloc(void);
|
||||
@@ -26,4 +30,5 @@ bool raw_file_reader_open(RawFileReader* reader, const char* file_path);
|
||||
void raw_file_reader_close(RawFileReader* reader);
|
||||
bool raw_file_reader_get_next(RawFileReader* reader, bool* level, uint32_t* duration);
|
||||
bool raw_file_reader_is_finished(RawFileReader* reader);
|
||||
uint8_t raw_file_reader_get_progress(const RawFileReader* reader);
|
||||
#endif // ENABLE_SUB_DECODE_SCENE
|
||||
|
||||
@@ -215,7 +215,7 @@ static void chrysler_v0_decoder_commit(SubGhzProtocolDecoderChrysler_V0* instanc
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static uint8_t chrysler_v0_payload_get_bit(const uint8_t payload[10], uint8_t index) {
|
||||
const uint8_t byte = payload[index >> 3U];
|
||||
@@ -295,7 +295,7 @@ const SubGhzProtocolDecoder subghz_protocol_chrysler_v0_decoder = {
|
||||
.get_string = subghz_protocol_decoder_chrysler_v0_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_chrysler_v0_encoder = {
|
||||
.alloc = subghz_protocol_encoder_chrysler_v0_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -318,15 +318,23 @@ const SubGhzProtocol chrysler_protocol_v0 = {
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Load
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
| SubGhzProtocolFlag_Send
|
||||
#endif
|
||||
,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_chrysler_v0_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_chrysler_v0_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void* subghz_protocol_encoder_chrysler_v0_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
@@ -31,7 +31,7 @@ SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_chrysler_v0_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_chrysler_v0_get_string(void* context, FuriString* output);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* subghz_protocol_encoder_chrysler_v0_alloc(SubGhzEnvironment* environment);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_chrysler_v0_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
#include "fiat_v0.h"
|
||||
#include "protocols_common.h"
|
||||
#include "../protopirate_app_i.h"
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define TAG "FiatProtocolV0"
|
||||
#define FIAT_PROTOCOL_V0_NAME "Fiat V0"
|
||||
#define FIAT_V0_PREAMBLE_PAIRS 150
|
||||
#define FIAT_V0_GAP_US 800
|
||||
#define FIAT_V0_TOTAL_BURSTS 3
|
||||
@@ -42,6 +39,17 @@ typedef enum {
|
||||
FiatV0DecoderStepData = 2,
|
||||
} FiatV0DecoderStep;
|
||||
|
||||
static const char* fiat_v0_display_suffix(uint8_t endbyte) {
|
||||
const uint8_t low_nibble = endbyte & 0x0FU;
|
||||
if((low_nibble >= 0x04U) && (low_nibble <= 0x07U)) {
|
||||
return "Lock";
|
||||
}
|
||||
if((low_nibble >= 0x08U) && (low_nibble <= 0x0BU)) {
|
||||
return "Unlock";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
|
||||
static void fiat_v0_finish_packet(struct SubGhzProtocolDecoderFiatV0* instance) {
|
||||
instance->generic.data = ((uint64_t)instance->hop << 32) | instance->fix;
|
||||
instance->generic.data_count_bit = 71;
|
||||
@@ -78,7 +86,7 @@ const SubGhzProtocolDecoder subghz_protocol_fiat_v0_decoder = {
|
||||
.get_string = subghz_protocol_decoder_fiat_v0_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_fiat_v0_encoder = {
|
||||
.alloc = subghz_protocol_encoder_fiat_v0_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -102,14 +110,22 @@ const SubGhzProtocol fiat_protocol_v0 = {
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_fiat_v0_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_fiat_v0_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ENCODER IMPLEMENTATION
|
||||
// ============================================================================
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void* subghz_protocol_encoder_fiat_v0_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
@@ -128,7 +144,7 @@ void* subghz_protocol_encoder_fiat_v0_alloc(SubGhzEnvironment* environment) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void subghz_protocol_encoder_fiat_v0_get_upload(SubGhzProtocolEncoderFiatV0* instance) {
|
||||
furi_check(instance);
|
||||
@@ -220,7 +236,7 @@ static void subghz_protocol_encoder_fiat_v0_get_upload(SubGhzProtocolEncoderFiat
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_fiat_v0_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
@@ -239,10 +255,9 @@ SubGhzProtocolStatus
|
||||
uint32_t bit_count = 0;
|
||||
if(pp_encoder_read_bit(flipper_format, allowed_bits, 2, &bit_count) !=
|
||||
SubGhzProtocolStatusOk) {
|
||||
instance->generic.data_count_bit = 71; // legacy default for garbage Bit values
|
||||
} else {
|
||||
instance->generic.data_count_bit = bit_count;
|
||||
return SubGhzProtocolStatusErrorValueBitCount;
|
||||
}
|
||||
instance->generic.data_count_bit = bit_count;
|
||||
|
||||
uint64_t key = 0;
|
||||
if(!pp_flipper_read_hex_u64(flipper_format, FF_KEY, &key)) {
|
||||
@@ -489,51 +504,64 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_v0_serialize(
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderFiatV0* instance = context;
|
||||
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
SubGhzProtocolStatus ret =
|
||||
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
do {
|
||||
if(!flipper_format_write_uint32(flipper_format, FF_FREQUENCY, &preset->frequency, 1))
|
||||
break;
|
||||
ret = pp_serialize_fields(
|
||||
flipper_format,
|
||||
PP_FIELD_SERIAL | PP_FIELD_BTN | PP_FIELD_CNT,
|
||||
instance->fix,
|
||||
instance->endbyte,
|
||||
instance->hop,
|
||||
0);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_string_cstr(
|
||||
flipper_format, FF_PRESET, furi_string_get_cstr(preset->name)))
|
||||
break;
|
||||
uint32_t endbyte_ff = instance->endbyte;
|
||||
if(!flipper_format_write_uint32(flipper_format, "EndByte", &endbyte_ff, 1)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_string_cstr(
|
||||
flipper_format, FF_PROTOCOL, instance->generic.protocol_name))
|
||||
break;
|
||||
|
||||
uint32_t bits = instance->generic.data_count_bit;
|
||||
if(!flipper_format_write_uint32(flipper_format, FF_BIT, &bits, 1)) break;
|
||||
|
||||
char key_str[20];
|
||||
snprintf(key_str, sizeof(key_str), "%08lX%08lX", instance->hop, instance->fix);
|
||||
if(!flipper_format_write_string_cstr(flipper_format, FF_KEY, key_str)) break;
|
||||
|
||||
if(pp_serialize_fields(
|
||||
flipper_format,
|
||||
PP_FIELD_SERIAL | PP_FIELD_BTN | PP_FIELD_CNT,
|
||||
instance->fix,
|
||||
instance->endbyte,
|
||||
instance->hop,
|
||||
0) != SubGhzProtocolStatusOk)
|
||||
break;
|
||||
|
||||
uint32_t endbyte_ff = instance->endbyte;
|
||||
if(!flipper_format_write_uint32(flipper_format, "EndByte", &endbyte_ff, 1)) break;
|
||||
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
return pp_write_display(
|
||||
flipper_format,
|
||||
instance->generic.protocol_name,
|
||||
fiat_v0_display_suffix(instance->endbyte));
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_fiat_v0_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderFiatV0* instance = context;
|
||||
return subghz_block_generic_deserialize_check_count_bit(
|
||||
|
||||
SubGhzProtocolStatus status = subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic, flipper_format, subghz_protocol_fiat_v0_const.min_count_bit_for_found);
|
||||
if(status != SubGhzProtocolStatusOk) {
|
||||
return status;
|
||||
}
|
||||
|
||||
instance->hop = (uint32_t)(instance->generic.data >> 32U);
|
||||
instance->fix = (uint32_t)(instance->generic.data & 0xFFFFFFFFU);
|
||||
instance->decoder.decode_data = instance->generic.data;
|
||||
instance->decoder.decode_count_bit = instance->generic.data_count_bit;
|
||||
|
||||
uint32_t endbyte_u32 = 0U;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "EndByte", &endbyte_u32, 1)) {
|
||||
instance->endbyte = (uint8_t)(endbyte_u32 & 0x7FU);
|
||||
} else {
|
||||
pp_encoder_read_fields(flipper_format, NULL, &endbyte_u32, NULL, NULL);
|
||||
instance->endbyte = (uint8_t)(endbyte_u32 & 0x7FU);
|
||||
}
|
||||
|
||||
instance->generic.serial = instance->fix;
|
||||
instance->generic.btn = instance->endbyte;
|
||||
instance->generic.cnt = instance->hop;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_fiat_v0_get_string(void* context, FuriString* output) {
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
#include "../defines.h"
|
||||
|
||||
#define FIAT_PROTOCOL_V0_NAME "Fiat V0"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderFiatV0 SubGhzProtocolDecoderFiatV0;
|
||||
typedef struct SubGhzProtocolEncoderFiatV0 SubGhzProtocolEncoderFiatV0;
|
||||
|
||||
@@ -19,7 +21,6 @@ extern const SubGhzProtocol fiat_protocol_v0;
|
||||
|
||||
// Decoder functions
|
||||
void* subghz_protocol_decoder_fiat_v0_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_fiat_v0_free(void* context);
|
||||
void subghz_protocol_decoder_fiat_v0_reset(void* context);
|
||||
void subghz_protocol_decoder_fiat_v0_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_fiat_v0_get_hash_data(void* context);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,28 +5,32 @@
|
||||
#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>
|
||||
|
||||
#include "../defines.h"
|
||||
|
||||
#define FIAT_MARELLI_PROTOCOL_NAME "Fiat V1"
|
||||
#define FIAT_V1_PROTOCOL_NAME "Fiat V1"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderFiatMarelli SubGhzProtocolDecoderFiatMarelli;
|
||||
typedef struct SubGhzProtocolDecoderFiatV1 SubGhzProtocolDecoderFiatV1;
|
||||
typedef struct SubGhzProtocolEncoderFiatV1 SubGhzProtocolEncoderFiatV1;
|
||||
|
||||
extern const SubGhzProtocol fiat_v1_protocol;
|
||||
|
||||
void* subghz_protocol_decoder_fiat_marelli_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_fiat_marelli_free(void* context);
|
||||
void subghz_protocol_decoder_fiat_marelli_reset(void* context);
|
||||
void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_fiat_marelli_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_fiat_marelli_serialize(
|
||||
void* subghz_protocol_decoder_fiat_v1_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_fiat_v1_reset(void* context);
|
||||
void subghz_protocol_decoder_fiat_v1_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_fiat_v1_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_fiat_v1_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_fiat_marelli_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString* output);
|
||||
subghz_protocol_decoder_fiat_v1_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_fiat_v1_get_string(void* context, FuriString* output);
|
||||
|
||||
void* subghz_protocol_encoder_fiat_v1_alloc(SubGhzEnvironment* environment);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_fiat_v1_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
@@ -0,0 +1,417 @@
|
||||
#include "fiat_v2.h"
|
||||
#include "protocols_common.h"
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "FiatProtocolV2"
|
||||
|
||||
#define FIAT_V2_TE_SHORT 210U
|
||||
#define FIAT_V2_TE_LONG 420U
|
||||
#define FIAT_V2_TE_DELTA 100U
|
||||
#define FIAT_V2_BOUNDARY_MIN_US 900U
|
||||
#define FIAT_V2_WIRE_BITS 112U
|
||||
#define FIAT_V2_WIRE_BYTES 14U
|
||||
#define FIAT_V2_WIRE_CELLS (FIAT_V2_WIRE_BITS * 2U)
|
||||
#define FIAT_V2_LOGICAL_BITS 112U
|
||||
#define FIAT_V2_MARKER0 0x00U
|
||||
#define FIAT_V2_MARKER1 0x01U
|
||||
#define FIAT_V2_BTN_SHIFT 6U
|
||||
#define FIAT_V2_BUTTON_LOCK 0x2U
|
||||
#define FIAT_V2_BUTTON_UNLOCK 0x3U
|
||||
#define FIAT_V2_BUTTON_TRUNK 0x1U
|
||||
#define FIAT_V2_CNT_SHIFT 3U
|
||||
#define FIAT_V2_FCA_TYPE_NIBBLE 0xD0U
|
||||
#define FIAT_V2_RAW_FIELD "Raw"
|
||||
#define FIAT_V2_HOP_FIELD "Hop"
|
||||
#define FIAT_V2_BTN_FIELD "Btn"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_fiat_v2_const = {
|
||||
.te_short = FIAT_V2_TE_SHORT,
|
||||
.te_long = FIAT_V2_TE_LONG,
|
||||
.te_delta = FIAT_V2_TE_DELTA,
|
||||
.min_count_bit_for_found = FIAT_V2_LOGICAL_BITS,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
FiatV2DecoderStepReset = 0,
|
||||
FiatV2DecoderStepData = 1,
|
||||
} FiatV2DecoderStep;
|
||||
|
||||
struct SubGhzProtocolDecoderFiatV2 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint8_t cells[FIAT_V2_WIRE_CELLS];
|
||||
uint16_t cell_count;
|
||||
|
||||
uint8_t raw_data[FIAT_V2_WIRE_BYTES];
|
||||
uint8_t last_raw_data[FIAT_V2_WIRE_BYTES];
|
||||
bool last_raw_valid;
|
||||
|
||||
uint32_t uid;
|
||||
uint32_t hop;
|
||||
uint8_t button;
|
||||
};
|
||||
|
||||
static bool fiat_v2_feed_data_pulse(
|
||||
SubGhzProtocolDecoderFiatV2* instance,
|
||||
bool level,
|
||||
uint32_t duration);
|
||||
static bool fiat_v2_frame_valid(const uint8_t raw[FIAT_V2_WIRE_BYTES]);
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_fiat_v2_decoder = {
|
||||
.alloc = subghz_protocol_decoder_fiat_v2_alloc,
|
||||
.free = pp_decoder_free_default,
|
||||
.feed = subghz_protocol_decoder_fiat_v2_feed,
|
||||
.reset = subghz_protocol_decoder_fiat_v2_reset,
|
||||
.get_hash_data = subghz_protocol_decoder_fiat_v2_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_fiat_v2_serialize,
|
||||
.deserialize = subghz_protocol_decoder_fiat_v2_deserialize,
|
||||
.get_string = subghz_protocol_decoder_fiat_v2_get_string,
|
||||
};
|
||||
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_fiat_v2_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
#endif
|
||||
|
||||
const SubGhzProtocol fiat_v2_protocol = {
|
||||
.name = FIAT_V2_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_fiat_v2_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_fiat_v2_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
static bool fiat_v2_duration_is_short(uint32_t duration) {
|
||||
return pp_is_short(duration, &subghz_protocol_fiat_v2_const);
|
||||
}
|
||||
|
||||
static bool fiat_v2_duration_is_long(uint32_t duration) {
|
||||
return pp_is_long(duration, &subghz_protocol_fiat_v2_const);
|
||||
}
|
||||
|
||||
static bool fiat_v2_button_valid(uint8_t button) {
|
||||
const uint8_t sel = button >> FIAT_V2_BTN_SHIFT;
|
||||
return sel == FIAT_V2_BUTTON_LOCK || sel == FIAT_V2_BUTTON_UNLOCK ||
|
||||
sel == FIAT_V2_BUTTON_TRUNK;
|
||||
}
|
||||
|
||||
static const char* fiat_v2_button_name(uint8_t button) {
|
||||
switch(button >> FIAT_V2_BTN_SHIFT) {
|
||||
case FIAT_V2_BUTTON_LOCK:
|
||||
return "Lock";
|
||||
case FIAT_V2_BUTTON_UNLOCK:
|
||||
return "Unlock";
|
||||
case FIAT_V2_BUTTON_TRUNK:
|
||||
return "Trunk";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t fiat_v2_uid(const uint8_t raw[FIAT_V2_WIRE_BYTES]) {
|
||||
return ((uint32_t)raw[2] << 24U) | ((uint32_t)raw[3] << 16U) |
|
||||
((uint32_t)raw[4] << 8U) | raw[5];
|
||||
}
|
||||
|
||||
static bool fiat_v2_is_fca(const uint8_t raw[FIAT_V2_WIRE_BYTES]) {
|
||||
return (raw[6] & 0xF0U) == FIAT_V2_FCA_TYPE_NIBBLE;
|
||||
}
|
||||
static uint32_t fiat_v2_hop(const uint8_t raw[FIAT_V2_WIRE_BYTES]) {
|
||||
if(fiat_v2_is_fca(raw)) {
|
||||
return ((uint32_t)raw[10] << 24U) | ((uint32_t)raw[11] << 16U) |
|
||||
((uint32_t)raw[12] << 8U) | raw[13];
|
||||
}
|
||||
return ((uint32_t)raw[9] << 24U) | ((uint32_t)raw[10] << 16U) |
|
||||
((uint32_t)raw[11] << 8U) | raw[12];
|
||||
}
|
||||
|
||||
static uint32_t fiat_v2_counter(const uint8_t raw[FIAT_V2_WIRE_BYTES]) {
|
||||
if(fiat_v2_is_fca(raw)) {
|
||||
const uint32_t raw_cnt = ((uint32_t)raw[8] << 6U) | (uint32_t)(raw[9] >> 2U);
|
||||
return (~raw_cnt) & 0x3FFFU;
|
||||
}
|
||||
const uint32_t raw_cnt =
|
||||
((uint32_t)(raw[7] & 0x3FU) << 5U) | (uint32_t)(raw[8] >> FIAT_V2_CNT_SHIFT);
|
||||
return (~raw_cnt) & 0x7FFU;
|
||||
}
|
||||
|
||||
|
||||
static bool fiat_v2_frame_valid(const uint8_t raw[FIAT_V2_WIRE_BYTES]) {
|
||||
if(raw[0] != FIAT_V2_MARKER0 || raw[1] != FIAT_V2_MARKER1) {
|
||||
return false;
|
||||
}
|
||||
if(!fiat_v2_button_valid(raw[7])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t uid = fiat_v2_uid(raw);
|
||||
return uid != 0U && uid != UINT32_MAX;
|
||||
}
|
||||
|
||||
static void fiat_v2_clear_cells(SubGhzProtocolDecoderFiatV2* instance) {
|
||||
instance->cell_count = 0U;
|
||||
memset(instance->cells, 0, sizeof(instance->cells));
|
||||
}
|
||||
|
||||
static void fiat_v2_decode_fields(SubGhzProtocolDecoderFiatV2* instance) {
|
||||
instance->uid = fiat_v2_uid(instance->raw_data);
|
||||
instance->button = instance->raw_data[7];
|
||||
instance->hop = fiat_v2_hop(instance->raw_data);
|
||||
instance->generic.serial = instance->uid;
|
||||
instance->generic.btn = instance->button;
|
||||
instance->generic.cnt = fiat_v2_counter(instance->raw_data);
|
||||
instance->generic.data = ((uint64_t)instance->generic.serial << 32U) | instance->hop;
|
||||
instance->generic.data_count_bit = FIAT_V2_LOGICAL_BITS;
|
||||
instance->decoder.decode_data = instance->generic.data;
|
||||
instance->decoder.decode_count_bit = instance->generic.data_count_bit;
|
||||
}
|
||||
|
||||
static bool fiat_v2_commit(
|
||||
SubGhzProtocolDecoderFiatV2* instance,
|
||||
const uint8_t raw[FIAT_V2_WIRE_BYTES]) {
|
||||
if(!fiat_v2_frame_valid(raw)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(instance->last_raw_valid && memcmp(instance->last_raw_data, raw, FIAT_V2_WIRE_BYTES) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
memcpy(instance->raw_data, raw, FIAT_V2_WIRE_BYTES);
|
||||
memcpy(instance->last_raw_data, raw, FIAT_V2_WIRE_BYTES);
|
||||
instance->last_raw_valid = true;
|
||||
fiat_v2_decode_fields(instance);
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Accepted UID:%08lX Btn:%02X Cnt:%02lX Hop:%08lX",
|
||||
(unsigned long)instance->uid,
|
||||
instance->button,
|
||||
(unsigned long)instance->generic.cnt,
|
||||
(unsigned long)instance->hop);
|
||||
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fiat_v2_try_decode_window(SubGhzProtocolDecoderFiatV2* instance, bool invert) {
|
||||
if(instance->cell_count != FIAT_V2_WIRE_CELLS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t raw[FIAT_V2_WIRE_BYTES] = {0};
|
||||
for(uint8_t bit_index = 0U; bit_index < FIAT_V2_WIRE_BITS; bit_index++) {
|
||||
const uint8_t first = instance->cells[bit_index * 2U];
|
||||
const uint8_t second = instance->cells[bit_index * 2U + 1U];
|
||||
if(first == second) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bit = first != 0U;
|
||||
if(invert) {
|
||||
bit = !bit;
|
||||
}
|
||||
if(bit) {
|
||||
raw[bit_index >> 3U] |= (uint8_t)(1U << (7U - (bit_index & 7U)));
|
||||
}
|
||||
}
|
||||
|
||||
return fiat_v2_commit(instance, raw);
|
||||
}
|
||||
|
||||
static void fiat_v2_try_decode(SubGhzProtocolDecoderFiatV2* instance) {
|
||||
if(fiat_v2_try_decode_window(instance, false)) {
|
||||
return;
|
||||
}
|
||||
(void)fiat_v2_try_decode_window(instance, true);
|
||||
}
|
||||
|
||||
static void fiat_v2_push_cell(SubGhzProtocolDecoderFiatV2* instance, bool level) {
|
||||
if(instance->cell_count < FIAT_V2_WIRE_CELLS) {
|
||||
instance->cells[instance->cell_count++] = level ? 1U : 0U;
|
||||
} else {
|
||||
memmove(instance->cells, &instance->cells[1], FIAT_V2_WIRE_CELLS - 1U);
|
||||
instance->cells[FIAT_V2_WIRE_CELLS - 1U] = level ? 1U : 0U;
|
||||
}
|
||||
fiat_v2_try_decode(instance);
|
||||
}
|
||||
|
||||
static bool fiat_v2_feed_data_pulse(
|
||||
SubGhzProtocolDecoderFiatV2* instance,
|
||||
bool level,
|
||||
uint32_t duration) {
|
||||
if(fiat_v2_duration_is_short(duration)) {
|
||||
fiat_v2_push_cell(instance, level);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(fiat_v2_duration_is_long(duration)) {
|
||||
fiat_v2_push_cell(instance, level);
|
||||
fiat_v2_push_cell(instance, level);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!level && duration >= FIAT_V2_BOUNDARY_MIN_US) {
|
||||
fiat_v2_push_cell(instance, false);
|
||||
}
|
||||
fiat_v2_clear_cells(instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_fiat_v2_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderFiatV2* instance = calloc(1, sizeof(SubGhzProtocolDecoderFiatV2));
|
||||
furi_check(instance);
|
||||
instance->base.protocol = &fiat_v2_protocol;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
subghz_protocol_decoder_fiat_v2_reset(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_fiat_v2_reset(void* context) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderFiatV2* instance = context;
|
||||
|
||||
memset(instance->raw_data, 0, sizeof(instance->raw_data));
|
||||
memset(instance->last_raw_data, 0, sizeof(instance->last_raw_data));
|
||||
instance->decoder.parser_step = FiatV2DecoderStepReset;
|
||||
instance->decoder.decode_data = 0U;
|
||||
instance->decoder.decode_count_bit = 0U;
|
||||
instance->last_raw_valid = false;
|
||||
instance->generic.data = 0U;
|
||||
instance->generic.data_count_bit = 0U;
|
||||
instance->generic.serial = 0U;
|
||||
instance->generic.btn = 0U;
|
||||
instance->generic.cnt = 0U;
|
||||
instance->uid = 0U;
|
||||
instance->hop = 0U;
|
||||
instance->button = 0U;
|
||||
fiat_v2_clear_cells(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_fiat_v2_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderFiatV2* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case FiatV2DecoderStepReset:
|
||||
if(fiat_v2_duration_is_short(duration) || fiat_v2_duration_is_long(duration)) {
|
||||
fiat_v2_clear_cells(instance);
|
||||
instance->decoder.parser_step = FiatV2DecoderStepData;
|
||||
(void)fiat_v2_feed_data_pulse(instance, level, duration);
|
||||
}
|
||||
break;
|
||||
|
||||
case FiatV2DecoderStepData:
|
||||
if(!fiat_v2_feed_data_pulse(instance, level, duration)) {
|
||||
instance->decoder.parser_step = FiatV2DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_fiat_v2_get_hash_data(void* context) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderFiatV2* instance = context;
|
||||
SubGhzBlockDecoder decoder = {
|
||||
.decode_data = instance->generic.data,
|
||||
.decode_count_bit = 64U,
|
||||
};
|
||||
return subghz_protocol_blocks_get_hash_data(&decoder, 8U) ^ instance->generic.cnt ^
|
||||
instance->button;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_fiat_v2_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderFiatV2* instance = context;
|
||||
|
||||
SubGhzProtocolStatus ret =
|
||||
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_hex(
|
||||
flipper_format, FIAT_V2_RAW_FIELD, instance->raw_data, FIAT_V2_WIRE_BYTES);
|
||||
|
||||
uint32_t hop = instance->hop;
|
||||
uint32_t button = instance->button;
|
||||
if(!flipper_format_write_uint32(flipper_format, FIAT_V2_HOP_FIELD, &hop, 1) ||
|
||||
!flipper_format_write_uint32(flipper_format, FIAT_V2_BTN_FIELD, &button, 1)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_SERIAL, instance->generic.serial);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_BTN, instance->generic.btn);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_CNT, instance->generic.cnt);
|
||||
return SubGhzProtocolStatusOk;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_fiat_v2_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderFiatV2* instance = context;
|
||||
|
||||
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic, flipper_format, subghz_protocol_fiat_v2_const.min_count_bit_for_found);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
return ret;
|
||||
}
|
||||
if(instance->generic.data_count_bit != FIAT_V2_LOGICAL_BITS) {
|
||||
return SubGhzProtocolStatusErrorValueBitCount;
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_hex(
|
||||
flipper_format, FIAT_V2_RAW_FIELD, instance->raw_data, FIAT_V2_WIRE_BYTES)) {
|
||||
if(!fiat_v2_frame_valid(instance->raw_data)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
fiat_v2_decode_fields(instance);
|
||||
return SubGhzProtocolStatusOk;
|
||||
}
|
||||
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_fiat_v2_get_string(void* context, FuriString* output) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderFiatV2* instance = context;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %ubit\r\n"
|
||||
"UID:%08lX\r\n"
|
||||
"Hop:%08lX Type:%01X\r\n"
|
||||
"Btn:%02X [%s] Cnt:%02lX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
FIAT_V2_LOGICAL_BITS,
|
||||
(unsigned long)instance->uid,
|
||||
(unsigned long)instance->hop,
|
||||
(unsigned)(instance->raw_data[6] >> 4),
|
||||
instance->button,
|
||||
fiat_v2_button_name(instance->button),
|
||||
(unsigned long)instance->generic.cnt);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#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 "../defines.h"
|
||||
|
||||
#define FIAT_V2_PROTOCOL_NAME "Fiat V2"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderFiatV2 SubGhzProtocolDecoderFiatV2;
|
||||
|
||||
extern const SubGhzProtocol fiat_v2_protocol;
|
||||
|
||||
void* subghz_protocol_decoder_fiat_v2_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_fiat_v2_reset(void* context);
|
||||
void subghz_protocol_decoder_fiat_v2_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_fiat_v2_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_fiat_v2_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_fiat_v2_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_fiat_v2_get_string(void* context, FuriString* output);
|
||||
@@ -61,7 +61,7 @@ typedef struct SubGhzProtocolDecoderFordV0 {
|
||||
uint8_t button;
|
||||
uint32_t count;
|
||||
} SubGhzProtocolDecoderFordV0;
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
typedef struct SubGhzProtocolEncoderFordV0 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
@@ -94,7 +94,7 @@ static void decode_ford_v0(
|
||||
uint32_t* serial,
|
||||
uint8_t* button,
|
||||
uint32_t* count);
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void encode_ford_v0(
|
||||
uint8_t header_byte,
|
||||
uint32_t serial,
|
||||
@@ -120,7 +120,7 @@ const SubGhzProtocolDecoder subghz_protocol_ford_v0_decoder = {
|
||||
.get_string = subghz_protocol_decoder_ford_v0_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_ford_v0_encoder = {
|
||||
.alloc = subghz_protocol_encoder_ford_v0_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -144,14 +144,22 @@ const SubGhzProtocol ford_protocol_v0 = {
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_ford_v0_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_ford_v0_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// CHECKSUM CALCULATION
|
||||
// =============================================================================
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static uint8_t ford_v0_calculate_checksum(uint32_t serial, uint32_t count, uint8_t button) {
|
||||
return (uint8_t)((((count >> 24) & 0xFF) + ((count >> 16) & 0xFF) + ((count >> 8) & 0xFF) +
|
||||
(count & 0xFF) + ((serial >> 24) & 0xFF) + ((serial >> 16) & 0xFF) +
|
||||
@@ -179,7 +187,7 @@ static uint8_t ford_v0_calculate_crc(uint8_t* buf) {
|
||||
|
||||
return crc;
|
||||
}
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static uint8_t ford_v0_calculate_crc_for_tx(uint64_t key1, uint8_t checksum) {
|
||||
uint8_t buf[16] = {0};
|
||||
|
||||
@@ -274,7 +282,7 @@ static void decode_ford_v0(
|
||||
// =============================================================================
|
||||
// ENCODE FUNCTION
|
||||
// =============================================================================
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void encode_ford_v0(
|
||||
uint8_t header_byte,
|
||||
uint32_t serial,
|
||||
@@ -357,7 +365,10 @@ static void encode_ford_v0(
|
||||
|
||||
void* subghz_protocol_encoder_ford_v0_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderFordV0* instance = malloc(sizeof(SubGhzProtocolEncoderFordV0));
|
||||
SubGhzProtocolEncoderFordV0* instance = calloc(1, sizeof(SubGhzProtocolEncoderFordV0));
|
||||
if(!instance) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
instance->base.protocol = &ford_protocol_v0;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
@@ -607,7 +618,10 @@ static bool ford_v0_process_data(SubGhzProtocolDecoderFordV0* instance) {
|
||||
|
||||
void* subghz_protocol_decoder_ford_v0_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderFordV0* instance = malloc(sizeof(SubGhzProtocolDecoderFordV0));
|
||||
SubGhzProtocolDecoderFordV0* instance = calloc(1, sizeof(SubGhzProtocolDecoderFordV0));
|
||||
if(!instance) {
|
||||
return NULL;
|
||||
}
|
||||
instance->base.protocol = &ford_protocol_v0;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
|
||||
@@ -66,7 +66,7 @@ static bool ford_v1_extract_plain_from_raw(
|
||||
const uint8_t* raw17_in,
|
||||
uint8_t* plain9_out,
|
||||
uint8_t* raw17_canonical_out_opt);
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void
|
||||
ford_v1_plain_apply_fields(uint8_t* plain9, uint32_t serial, uint8_t btn, uint32_t cnt);
|
||||
static void ford_v1_encoder_rebuild_raw_from_plain(uint8_t* raw17, const uint8_t* plain9);
|
||||
@@ -83,7 +83,7 @@ const SubGhzProtocolDecoder subghz_protocol_ford_v1_decoder = {
|
||||
.get_string = subghz_protocol_decoder_ford_v1_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_ford_v1_encoder = {
|
||||
.alloc = subghz_protocol_encoder_ford_v1_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -106,12 +106,20 @@ const SubGhzProtocol ford_protocol_v1 = {
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
| SubGhzProtocolFlag_Send
|
||||
#endif
|
||||
,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_ford_v1_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_ford_v1_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
#define ford_v1_crc16(data, len) subghz_protocol_blocks_crc16((data), (len), 0x1021, 0x0000)
|
||||
@@ -796,11 +804,15 @@ SubGhzProtocolStatus
|
||||
if(ret == SubGhzProtocolStatusOk) {
|
||||
flipper_format_rewind(flipper_format);
|
||||
uint8_t key1_bytes[8] = {0};
|
||||
flipper_format_read_hex(flipper_format, FF_KEY, key1_bytes, 8);
|
||||
if(!flipper_format_read_hex(flipper_format, FF_KEY, key1_bytes, 8)) {
|
||||
return SubGhzProtocolStatusErrorParserKey;
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
uint8_t key2_bytes[8] = {0};
|
||||
flipper_format_read_hex(flipper_format, "Key_2", key2_bytes, 8);
|
||||
if(!flipper_format_read_hex(flipper_format, "Key_2", key2_bytes, 8)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
uint8_t key3_bytes[4] = {0};
|
||||
@@ -912,7 +924,7 @@ void subghz_protocol_decoder_ford_v1_get_string(void* context, FuriString* outpu
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
#define FORD_V1_ENC_BURST_COUNT 6U
|
||||
#define FORD_V1_ENC_PREAMBLE_PAIRS 400U
|
||||
@@ -1200,4 +1212,4 @@ SubGhzProtocolStatus
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // ENABLE_EMULATE_FEATURE
|
||||
#endif // PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
@@ -32,7 +32,7 @@ SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_ford_v1_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_ford_v1_get_string(void* context, FuriString* output);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* subghz_protocol_encoder_ford_v1_alloc(SubGhzEnvironment* environment);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_ford_v1_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#define FORD_V2_ENC_PREAMBLE_PAIRS 70U
|
||||
#define FORD_V2_ENC_BURST_COUNT 6U
|
||||
#define FORD_V2_ENC_INTER_BURST_GAP_US 16000U
|
||||
#define FORD_V2_ENC_ALLOC_ELEMS 2600U
|
||||
#define FORD_V2_ENC_SEPARATOR_ELEMS 2U
|
||||
#define FORD_V2_ENC_PREAMBLE_ELEMS (FORD_V2_ENC_PREAMBLE_PAIRS * 2U)
|
||||
#define FORD_V2_ENC_DATA_ELEMS ((FORD_V2_DATA_BITS - 1U) * 2U)
|
||||
@@ -70,7 +69,7 @@ typedef struct SubGhzProtocolDecoderFordV2 {
|
||||
bool structure_ok;
|
||||
} SubGhzProtocolDecoderFordV2;
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
typedef struct SubGhzProtocolEncoderFordV2 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
@@ -125,7 +124,7 @@ static bool ford_v2_button_is_valid(uint8_t btn) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static uint8_t ford_v2_uint8_parity(uint8_t value) {
|
||||
uint8_t parity = 0U;
|
||||
while(value) {
|
||||
@@ -350,7 +349,7 @@ static void ford_v2_decoder_rebuild_raw_buffer(SubGhzProtocolDecoderFordV2* inst
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static inline void ford_v2_encoder_add_level(
|
||||
SubGhzProtocolEncoderFordV2* instance,
|
||||
bool level,
|
||||
@@ -360,7 +359,7 @@ static inline void ford_v2_encoder_add_level(
|
||||
uint32_t prev = level_duration_get_duration(instance->encoder.upload[idx - 1]);
|
||||
instance->encoder.upload[idx - 1] = level_duration_make(level, prev + duration);
|
||||
} else {
|
||||
furi_check(idx < FORD_V2_ENC_ALLOC_ELEMS);
|
||||
furi_check(idx < FORD_V2_ENC_UPLOAD_ELEMS);
|
||||
instance->encoder.upload[idx] = level_duration_make(level, duration);
|
||||
instance->encoder.size_upload++;
|
||||
}
|
||||
@@ -486,11 +485,8 @@ static SubGhzProtocolStatus
|
||||
static void ford_v2_encoder_deserialize_apply_repeat(
|
||||
SubGhzProtocolEncoderFordV2* instance,
|
||||
FlipperFormat* flipper_format) {
|
||||
flipper_format_rewind(flipper_format);
|
||||
uint32_t repeat = FORD_V2_ENCODER_DEFAULT_REPEAT;
|
||||
if(flipper_format_read_uint32(flipper_format, "Repeat", &repeat, 1)) {
|
||||
instance->encoder.repeat = repeat;
|
||||
}
|
||||
instance->encoder.repeat =
|
||||
(int32_t)pp_encoder_read_repeat(flipper_format, FORD_V2_ENCODER_DEFAULT_REPEAT);
|
||||
}
|
||||
|
||||
void* subghz_protocol_encoder_ford_v2_alloc(SubGhzEnvironment* environment) {
|
||||
@@ -501,7 +497,7 @@ void* subghz_protocol_encoder_ford_v2_alloc(SubGhzEnvironment* environment) {
|
||||
instance->base.protocol = &ford_protocol_v2;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->encoder.repeat = FORD_V2_ENCODER_DEFAULT_REPEAT;
|
||||
instance->encoder.upload = calloc(FORD_V2_ENC_ALLOC_ELEMS, sizeof(LevelDuration));
|
||||
instance->encoder.upload = calloc(FORD_V2_ENC_UPLOAD_ELEMS, sizeof(LevelDuration));
|
||||
furi_check(instance->encoder.upload);
|
||||
|
||||
return instance;
|
||||
@@ -797,7 +793,7 @@ const SubGhzProtocolDecoder subghz_protocol_ford_v2_decoder = {
|
||||
.get_string = subghz_protocol_decoder_ford_v2_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_ford_v2_encoder = {
|
||||
.alloc = subghz_protocol_encoder_ford_v2_alloc,
|
||||
.free = subghz_protocol_encoder_ford_v2_free,
|
||||
@@ -820,10 +816,18 @@ const SubGhzProtocol ford_protocol_v2 = {
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
| SubGhzProtocolFlag_Send
|
||||
#endif
|
||||
,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_ford_v2_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_ford_v2_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_ford_v2_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_ford_v2_get_string(void* context, FuriString* output);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* subghz_protocol_encoder_ford_v2_alloc(SubGhzEnvironment* environment);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_ford_v2_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
@@ -3,16 +3,24 @@
|
||||
#include "protocols_common.h"
|
||||
#include <string.h>
|
||||
|
||||
#define FORD_V3_TE_SHORT 240U
|
||||
#define FORD_V3_TE_LONG 480U
|
||||
#define FORD_V3_TE_DELTA 60U
|
||||
#define FORD_V3_DATA_BITS 104U
|
||||
#define FORD_V3_DATA_BYTES 13U
|
||||
#define FORD_V3_PREAMBLE_MIN 30U
|
||||
#define FORD_V3_TE_SHORT 240U
|
||||
#define FORD_V3_TE_LONG 480U
|
||||
#define FORD_V3_TE_DELTA 60U
|
||||
#define FORD_V3_CELL_TE_DELTA 120U
|
||||
#define FORD_V3_DATA_BITS 104U
|
||||
#define FORD_V3_DATA_BYTES 13U
|
||||
#define FORD_V3_PREAMBLE_MIN 30U
|
||||
#define FORD_V3_CELL_CAP 320U
|
||||
#define FORD_V3_CELL_MIN 200U
|
||||
#define FORD_V3_CELL_MIN_BITS 100U
|
||||
#define FORD_V3_FF_VARIANT "Variant"
|
||||
|
||||
#define FORD_V3_BTN_LOCK 0x01U
|
||||
#define FORD_V3_BTN_UNLOCK 0x02U
|
||||
|
||||
#define FORD_V3_VARIANT_EU 0U
|
||||
#define FORD_V3_VARIANT_US 1U
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_ford_v3_const = {
|
||||
.te_short = FORD_V3_TE_SHORT,
|
||||
.te_long = FORD_V3_TE_LONG,
|
||||
@@ -20,6 +28,13 @@ static const SubGhzBlockConst subghz_protocol_ford_v3_const = {
|
||||
.min_count_bit_for_found = FORD_V3_DATA_BITS,
|
||||
};
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_ford_v3_cell_const = {
|
||||
.te_short = FORD_V3_TE_SHORT,
|
||||
.te_long = FORD_V3_TE_LONG,
|
||||
.te_delta = FORD_V3_CELL_TE_DELTA,
|
||||
.min_count_bit_for_found = FORD_V3_DATA_BITS,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
FordV3DecoderStepReset = 0,
|
||||
FordV3DecoderStepPreamble = 1,
|
||||
@@ -32,21 +47,52 @@ typedef struct SubGhzProtocolDecoderFordV3 {
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
ManchesterState manchester_state;
|
||||
uint8_t raw_bytes[FORD_V3_DATA_BYTES];
|
||||
uint8_t bit_count;
|
||||
uint8_t manchester_raw[FORD_V3_DATA_BYTES];
|
||||
uint8_t manchester_bit_count;
|
||||
uint16_t preamble_count;
|
||||
|
||||
uint8_t cells[FORD_V3_CELL_CAP];
|
||||
uint16_t cell_count;
|
||||
|
||||
uint8_t raw_bytes[FORD_V3_DATA_BYTES];
|
||||
uint8_t last_raw_bytes[FORD_V3_DATA_BYTES];
|
||||
bool last_raw_valid;
|
||||
|
||||
uint8_t variant;
|
||||
uint8_t flag;
|
||||
uint32_t serial;
|
||||
uint16_t counter;
|
||||
} SubGhzProtocolDecoderFordV3;
|
||||
|
||||
static void ford_v3_reset_data(SubGhzProtocolDecoderFordV3* instance);
|
||||
static void ford_v3_add_bit(SubGhzProtocolDecoderFordV3* instance, bool bit);
|
||||
static void ford_v3_reset_manchester(SubGhzProtocolDecoderFordV3* instance);
|
||||
static void ford_v3_reset_cells(SubGhzProtocolDecoderFordV3* instance);
|
||||
static void ford_v3_add_manchester_bit(SubGhzProtocolDecoderFordV3* instance, bool bit);
|
||||
static bool ford_v3_cell_frame_valid(const uint8_t* raw);
|
||||
static uint8_t ford_v3_variant_from_saved_or_raw(const uint8_t* raw, uint32_t saved_variant);
|
||||
static void ford_v3_parse_fields(SubGhzProtocolDecoderFordV3* instance);
|
||||
static void ford_v3_emit_if_ready(SubGhzProtocolDecoderFordV3* instance);
|
||||
static const char* ford_v3_button_name(uint8_t btn);
|
||||
static bool ford_v3_commit_frame(
|
||||
SubGhzProtocolDecoderFordV3* instance,
|
||||
const uint8_t* raw,
|
||||
uint8_t variant);
|
||||
static void ford_v3_manchester_emit_if_ready(SubGhzProtocolDecoderFordV3* instance);
|
||||
static void ford_v3_cell_process(SubGhzProtocolDecoderFordV3* instance);
|
||||
static void ford_v3_cell_feed(SubGhzProtocolDecoderFordV3* instance, bool level, uint32_t duration);
|
||||
static void
|
||||
ford_v3_manchester_feed(SubGhzProtocolDecoderFordV3* instance, bool level, uint32_t duration);
|
||||
static const char* ford_v3_button_name(uint8_t btn, uint8_t variant);
|
||||
|
||||
static const char* ford_v3_button_name(uint8_t btn, uint8_t variant) {
|
||||
if(variant == FORD_V3_VARIANT_US) {
|
||||
switch(btn) {
|
||||
case FORD_V3_BTN_LOCK:
|
||||
return "Lock";
|
||||
case FORD_V3_BTN_UNLOCK:
|
||||
return "Unlock";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* ford_v3_button_name(uint8_t btn) {
|
||||
switch(btn) {
|
||||
case FORD_V3_BTN_LOCK:
|
||||
return "Lock";
|
||||
@@ -57,25 +103,62 @@ static const char* ford_v3_button_name(uint8_t btn) {
|
||||
}
|
||||
}
|
||||
|
||||
static void ford_v3_reset_data(SubGhzProtocolDecoderFordV3* instance) {
|
||||
memset(instance->raw_bytes, 0, sizeof(instance->raw_bytes));
|
||||
instance->bit_count = 0;
|
||||
static bool ford_v3_cell_frame_valid(const uint8_t* raw) {
|
||||
if(raw[0] != 0xFFU) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t serial = ((uint32_t)raw[1] << 24) | ((uint32_t)raw[2] << 16) |
|
||||
((uint32_t)raw[3] << 8) | (uint32_t)raw[4];
|
||||
if(serial == 0U || serial == 0xFFFFFFFFU) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(raw[6] != FORD_V3_BTN_LOCK && raw[6] != FORD_V3_BTN_UNLOCK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if((raw[5] & 0x80U) == 0U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t ford_v3_variant_from_saved_or_raw(const uint8_t* raw, uint32_t saved_variant) {
|
||||
if(saved_variant == FORD_V3_VARIANT_US) {
|
||||
return FORD_V3_VARIANT_US;
|
||||
}
|
||||
if(saved_variant == FORD_V3_VARIANT_EU) {
|
||||
return FORD_V3_VARIANT_EU;
|
||||
}
|
||||
|
||||
return ford_v3_cell_frame_valid(raw) ? FORD_V3_VARIANT_US : FORD_V3_VARIANT_EU;
|
||||
}
|
||||
|
||||
static void ford_v3_reset_manchester(SubGhzProtocolDecoderFordV3* instance) {
|
||||
memset(instance->manchester_raw, 0, sizeof(instance->manchester_raw));
|
||||
instance->manchester_bit_count = 0;
|
||||
instance->preamble_count = 0;
|
||||
manchester_advance(
|
||||
instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL);
|
||||
}
|
||||
|
||||
static void ford_v3_add_bit(SubGhzProtocolDecoderFordV3* instance, bool bit) {
|
||||
if(instance->bit_count >= FORD_V3_DATA_BITS) {
|
||||
static void ford_v3_reset_cells(SubGhzProtocolDecoderFordV3* instance) {
|
||||
instance->cell_count = 0;
|
||||
}
|
||||
|
||||
static void ford_v3_add_manchester_bit(SubGhzProtocolDecoderFordV3* instance, bool bit) {
|
||||
if(instance->manchester_bit_count >= FORD_V3_DATA_BITS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t byte_index = instance->bit_count / 8U;
|
||||
const uint8_t bit_in_byte = 7U - (instance->bit_count % 8U);
|
||||
const uint8_t byte_index = instance->manchester_bit_count / 8U;
|
||||
const uint8_t bit_in_byte = 7U - (instance->manchester_bit_count % 8U);
|
||||
if(bit) {
|
||||
instance->raw_bytes[byte_index] |= (uint8_t)(1U << bit_in_byte);
|
||||
instance->manchester_raw[byte_index] |= (uint8_t)(1U << bit_in_byte);
|
||||
}
|
||||
instance->bit_count++;
|
||||
instance->manchester_bit_count++;
|
||||
}
|
||||
|
||||
static void ford_v3_parse_fields(SubGhzProtocolDecoderFordV3* instance) {
|
||||
@@ -83,55 +166,121 @@ static void ford_v3_parse_fields(SubGhzProtocolDecoderFordV3* instance) {
|
||||
|
||||
instance->serial = ((uint32_t)b[1] << 24) | ((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 8) |
|
||||
(uint32_t)b[4];
|
||||
instance->counter = (uint16_t)((((uint16_t)(uint8_t)~b[7]) << 8) | (uint8_t)~b[8]);
|
||||
|
||||
instance->generic.serial = instance->serial;
|
||||
instance->generic.btn = (b[6] & 0x01U) ? FORD_V3_BTN_UNLOCK : FORD_V3_BTN_LOCK;
|
||||
|
||||
if(instance->variant == FORD_V3_VARIANT_US) {
|
||||
instance->flag = b[5];
|
||||
instance->counter = (uint16_t)(((uint16_t)b[7] << 8) | (uint16_t)b[8]);
|
||||
instance->generic.btn = b[6];
|
||||
} else {
|
||||
instance->flag = 0;
|
||||
instance->counter = (uint16_t)((((uint16_t)(uint8_t)~b[7]) << 8) | (uint8_t)~b[8]);
|
||||
instance->generic.btn = (b[6] & 0x01U) ? FORD_V3_BTN_UNLOCK : FORD_V3_BTN_LOCK;
|
||||
}
|
||||
|
||||
instance->generic.cnt = instance->counter;
|
||||
}
|
||||
|
||||
static void ford_v3_emit_if_ready(SubGhzProtocolDecoderFordV3* instance) {
|
||||
if(instance->bit_count < FORD_V3_DATA_BITS) {
|
||||
return;
|
||||
static bool ford_v3_commit_frame(
|
||||
SubGhzProtocolDecoderFordV3* instance,
|
||||
const uint8_t* raw,
|
||||
uint8_t variant) {
|
||||
if(instance->last_raw_valid && memcmp(instance->last_raw_bytes, raw, FORD_V3_DATA_BYTES) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
memcpy(instance->raw_bytes, raw, FORD_V3_DATA_BYTES);
|
||||
memcpy(instance->last_raw_bytes, raw, FORD_V3_DATA_BYTES);
|
||||
instance->last_raw_valid = true;
|
||||
|
||||
instance->variant = variant;
|
||||
instance->generic.data_count_bit = FORD_V3_DATA_BITS;
|
||||
ford_v3_parse_fields(instance);
|
||||
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_ford_v3_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
static void ford_v3_manchester_emit_if_ready(SubGhzProtocolDecoderFordV3* instance) {
|
||||
if(instance->manchester_bit_count < FORD_V3_DATA_BITS) {
|
||||
return;
|
||||
}
|
||||
|
||||
SubGhzProtocolDecoderFordV3* instance = calloc(1, sizeof(SubGhzProtocolDecoderFordV3));
|
||||
furi_check(instance);
|
||||
|
||||
instance->base.protocol = &ford_protocol_v3;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
(void)ford_v3_commit_frame(instance, instance->manchester_raw, FORD_V3_VARIANT_EU);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ford_v3_reset(void* context) {
|
||||
furi_check(context);
|
||||
static bool ford_v3_cell_decode(const uint8_t* cells, uint16_t cell_count, uint8_t* raw_out) {
|
||||
for(int phase = 0; phase < 2; phase++) {
|
||||
uint8_t frame[FORD_V3_DATA_BYTES];
|
||||
memset(frame, 0, sizeof(frame));
|
||||
|
||||
SubGhzProtocolDecoderFordV3* instance = context;
|
||||
instance->decoder.parser_step = FordV3DecoderStepReset;
|
||||
ford_v3_reset_data(instance);
|
||||
int bit_count = 0;
|
||||
bool ok = true;
|
||||
for(int i = phase; (i + 1) < (int)cell_count && bit_count < (int)FORD_V3_DATA_BITS; i += 2) {
|
||||
const uint8_t first = cells[i];
|
||||
const uint8_t second = cells[i + 1];
|
||||
if(first == second) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if(first) {
|
||||
frame[bit_count >> 3] |= (uint8_t)(1U << (7 - (bit_count & 7)));
|
||||
}
|
||||
bit_count++;
|
||||
}
|
||||
|
||||
if(!ok || bit_count < (int)FORD_V3_CELL_MIN_BITS) {
|
||||
continue;
|
||||
}
|
||||
if(!ford_v3_cell_frame_valid(frame)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(raw_out, frame, FORD_V3_DATA_BYTES);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ford_v3_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_check(context);
|
||||
static void ford_v3_cell_process(SubGhzProtocolDecoderFordV3* instance) {
|
||||
if(instance->cell_count < FORD_V3_CELL_MIN) {
|
||||
return;
|
||||
}
|
||||
|
||||
SubGhzProtocolDecoderFordV3* instance = context;
|
||||
uint8_t raw[FORD_V3_DATA_BYTES];
|
||||
if(!ford_v3_cell_decode(instance->cells, instance->cell_count, raw)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)ford_v3_commit_frame(instance, raw, FORD_V3_VARIANT_US);
|
||||
}
|
||||
|
||||
static void ford_v3_cell_feed(SubGhzProtocolDecoderFordV3* instance, bool level, uint32_t duration) {
|
||||
if(pp_is_short(duration, &subghz_protocol_ford_v3_cell_const)) {
|
||||
if(instance->cell_count < FORD_V3_CELL_CAP) {
|
||||
instance->cells[instance->cell_count++] = level ? 1U : 0U;
|
||||
}
|
||||
} else if(pp_is_long(duration, &subghz_protocol_ford_v3_cell_const)) {
|
||||
if(instance->cell_count + 2U <= FORD_V3_CELL_CAP) {
|
||||
instance->cells[instance->cell_count++] = level ? 1U : 0U;
|
||||
instance->cells[instance->cell_count++] = level ? 1U : 0U;
|
||||
}
|
||||
} else {
|
||||
ford_v3_cell_process(instance);
|
||||
instance->cell_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ford_v3_manchester_feed(SubGhzProtocolDecoderFordV3* instance, bool level, uint32_t duration) {
|
||||
switch(instance->decoder.parser_step) {
|
||||
case FordV3DecoderStepReset:
|
||||
if(pp_is_short(duration, &subghz_protocol_ford_v3_const)) {
|
||||
ford_v3_reset_data(instance);
|
||||
ford_v3_reset_manchester(instance);
|
||||
instance->preamble_count = 1U;
|
||||
instance->decoder.parser_step = FordV3DecoderStepPreamble;
|
||||
}
|
||||
@@ -151,7 +300,7 @@ void subghz_protocol_decoder_ford_v3_feed(void* context, bool level, uint32_t du
|
||||
const bool valid = manchester_advance(
|
||||
instance->manchester_state, event, &instance->manchester_state, &data_bit);
|
||||
if(valid) {
|
||||
ford_v3_add_bit(instance, data_bit);
|
||||
ford_v3_add_manchester_bit(instance, data_bit);
|
||||
}
|
||||
instance->decoder.parser_step = FordV3DecoderStepData;
|
||||
} else {
|
||||
@@ -159,14 +308,14 @@ void subghz_protocol_decoder_ford_v3_feed(void* context, bool level, uint32_t du
|
||||
}
|
||||
break;
|
||||
|
||||
case FordV3DecoderStepData: {
|
||||
case FordV3DecoderStepData:
|
||||
if(!pp_is_short(duration, &subghz_protocol_ford_v3_const) &&
|
||||
!pp_is_long(duration, &subghz_protocol_ford_v3_const)) {
|
||||
ford_v3_emit_if_ready(instance);
|
||||
ford_v3_manchester_emit_if_ready(instance);
|
||||
instance->decoder.parser_step = FordV3DecoderStepReset;
|
||||
|
||||
if(pp_is_short(duration, &subghz_protocol_ford_v3_const)) {
|
||||
ford_v3_reset_data(instance);
|
||||
ford_v3_reset_manchester(instance);
|
||||
instance->preamble_count = 1U;
|
||||
instance->decoder.parser_step = FordV3DecoderStepPreamble;
|
||||
}
|
||||
@@ -189,15 +338,44 @@ void subghz_protocol_decoder_ford_v3_feed(void* context, bool level, uint32_t du
|
||||
instance->manchester_state, event, &instance->manchester_state, &data_bit);
|
||||
|
||||
if(valid) {
|
||||
ford_v3_add_bit(instance, data_bit);
|
||||
if(instance->bit_count >= FORD_V3_DATA_BITS) {
|
||||
ford_v3_emit_if_ready(instance);
|
||||
ford_v3_add_manchester_bit(instance, data_bit);
|
||||
if(instance->manchester_bit_count >= FORD_V3_DATA_BITS) {
|
||||
ford_v3_manchester_emit_if_ready(instance);
|
||||
instance->decoder.parser_step = FordV3DecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_ford_v3_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
SubGhzProtocolDecoderFordV3* instance = calloc(1, sizeof(SubGhzProtocolDecoderFordV3));
|
||||
furi_check(instance);
|
||||
|
||||
instance->base.protocol = &ford_protocol_v3;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ford_v3_reset(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderFordV3* instance = context;
|
||||
instance->decoder.parser_step = FordV3DecoderStepReset;
|
||||
ford_v3_reset_manchester(instance);
|
||||
ford_v3_reset_cells(instance);
|
||||
instance->last_raw_valid = false;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ford_v3_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderFordV3* instance = context;
|
||||
ford_v3_cell_feed(instance, level, duration);
|
||||
ford_v3_manchester_feed(instance, level, duration);
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_ford_v3_get_hash_data(void* context) {
|
||||
@@ -239,6 +417,7 @@ SubGhzProtocolStatus subghz_protocol_decoder_ford_v3_serialize(
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_SERIAL, instance->generic.serial);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_BTN, instance->generic.btn);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_CNT, instance->counter);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FORD_V3_FF_VARIANT, instance->variant);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -266,7 +445,13 @@ SubGhzProtocolStatus
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_read_hex(flipper_format, "Raw", instance->raw_bytes, FORD_V3_DATA_BYTES);
|
||||
|
||||
instance->bit_count = FORD_V3_DATA_BITS;
|
||||
uint32_t variant = UINT32_MAX;
|
||||
if(!flipper_format_read_uint32(flipper_format, FORD_V3_FF_VARIANT, &variant, 1)) {
|
||||
variant = UINT32_MAX;
|
||||
}
|
||||
instance->variant = ford_v3_variant_from_saved_or_raw(instance->raw_bytes, variant);
|
||||
|
||||
instance->manchester_bit_count = FORD_V3_DATA_BITS;
|
||||
ford_v3_parse_fields(instance);
|
||||
|
||||
return ret;
|
||||
@@ -278,6 +463,39 @@ void subghz_protocol_decoder_ford_v3_get_string(void* context, FuriString* outpu
|
||||
SubGhzProtocolDecoderFordV3* instance = context;
|
||||
const uint8_t* k = instance->raw_bytes;
|
||||
|
||||
if(instance->variant == FORD_V3_VARIANT_US) {
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s US %dbit\r\n"
|
||||
"Key:%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\r\n"
|
||||
"Sn:%08lX Btn:%02X %s\r\n"
|
||||
"Cnt:%04X Hop:%02X%02X%02X%02X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
(int)instance->generic.data_count_bit,
|
||||
k[0],
|
||||
k[1],
|
||||
k[2],
|
||||
k[3],
|
||||
k[4],
|
||||
k[5],
|
||||
k[6],
|
||||
k[7],
|
||||
k[8],
|
||||
k[9],
|
||||
k[10],
|
||||
k[11],
|
||||
k[12],
|
||||
(unsigned long)instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
ford_v3_button_name(instance->generic.btn, FORD_V3_VARIANT_US),
|
||||
(unsigned)instance->counter,
|
||||
k[9],
|
||||
k[10],
|
||||
k[11],
|
||||
k[12]);
|
||||
return;
|
||||
}
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
@@ -301,7 +519,7 @@ void subghz_protocol_decoder_ford_v3_get_string(void* context, FuriString* outpu
|
||||
k[12],
|
||||
(unsigned long)instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
ford_v3_button_name(instance->generic.btn),
|
||||
ford_v3_button_name(instance->generic.btn, FORD_V3_VARIANT_EU),
|
||||
(unsigned)instance->counter,
|
||||
k[9],
|
||||
k[10],
|
||||
@@ -331,8 +549,17 @@ const SubGhzProtocolEncoder subghz_protocol_ford_v3_encoder = {
|
||||
const SubGhzProtocol ford_protocol_v3 = {
|
||||
.name = FORD_PROTOCOL_V3_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |
|
||||
SubGhzProtocolFlag_Save,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_ford_v3_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_ford_v3_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ _Static_assert(
|
||||
HONDA_STATIC_UPLOAD_CAPACITY <= PP_SHARED_UPLOAD_CAPACITY,
|
||||
"HONDA_STATIC_UPLOAD_CAPACITY exceeds shared upload slab");
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static const uint8_t honda_static_encoder_button_map[4] = {0x02, 0x04, 0x08, 0x05};
|
||||
#endif
|
||||
static const char* const honda_static_button_names[9] = {
|
||||
@@ -50,7 +50,7 @@ struct SubGhzProtocolDecoderHondaStatic {
|
||||
uint16_t symbols_count;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
struct SubGhzProtocolEncoderHondaStatic {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
@@ -92,7 +92,7 @@ static uint32_t honda_static_get_bits_u32(const uint8_t* data, uint8_t start, ui
|
||||
return value;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void honda_static_set_bits(uint8_t* data, uint8_t start, uint8_t count, uint32_t value) {
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
const uint8_t bit_index = start + i;
|
||||
@@ -143,7 +143,7 @@ static bool honda_static_is_valid_serial(uint32_t serial) {
|
||||
return (serial != 0U) && (serial != 0x0FFFFFFFU);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static uint8_t honda_static_encoder_remap_button(uint8_t button) {
|
||||
if(button < 2U) {
|
||||
return 1U;
|
||||
@@ -212,7 +212,7 @@ static uint64_t honda_static_pack_compact(const HondaStaticFields* fields) {
|
||||
return pp_bytes_to_u64_be(compact);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void honda_static_build_packet_bytes(const HondaStaticFields* fields, uint8_t packet[8]) {
|
||||
memset(packet, 0, 8);
|
||||
|
||||
@@ -408,7 +408,7 @@ static void honda_static_decoder_commit(
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void honda_static_build_upload(SubGhzProtocolEncoderHondaStatic* instance) {
|
||||
uint8_t packet[8];
|
||||
honda_static_build_packet_bytes(&instance->decoded, packet);
|
||||
@@ -449,7 +449,7 @@ const SubGhzProtocolDecoder subghz_protocol_honda_static_decoder = {
|
||||
.get_string = subghz_protocol_decoder_honda_static_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_honda_static_encoder = {
|
||||
.alloc = subghz_protocol_encoder_honda_static_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -473,11 +473,19 @@ const SubGhzProtocol honda_static_protocol = {
|
||||
.flag = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 |
|
||||
SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Load |
|
||||
SubGhzProtocolFlag_Send,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_honda_static_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_honda_static_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* subghz_protocol_encoder_honda_static_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_honda_static_get_string(void* context, FuriString* output);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* subghz_protocol_encoder_honda_static_alloc(SubGhzEnvironment* environment);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
@@ -62,7 +62,7 @@ static const char* const honda_v1_button_names[HONDA_V1_BUTTON_MAX + 1U] = {
|
||||
[HondaV1ButtonPanic] = "Panic",
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static const uint32_t honda_v1_button_codes[HONDA_V1_BUTTON_MAX + 1U] = {
|
||||
[HondaV1ButtonUnlock] = 0x00080808,
|
||||
[HondaV1ButtonLock] = 0x00088888,
|
||||
@@ -87,7 +87,7 @@ struct SubGhzProtocolDecoderHondaV1 {
|
||||
uint8_t k2;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
struct SubGhzProtocolEncoderHondaV1 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
@@ -109,7 +109,7 @@ static const char* honda_v1_button_name(uint8_t b) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static uint32_t honda_v1_button_code(uint8_t button) {
|
||||
if(!honda_v1_button_valid(button)) {
|
||||
return HONDA_V1_BUTTON_FALLBACK_CODE;
|
||||
@@ -177,7 +177,7 @@ static void honda_v1_decode_fields(SubGhzBlockGeneric* generic) {
|
||||
generic->data_count_bit = HONDA_V1_BIT_COUNT;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static uint64_t honda_v1_build_key(uint32_t serial, uint8_t button, uint16_t counter) {
|
||||
const uint32_t table = honda_v1_button_code(button);
|
||||
const uint32_t low = ((table & HONDA_V1_COUNTER_MASK) << 16U) | counter;
|
||||
@@ -306,7 +306,7 @@ static void
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static bool honda_v1_append_frame(
|
||||
SubGhzProtocolEncoderHondaV1* instance,
|
||||
size_t* index,
|
||||
@@ -407,7 +407,7 @@ const SubGhzProtocolDecoder subghz_protocol_honda_v1_decoder = {
|
||||
.get_string = subghz_protocol_decoder_honda_v1_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_honda_v1_encoder = {
|
||||
.alloc = subghz_protocol_encoder_honda_v1_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -430,15 +430,23 @@ const SubGhzProtocol honda_v1_protocol = {
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Load
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
| SubGhzProtocolFlag_Send
|
||||
#endif
|
||||
,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_honda_v1_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_honda_v1_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* subghz_protocol_encoder_honda_v1_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_honda_v1_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_honda_v1_get_string(void* context, FuriString* output);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* subghz_protocol_encoder_honda_v1_alloc(SubGhzEnvironment* environment);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_honda_v1_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
+234
-226
@@ -1,40 +1,40 @@
|
||||
#include "land_rover_v0.h"
|
||||
#include "honda_v2.h"
|
||||
#include "protocols_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "LandRoverV0"
|
||||
#define TAG "HondaV2"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_land_rover_v0_const = {
|
||||
static const SubGhzBlockConst subghz_protocol_honda_v2_const = {
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 81,
|
||||
};
|
||||
|
||||
#define LAND_ROVER_V0_PREAMBLE_PAIRS 319U
|
||||
#define LAND_ROVER_V0_MIN_PREAMBLE_PAIRS 64U
|
||||
#define LAND_ROVER_V0_SYNC_US 750U
|
||||
#define LAND_ROVER_V0_SYNC_DELTA_US 120U
|
||||
#define LAND_ROVER_V0_UPLOAD_CAPACITY 1024U
|
||||
#define LAND_ROVER_V0_GAP_US 50000U
|
||||
#define HONDA_V2_PREAMBLE_PAIRS 319U
|
||||
#define HONDA_V2_MIN_PREAMBLE_PAIRS 64U
|
||||
#define HONDA_V2_SYNC_US 750U
|
||||
#define HONDA_V2_SYNC_DELTA_US 120U
|
||||
#define HONDA_V2_UPLOAD_CAPACITY 1024U
|
||||
#define HONDA_V2_GAP_US 50000U
|
||||
_Static_assert(
|
||||
LAND_ROVER_V0_UPLOAD_CAPACITY <= PP_SHARED_UPLOAD_CAPACITY,
|
||||
"LAND_ROVER_V0_UPLOAD_CAPACITY exceeds shared upload slab");
|
||||
HONDA_V2_UPLOAD_CAPACITY <= PP_SHARED_UPLOAD_CAPACITY,
|
||||
"HONDA_V2_UPLOAD_CAPACITY exceeds shared upload slab");
|
||||
|
||||
#define LAND_ROVER_V0_BTN_UNKNOWN 0x00U
|
||||
#define LAND_ROVER_V0_BTN_LOCK 0x02U
|
||||
#define LAND_ROVER_V0_BTN_UNLOCK 0x04U
|
||||
#define HONDA_V2_BTN_UNKNOWN 0x00U
|
||||
#define HONDA_V2_BTN_LOCK 0x02U
|
||||
#define HONDA_V2_BTN_UNLOCK 0x04U
|
||||
|
||||
#define LAND_ROVER_V0_SIG_UNLOCK 0xA285E3UL
|
||||
#define LAND_ROVER_V0_SIG_LOCK 0xC20363UL
|
||||
#define HONDA_V2_SIG_UNLOCK 0xA285E3UL
|
||||
#define HONDA_V2_SIG_LOCK 0xC20363UL
|
||||
|
||||
#define LAND_ROVER_V0_FF_BTNSIG "BtnSig"
|
||||
#define LAND_ROVER_V0_FF_CHECK "Check"
|
||||
#define LAND_ROVER_V0_FF_TAIL "Tail"
|
||||
#define LAND_ROVER_V0_FF_EXTRA_BIT "ExtraBit"
|
||||
#define HONDA_V2_FF_BTNSIG "BtnSig"
|
||||
#define HONDA_V2_FF_CHECK "Check"
|
||||
#define HONDA_V2_FF_TAIL "Tail"
|
||||
#define HONDA_V2_FF_EXTRA_BIT "ExtraBit"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderLandRoverV0 {
|
||||
typedef struct SubGhzProtocolDecoderHondaV2 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
@@ -56,10 +56,10 @@ typedef struct SubGhzProtocolDecoderLandRoverV0 {
|
||||
uint8_t check;
|
||||
bool check_ok;
|
||||
bool tail_ok;
|
||||
} SubGhzProtocolDecoderLandRoverV0;
|
||||
} SubGhzProtocolDecoderHondaV2;
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
typedef struct SubGhzProtocolEncoderLandRoverV0 {
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
typedef struct SubGhzProtocolEncoderHondaV2 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
@@ -71,77 +71,77 @@ typedef struct SubGhzProtocolEncoderLandRoverV0 {
|
||||
uint32_t count;
|
||||
uint8_t button;
|
||||
uint8_t check;
|
||||
} SubGhzProtocolEncoderLandRoverV0;
|
||||
} SubGhzProtocolEncoderHondaV2;
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
LandRoverV0DecoderStepReset = 0,
|
||||
LandRoverV0DecoderStepPreambleLow,
|
||||
LandRoverV0DecoderStepPreambleHigh,
|
||||
LandRoverV0DecoderStepSyncLow,
|
||||
LandRoverV0DecoderStepData,
|
||||
} LandRoverV0DecoderStep;
|
||||
HondaV2DecoderStepReset = 0,
|
||||
HondaV2DecoderStepPreambleLow,
|
||||
HondaV2DecoderStepPreambleHigh,
|
||||
HondaV2DecoderStepSyncLow,
|
||||
HondaV2DecoderStepData,
|
||||
} HondaV2DecoderStep;
|
||||
|
||||
static uint8_t land_rover_v0_button_from_signature(uint32_t signature);
|
||||
static const char* land_rover_v0_button_name(uint8_t button);
|
||||
static uint8_t land_rover_v0_calculate_check(uint32_t count);
|
||||
static bool land_rover_v0_calculate_tail_msb(uint32_t count);
|
||||
static uint16_t land_rover_v0_calculate_tail(uint32_t count);
|
||||
static void land_rover_v0_parse_key_fields(
|
||||
static uint8_t honda_v2_button_from_signature(uint32_t signature);
|
||||
static const char* honda_v2_button_name(uint8_t button);
|
||||
static uint8_t honda_v2_calculate_check(uint32_t count);
|
||||
static bool honda_v2_calculate_tail_msb(uint32_t count);
|
||||
static uint16_t honda_v2_calculate_tail(uint32_t count);
|
||||
static void honda_v2_parse_key_fields(
|
||||
uint64_t key,
|
||||
uint32_t* signature,
|
||||
uint32_t* serial,
|
||||
uint32_t* count,
|
||||
uint8_t* button,
|
||||
uint8_t* check);
|
||||
static bool land_rover_v0_validate_frame(
|
||||
static bool honda_v2_validate_frame(
|
||||
uint64_t key,
|
||||
uint16_t tail,
|
||||
bool extra_bit,
|
||||
bool* check_ok,
|
||||
bool* tail_ok);
|
||||
static bool land_rover_v0_add_decoded_bit(SubGhzProtocolDecoderLandRoverV0* instance, bool bit);
|
||||
static bool land_rover_v0_process_transition(
|
||||
SubGhzProtocolDecoderLandRoverV0* instance,
|
||||
static bool honda_v2_add_decoded_bit(SubGhzProtocolDecoderHondaV2* instance, bool bit);
|
||||
static bool honda_v2_process_transition(
|
||||
SubGhzProtocolDecoderHondaV2* instance,
|
||||
bool level,
|
||||
uint32_t duration);
|
||||
static bool land_rover_v0_finish_frame(SubGhzProtocolDecoderLandRoverV0* instance);
|
||||
static bool honda_v2_finish_frame(SubGhzProtocolDecoderHondaV2* instance);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
static bool land_rover_v0_encoder_add_level(
|
||||
SubGhzProtocolEncoderLandRoverV0* instance,
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static bool honda_v2_encoder_add_level(
|
||||
SubGhzProtocolEncoderHondaV2* instance,
|
||||
size_t* index,
|
||||
bool level,
|
||||
uint32_t duration);
|
||||
static bool land_rover_v0_encoder_add_bit(
|
||||
SubGhzProtocolEncoderLandRoverV0* instance,
|
||||
static bool honda_v2_encoder_add_bit(
|
||||
SubGhzProtocolEncoderHondaV2* instance,
|
||||
size_t* index,
|
||||
bool* previous_bit,
|
||||
bool bit);
|
||||
static bool land_rover_v0_build_upload(SubGhzProtocolEncoderLandRoverV0* instance);
|
||||
static bool honda_v2_build_upload(SubGhzProtocolEncoderHondaV2* instance);
|
||||
#endif
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_land_rover_v0_decoder = {
|
||||
.alloc = subghz_protocol_decoder_land_rover_v0_alloc,
|
||||
.free = subghz_protocol_decoder_land_rover_v0_free,
|
||||
.feed = subghz_protocol_decoder_land_rover_v0_feed,
|
||||
.reset = subghz_protocol_decoder_land_rover_v0_reset,
|
||||
.get_hash_data = subghz_protocol_decoder_land_rover_v0_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_land_rover_v0_serialize,
|
||||
.deserialize = subghz_protocol_decoder_land_rover_v0_deserialize,
|
||||
.get_string = subghz_protocol_decoder_land_rover_v0_get_string,
|
||||
const SubGhzProtocolDecoder subghz_protocol_honda_v2_decoder = {
|
||||
.alloc = subghz_protocol_decoder_honda_v2_alloc,
|
||||
.free = subghz_protocol_decoder_honda_v2_free,
|
||||
.feed = subghz_protocol_decoder_honda_v2_feed,
|
||||
.reset = subghz_protocol_decoder_honda_v2_reset,
|
||||
.get_hash_data = subghz_protocol_decoder_honda_v2_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_honda_v2_serialize,
|
||||
.deserialize = subghz_protocol_decoder_honda_v2_deserialize,
|
||||
.get_string = subghz_protocol_decoder_honda_v2_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
const SubGhzProtocolEncoder subghz_protocol_land_rover_v0_encoder = {
|
||||
.alloc = subghz_protocol_encoder_land_rover_v0_alloc,
|
||||
.free = subghz_protocol_encoder_land_rover_v0_free,
|
||||
.deserialize = subghz_protocol_encoder_land_rover_v0_deserialize,
|
||||
.stop = subghz_protocol_encoder_land_rover_v0_stop,
|
||||
.yield = subghz_protocol_encoder_land_rover_v0_yield,
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_honda_v2_encoder = {
|
||||
.alloc = subghz_protocol_encoder_honda_v2_alloc,
|
||||
.free = subghz_protocol_encoder_honda_v2_free,
|
||||
.deserialize = subghz_protocol_encoder_honda_v2_deserialize,
|
||||
.stop = subghz_protocol_encoder_honda_v2_stop,
|
||||
.yield = subghz_protocol_encoder_honda_v2_yield,
|
||||
};
|
||||
#else
|
||||
const SubGhzProtocolEncoder subghz_protocol_land_rover_v0_encoder = {
|
||||
const SubGhzProtocolEncoder subghz_protocol_honda_v2_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
.deserialize = NULL,
|
||||
@@ -150,49 +150,57 @@ const SubGhzProtocolEncoder subghz_protocol_land_rover_v0_encoder = {
|
||||
};
|
||||
#endif
|
||||
|
||||
const SubGhzProtocol land_rover_v0_protocol = {
|
||||
.name = LAND_ROVER_PROTOCOL_V0_NAME,
|
||||
const SubGhzProtocol honda_v2_protocol = {
|
||||
.name = HONDA_V2_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
.decoder = &subghz_protocol_land_rover_v0_decoder,
|
||||
.encoder = &subghz_protocol_land_rover_v0_encoder,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_honda_v2_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_honda_v2_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
static bool land_rover_v0_is_short(uint32_t duration) {
|
||||
return pp_is_short(duration, &subghz_protocol_land_rover_v0_const);
|
||||
static bool honda_v2_is_short(uint32_t duration) {
|
||||
return pp_is_short(duration, &subghz_protocol_honda_v2_const);
|
||||
}
|
||||
|
||||
static bool land_rover_v0_is_long(uint32_t duration) {
|
||||
return pp_is_long(duration, &subghz_protocol_land_rover_v0_const);
|
||||
static bool honda_v2_is_long(uint32_t duration) {
|
||||
return pp_is_long(duration, &subghz_protocol_honda_v2_const);
|
||||
}
|
||||
|
||||
static bool land_rover_v0_is_sync(uint32_t duration) {
|
||||
return DURATION_DIFF(duration, LAND_ROVER_V0_SYNC_US) < LAND_ROVER_V0_SYNC_DELTA_US;
|
||||
static bool honda_v2_is_sync(uint32_t duration) {
|
||||
return DURATION_DIFF(duration, HONDA_V2_SYNC_US) < HONDA_V2_SYNC_DELTA_US;
|
||||
}
|
||||
|
||||
static uint8_t land_rover_v0_button_from_signature(uint32_t signature) {
|
||||
if(signature == LAND_ROVER_V0_SIG_UNLOCK) {
|
||||
return LAND_ROVER_V0_BTN_UNLOCK;
|
||||
} else if(signature == LAND_ROVER_V0_SIG_LOCK) {
|
||||
return LAND_ROVER_V0_BTN_LOCK;
|
||||
static uint8_t honda_v2_button_from_signature(uint32_t signature) {
|
||||
if(signature == HONDA_V2_SIG_UNLOCK) {
|
||||
return HONDA_V2_BTN_UNLOCK;
|
||||
} else if(signature == HONDA_V2_SIG_LOCK) {
|
||||
return HONDA_V2_BTN_LOCK;
|
||||
}
|
||||
return LAND_ROVER_V0_BTN_UNKNOWN;
|
||||
return HONDA_V2_BTN_UNKNOWN;
|
||||
}
|
||||
|
||||
static const char* land_rover_v0_button_name(uint8_t button) {
|
||||
static const char* honda_v2_button_name(uint8_t button) {
|
||||
switch(button) {
|
||||
case LAND_ROVER_V0_BTN_LOCK:
|
||||
case HONDA_V2_BTN_LOCK:
|
||||
return "Lock";
|
||||
case LAND_ROVER_V0_BTN_UNLOCK:
|
||||
case HONDA_V2_BTN_UNLOCK:
|
||||
return "Unlock";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t land_rover_v0_calculate_check(uint32_t count) {
|
||||
static uint8_t honda_v2_calculate_check(uint32_t count) {
|
||||
const uint8_t c0 = ((count >> 1) ^ (count >> 2) ^ (count >> 3) ^ (count >> 4) ^ (count >> 6)) &
|
||||
1U;
|
||||
const uint8_t c1 = ((count >> 0) ^ (count >> 2) ^ (count >> 3) ^ (count >> 4) ^ (count >> 5) ^
|
||||
@@ -204,16 +212,16 @@ static uint8_t land_rover_v0_calculate_check(uint32_t count) {
|
||||
return (uint8_t)(c0 | (c1 << 1) | (c2 << 2));
|
||||
}
|
||||
|
||||
static bool land_rover_v0_calculate_tail_msb(uint32_t count) {
|
||||
static bool honda_v2_calculate_tail_msb(uint32_t count) {
|
||||
const uint8_t tail = ((count >> 0) ^ (count >> 2) ^ (count >> 4) ^ (count >> 5)) & 1U;
|
||||
return tail != 0U;
|
||||
}
|
||||
|
||||
static uint16_t land_rover_v0_calculate_tail(uint32_t count) {
|
||||
return land_rover_v0_calculate_tail_msb(count) ? 0xFFFFU : 0x7FFFU;
|
||||
static uint16_t honda_v2_calculate_tail(uint32_t count) {
|
||||
return honda_v2_calculate_tail_msb(count) ? 0xFFFFU : 0x7FFFU;
|
||||
}
|
||||
|
||||
static void land_rover_v0_parse_key_fields(
|
||||
static void honda_v2_parse_key_fields(
|
||||
uint64_t key,
|
||||
uint32_t* signature,
|
||||
uint32_t* serial,
|
||||
@@ -232,11 +240,11 @@ static void land_rover_v0_parse_key_fields(
|
||||
if(signature) *signature = sig;
|
||||
if(serial) *serial = sn;
|
||||
if(count) *count = cnt;
|
||||
if(button) *button = land_rover_v0_button_from_signature(sig);
|
||||
if(button) *button = honda_v2_button_from_signature(sig);
|
||||
if(check) *check = key_bytes[7] & 0x07U;
|
||||
}
|
||||
|
||||
static bool land_rover_v0_validate_frame(
|
||||
static bool honda_v2_validate_frame(
|
||||
uint64_t key,
|
||||
uint16_t tail,
|
||||
bool extra_bit,
|
||||
@@ -246,8 +254,8 @@ static bool land_rover_v0_validate_frame(
|
||||
pp_u64_to_bytes_be(key, key_bytes);
|
||||
|
||||
const uint32_t count = ((uint32_t)key_bytes[6] << 1) | ((key_bytes[7] >> 7) & 1U);
|
||||
const uint8_t expected_check = land_rover_v0_calculate_check(count);
|
||||
const uint16_t expected_tail = land_rover_v0_calculate_tail(count);
|
||||
const uint8_t expected_check = honda_v2_calculate_check(count);
|
||||
const uint16_t expected_tail = honda_v2_calculate_tail(count);
|
||||
|
||||
const bool local_check_ok = ((key_bytes[7] & 0x78U) == 0U) &&
|
||||
((key_bytes[7] & 0x07U) == expected_check);
|
||||
@@ -259,7 +267,7 @@ static bool land_rover_v0_validate_frame(
|
||||
return local_check_ok && local_tail_ok;
|
||||
}
|
||||
|
||||
static bool land_rover_v0_add_decoded_bit(SubGhzProtocolDecoderLandRoverV0* instance, bool bit) {
|
||||
static bool honda_v2_add_decoded_bit(SubGhzProtocolDecoderHondaV2* instance, bool bit) {
|
||||
if(instance->bit_count < 80U) {
|
||||
const uint8_t byte_index = instance->bit_count / 8U;
|
||||
const uint8_t bit_index = 7U - (instance->bit_count % 8U);
|
||||
@@ -276,11 +284,11 @@ static bool land_rover_v0_add_decoded_bit(SubGhzProtocolDecoderLandRoverV0* inst
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool land_rover_v0_finish_frame(SubGhzProtocolDecoderLandRoverV0* instance) {
|
||||
static bool honda_v2_finish_frame(SubGhzProtocolDecoderHondaV2* instance) {
|
||||
const uint64_t key = pp_bytes_to_u64_be(instance->raw);
|
||||
const uint16_t tail = ((uint16_t)instance->raw[8] << 8) | instance->raw[9];
|
||||
|
||||
if(!land_rover_v0_validate_frame(
|
||||
if(!honda_v2_validate_frame(
|
||||
key, tail, instance->extra_bit, &instance->check_ok, &instance->tail_ok)) {
|
||||
return false;
|
||||
}
|
||||
@@ -288,7 +296,7 @@ static bool land_rover_v0_finish_frame(SubGhzProtocolDecoderLandRoverV0* instanc
|
||||
instance->key = key;
|
||||
instance->tail = tail;
|
||||
|
||||
land_rover_v0_parse_key_fields(
|
||||
honda_v2_parse_key_fields(
|
||||
key,
|
||||
&instance->command_signature,
|
||||
&instance->serial,
|
||||
@@ -297,7 +305,7 @@ static bool land_rover_v0_finish_frame(SubGhzProtocolDecoderLandRoverV0* instanc
|
||||
&instance->check);
|
||||
|
||||
instance->generic.data = instance->key;
|
||||
instance->generic.data_count_bit = subghz_protocol_land_rover_v0_const.min_count_bit_for_found;
|
||||
instance->generic.data_count_bit = subghz_protocol_honda_v2_const.min_count_bit_for_found;
|
||||
instance->generic.serial = instance->serial;
|
||||
instance->generic.btn = instance->button;
|
||||
instance->generic.cnt = instance->count;
|
||||
@@ -305,12 +313,12 @@ static bool land_rover_v0_finish_frame(SubGhzProtocolDecoderLandRoverV0* instanc
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool land_rover_v0_process_transition(
|
||||
SubGhzProtocolDecoderLandRoverV0* instance,
|
||||
static bool honda_v2_process_transition(
|
||||
SubGhzProtocolDecoderHondaV2* instance,
|
||||
bool level,
|
||||
uint32_t duration) {
|
||||
if(!instance->boundary_pad_skipped) {
|
||||
if(level && land_rover_v0_is_short(duration)) {
|
||||
if(level && honda_v2_is_short(duration)) {
|
||||
instance->boundary_pad_skipped = true;
|
||||
return true;
|
||||
}
|
||||
@@ -318,31 +326,31 @@ static bool land_rover_v0_process_transition(
|
||||
}
|
||||
|
||||
if(instance->pending_short) {
|
||||
if(!instance->previous_bit && !level && land_rover_v0_is_short(duration)) {
|
||||
if(!instance->previous_bit && !level && honda_v2_is_short(duration)) {
|
||||
instance->pending_short = false;
|
||||
return land_rover_v0_add_decoded_bit(instance, false);
|
||||
} else if(instance->previous_bit && level && land_rover_v0_is_short(duration)) {
|
||||
return honda_v2_add_decoded_bit(instance, false);
|
||||
} else if(instance->previous_bit && level && honda_v2_is_short(duration)) {
|
||||
instance->pending_short = false;
|
||||
return land_rover_v0_add_decoded_bit(instance, true);
|
||||
return honda_v2_add_decoded_bit(instance, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!instance->previous_bit) {
|
||||
if(level && land_rover_v0_is_long(duration)) {
|
||||
if(level && honda_v2_is_long(duration)) {
|
||||
instance->previous_bit = true;
|
||||
return land_rover_v0_add_decoded_bit(instance, true);
|
||||
} else if(level && land_rover_v0_is_short(duration)) {
|
||||
return honda_v2_add_decoded_bit(instance, true);
|
||||
} else if(level && honda_v2_is_short(duration)) {
|
||||
instance->pending_short = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!level && land_rover_v0_is_long(duration)) {
|
||||
if(!level && honda_v2_is_long(duration)) {
|
||||
instance->previous_bit = false;
|
||||
return land_rover_v0_add_decoded_bit(instance, false);
|
||||
} else if(!level && land_rover_v0_is_short(duration)) {
|
||||
return honda_v2_add_decoded_bit(instance, false);
|
||||
} else if(!level && honda_v2_is_short(duration)) {
|
||||
instance->pending_short = true;
|
||||
return true;
|
||||
}
|
||||
@@ -350,43 +358,43 @@ static bool land_rover_v0_process_transition(
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
static bool land_rover_v0_encoder_add_level(
|
||||
SubGhzProtocolEncoderLandRoverV0* instance,
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static bool honda_v2_encoder_add_level(
|
||||
SubGhzProtocolEncoderHondaV2* instance,
|
||||
size_t* index,
|
||||
bool level,
|
||||
uint32_t duration) {
|
||||
if(*index >= LAND_ROVER_V0_UPLOAD_CAPACITY) {
|
||||
if(*index >= HONDA_V2_UPLOAD_CAPACITY) {
|
||||
return false;
|
||||
}
|
||||
instance->encoder.upload[(*index)++] = level_duration_make(level, duration);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool land_rover_v0_encoder_add_bit(
|
||||
SubGhzProtocolEncoderLandRoverV0* instance,
|
||||
static bool honda_v2_encoder_add_bit(
|
||||
SubGhzProtocolEncoderHondaV2* instance,
|
||||
size_t* index,
|
||||
bool* previous_bit,
|
||||
bool bit) {
|
||||
const uint32_t te_short = subghz_protocol_land_rover_v0_const.te_short;
|
||||
const uint32_t te_long = subghz_protocol_land_rover_v0_const.te_long;
|
||||
const uint32_t te_short = subghz_protocol_honda_v2_const.te_short;
|
||||
const uint32_t te_long = subghz_protocol_honda_v2_const.te_long;
|
||||
|
||||
if(!*previous_bit && !bit) {
|
||||
if(!land_rover_v0_encoder_add_level(instance, index, true, te_short) ||
|
||||
!land_rover_v0_encoder_add_level(instance, index, false, te_short)) {
|
||||
if(!honda_v2_encoder_add_level(instance, index, true, te_short) ||
|
||||
!honda_v2_encoder_add_level(instance, index, false, te_short)) {
|
||||
return false;
|
||||
}
|
||||
} else if(!*previous_bit && bit) {
|
||||
if(!land_rover_v0_encoder_add_level(instance, index, true, te_long)) {
|
||||
if(!honda_v2_encoder_add_level(instance, index, true, te_long)) {
|
||||
return false;
|
||||
}
|
||||
} else if(*previous_bit && !bit) {
|
||||
if(!land_rover_v0_encoder_add_level(instance, index, false, te_long)) {
|
||||
if(!honda_v2_encoder_add_level(instance, index, false, te_long)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(!land_rover_v0_encoder_add_level(instance, index, false, te_short) ||
|
||||
!land_rover_v0_encoder_add_level(instance, index, true, te_short)) {
|
||||
if(!honda_v2_encoder_add_level(instance, index, false, te_short) ||
|
||||
!honda_v2_encoder_add_level(instance, index, true, te_short)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -395,30 +403,30 @@ static bool land_rover_v0_encoder_add_bit(
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool land_rover_v0_build_upload(SubGhzProtocolEncoderLandRoverV0* instance) {
|
||||
static bool honda_v2_build_upload(SubGhzProtocolEncoderHondaV2* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
size_t index = 0;
|
||||
const uint32_t te_short = subghz_protocol_land_rover_v0_const.te_short;
|
||||
const uint32_t te_short = subghz_protocol_honda_v2_const.te_short;
|
||||
|
||||
uint8_t key_bytes[8];
|
||||
pp_u64_to_bytes_be(instance->key, key_bytes);
|
||||
|
||||
for(uint16_t i = 0; i < LAND_ROVER_V0_PREAMBLE_PAIRS; i++) {
|
||||
if(!land_rover_v0_encoder_add_level(instance, &index, true, te_short) ||
|
||||
!land_rover_v0_encoder_add_level(instance, &index, false, te_short)) {
|
||||
for(uint16_t i = 0; i < HONDA_V2_PREAMBLE_PAIRS; i++) {
|
||||
if(!honda_v2_encoder_add_level(instance, &index, true, te_short) ||
|
||||
!honda_v2_encoder_add_level(instance, &index, false, te_short)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!land_rover_v0_encoder_add_level(instance, &index, true, LAND_ROVER_V0_SYNC_US) ||
|
||||
!land_rover_v0_encoder_add_level(instance, &index, false, LAND_ROVER_V0_SYNC_US) ||
|
||||
!land_rover_v0_encoder_add_level(instance, &index, true, te_short)) {
|
||||
if(!honda_v2_encoder_add_level(instance, &index, true, HONDA_V2_SYNC_US) ||
|
||||
!honda_v2_encoder_add_level(instance, &index, false, HONDA_V2_SYNC_US) ||
|
||||
!honda_v2_encoder_add_level(instance, &index, true, te_short)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool previous_bit = true;
|
||||
if(!land_rover_v0_encoder_add_bit(instance, &index, &previous_bit, false)) {
|
||||
if(!honda_v2_encoder_add_bit(instance, &index, &previous_bit, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -426,24 +434,24 @@ static bool land_rover_v0_build_upload(SubGhzProtocolEncoderLandRoverV0* instanc
|
||||
const uint8_t byte_index = bit_index / 8U;
|
||||
const uint8_t bit_in_byte = 7U - (bit_index % 8U);
|
||||
const bool bit = (key_bytes[byte_index] >> bit_in_byte) & 1U;
|
||||
if(!land_rover_v0_encoder_add_bit(instance, &index, &previous_bit, bit)) {
|
||||
if(!honda_v2_encoder_add_bit(instance, &index, &previous_bit, bit)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
instance->tail = land_rover_v0_calculate_tail(instance->count);
|
||||
instance->tail = honda_v2_calculate_tail(instance->count);
|
||||
for(uint8_t bit_index = 0; bit_index < 16; bit_index++) {
|
||||
const bool bit = (instance->tail >> (15U - bit_index)) & 1U;
|
||||
if(!land_rover_v0_encoder_add_bit(instance, &index, &previous_bit, bit)) {
|
||||
if(!honda_v2_encoder_add_bit(instance, &index, &previous_bit, bit)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!land_rover_v0_encoder_add_bit(instance, &index, &previous_bit, true)) {
|
||||
if(!honda_v2_encoder_add_bit(instance, &index, &previous_bit, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!land_rover_v0_encoder_add_level(instance, &index, false, LAND_ROVER_V0_GAP_US)) {
|
||||
if(!honda_v2_encoder_add_level(instance, &index, false, HONDA_V2_GAP_US)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -453,29 +461,29 @@ static bool land_rover_v0_build_upload(SubGhzProtocolEncoderLandRoverV0* instanc
|
||||
}
|
||||
#endif
|
||||
|
||||
void* subghz_protocol_decoder_land_rover_v0_alloc(SubGhzEnvironment* environment) {
|
||||
void* subghz_protocol_decoder_honda_v2_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderLandRoverV0* instance =
|
||||
calloc(1, sizeof(SubGhzProtocolDecoderLandRoverV0));
|
||||
SubGhzProtocolDecoderHondaV2* instance =
|
||||
calloc(1, sizeof(SubGhzProtocolDecoderHondaV2));
|
||||
furi_check(instance);
|
||||
|
||||
instance->base.protocol = &land_rover_v0_protocol;
|
||||
instance->base.protocol = &honda_v2_protocol;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_land_rover_v0_free(void* context) {
|
||||
void subghz_protocol_decoder_honda_v2_free(void* context) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderLandRoverV0* instance = context;
|
||||
SubGhzProtocolDecoderHondaV2* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_land_rover_v0_reset(void* context) {
|
||||
void subghz_protocol_decoder_honda_v2_reset(void* context) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderLandRoverV0* instance = context;
|
||||
SubGhzProtocolDecoderHondaV2* instance = context;
|
||||
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepReset;
|
||||
instance->decoder.parser_step = HondaV2DecoderStepReset;
|
||||
instance->decoder.te_last = 0;
|
||||
instance->preamble_count = 0;
|
||||
memset(instance->raw, 0, sizeof(instance->raw));
|
||||
@@ -486,65 +494,65 @@ void subghz_protocol_decoder_land_rover_v0_reset(void* context) {
|
||||
instance->pending_short = false;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_land_rover_v0_feed(void* context, bool level, uint32_t duration) {
|
||||
void subghz_protocol_decoder_honda_v2_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderLandRoverV0* instance = context;
|
||||
SubGhzProtocolDecoderHondaV2* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case LandRoverV0DecoderStepReset:
|
||||
if(level && land_rover_v0_is_short(duration)) {
|
||||
case HondaV2DecoderStepReset:
|
||||
if(level && honda_v2_is_short(duration)) {
|
||||
instance->preamble_count = 0;
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepPreambleLow;
|
||||
instance->decoder.parser_step = HondaV2DecoderStepPreambleLow;
|
||||
}
|
||||
break;
|
||||
|
||||
case LandRoverV0DecoderStepPreambleLow:
|
||||
if(!level && land_rover_v0_is_short(duration)) {
|
||||
case HondaV2DecoderStepPreambleLow:
|
||||
if(!level && honda_v2_is_short(duration)) {
|
||||
instance->preamble_count++;
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepPreambleHigh;
|
||||
instance->decoder.parser_step = HondaV2DecoderStepPreambleHigh;
|
||||
} else {
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepReset;
|
||||
instance->decoder.parser_step = HondaV2DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case LandRoverV0DecoderStepPreambleHigh:
|
||||
if(level && land_rover_v0_is_short(duration)) {
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepPreambleLow;
|
||||
case HondaV2DecoderStepPreambleHigh:
|
||||
if(level && honda_v2_is_short(duration)) {
|
||||
instance->decoder.parser_step = HondaV2DecoderStepPreambleLow;
|
||||
} else if(
|
||||
level && land_rover_v0_is_sync(duration) &&
|
||||
instance->preamble_count >= LAND_ROVER_V0_MIN_PREAMBLE_PAIRS) {
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepSyncLow;
|
||||
level && honda_v2_is_sync(duration) &&
|
||||
instance->preamble_count >= HONDA_V2_MIN_PREAMBLE_PAIRS) {
|
||||
instance->decoder.parser_step = HondaV2DecoderStepSyncLow;
|
||||
} else {
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepReset;
|
||||
instance->decoder.parser_step = HondaV2DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case LandRoverV0DecoderStepSyncLow:
|
||||
if(!level && land_rover_v0_is_sync(duration)) {
|
||||
case HondaV2DecoderStepSyncLow:
|
||||
if(!level && honda_v2_is_sync(duration)) {
|
||||
memset(instance->raw, 0, sizeof(instance->raw));
|
||||
instance->bit_count = 0;
|
||||
instance->extra_bit = false;
|
||||
instance->previous_bit = true;
|
||||
instance->boundary_pad_skipped = false;
|
||||
instance->pending_short = false;
|
||||
land_rover_v0_add_decoded_bit(instance, true);
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepData;
|
||||
honda_v2_add_decoded_bit(instance, true);
|
||||
instance->decoder.parser_step = HondaV2DecoderStepData;
|
||||
} else {
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepReset;
|
||||
instance->decoder.parser_step = HondaV2DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case LandRoverV0DecoderStepData:
|
||||
if(!land_rover_v0_process_transition(instance, level, duration)) {
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepReset;
|
||||
case HondaV2DecoderStepData:
|
||||
if(!honda_v2_process_transition(instance, level, duration)) {
|
||||
instance->decoder.parser_step = HondaV2DecoderStepReset;
|
||||
break;
|
||||
}
|
||||
|
||||
if(instance->bit_count == subghz_protocol_land_rover_v0_const.min_count_bit_for_found) {
|
||||
if(land_rover_v0_finish_frame(instance) && instance->base.callback) {
|
||||
if(instance->bit_count == subghz_protocol_honda_v2_const.min_count_bit_for_found) {
|
||||
if(honda_v2_finish_frame(instance) && instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
instance->decoder.parser_step = LandRoverV0DecoderStepReset;
|
||||
instance->decoder.parser_step = HondaV2DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -552,9 +560,9 @@ void subghz_protocol_decoder_land_rover_v0_feed(void* context, bool level, uint3
|
||||
instance->decoder.te_last = duration;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_land_rover_v0_get_hash_data(void* context) {
|
||||
uint8_t subghz_protocol_decoder_honda_v2_get_hash_data(void* context) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderLandRoverV0* instance = context;
|
||||
SubGhzProtocolDecoderHondaV2* instance = context;
|
||||
|
||||
SubGhzBlockDecoder decoder = {
|
||||
.decode_data = instance->key,
|
||||
@@ -567,12 +575,12 @@ uint8_t subghz_protocol_decoder_land_rover_v0_get_hash_data(void* context) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_land_rover_v0_serialize(
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_v2_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderLandRoverV0* instance = context;
|
||||
SubGhzProtocolDecoderHondaV2* instance = context;
|
||||
|
||||
SubGhzProtocolStatus ret =
|
||||
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
@@ -585,27 +593,27 @@ SubGhzProtocolStatus subghz_protocol_decoder_land_rover_v0_serialize(
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_SERIAL, instance->serial);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_BTN, instance->button);
|
||||
pp_flipper_update_or_insert_u32(
|
||||
flipper_format, LAND_ROVER_V0_FF_BTNSIG, instance->command_signature);
|
||||
flipper_format, HONDA_V2_FF_BTNSIG, instance->command_signature);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_CNT, instance->count);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, LAND_ROVER_V0_FF_CHECK, instance->check);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, LAND_ROVER_V0_FF_TAIL, instance->tail);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, HONDA_V2_FF_CHECK, instance->check);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, HONDA_V2_FF_TAIL, instance->tail);
|
||||
pp_flipper_update_or_insert_u32(
|
||||
flipper_format, LAND_ROVER_V0_FF_EXTRA_BIT, instance->extra_bit ? 1U : 0U);
|
||||
flipper_format, HONDA_V2_FF_EXTRA_BIT, instance->extra_bit ? 1U : 0U);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_land_rover_v0_deserialize(
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_v2_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderLandRoverV0* instance = context;
|
||||
SubGhzProtocolDecoderHondaV2* instance = context;
|
||||
|
||||
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic,
|
||||
flipper_format,
|
||||
subghz_protocol_land_rover_v0_const.min_count_bit_for_found);
|
||||
subghz_protocol_honda_v2_const.min_count_bit_for_found);
|
||||
|
||||
if(ret == SubGhzProtocolStatusOk) {
|
||||
uint8_t key_bytes[8] = {0};
|
||||
@@ -624,27 +632,27 @@ SubGhzProtocolStatus subghz_protocol_decoder_land_rover_v0_deserialize(
|
||||
|
||||
uint32_t temp = 0;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, LAND_ROVER_V0_FF_TAIL, &temp, 1)) {
|
||||
if(flipper_format_read_uint32(flipper_format, HONDA_V2_FF_TAIL, &temp, 1)) {
|
||||
instance->tail = temp & 0xFFFFU;
|
||||
} else {
|
||||
const uint32_t count = ((uint32_t)key_bytes[6] << 1) | ((key_bytes[7] >> 7) & 1U);
|
||||
instance->tail = land_rover_v0_calculate_tail(count);
|
||||
instance->tail = honda_v2_calculate_tail(count);
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, LAND_ROVER_V0_FF_EXTRA_BIT, &temp, 1)) {
|
||||
if(flipper_format_read_uint32(flipper_format, HONDA_V2_FF_EXTRA_BIT, &temp, 1)) {
|
||||
instance->extra_bit = (temp & 1U) != 0;
|
||||
} else {
|
||||
instance->extra_bit = true;
|
||||
}
|
||||
|
||||
land_rover_v0_validate_frame(
|
||||
honda_v2_validate_frame(
|
||||
instance->key,
|
||||
instance->tail,
|
||||
instance->extra_bit,
|
||||
&instance->check_ok,
|
||||
&instance->tail_ok);
|
||||
land_rover_v0_parse_key_fields(
|
||||
honda_v2_parse_key_fields(
|
||||
instance->key,
|
||||
&instance->command_signature,
|
||||
&instance->serial,
|
||||
@@ -654,7 +662,7 @@ SubGhzProtocolStatus subghz_protocol_decoder_land_rover_v0_deserialize(
|
||||
|
||||
instance->generic.data = instance->key;
|
||||
instance->generic.data_count_bit =
|
||||
subghz_protocol_land_rover_v0_const.min_count_bit_for_found;
|
||||
subghz_protocol_honda_v2_const.min_count_bit_for_found;
|
||||
instance->generic.serial = instance->serial;
|
||||
instance->generic.btn = instance->button;
|
||||
instance->generic.cnt = instance->count;
|
||||
@@ -663,9 +671,9 @@ SubGhzProtocolStatus subghz_protocol_decoder_land_rover_v0_deserialize(
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_land_rover_v0_get_string(void* context, FuriString* output) {
|
||||
void subghz_protocol_decoder_honda_v2_get_string(void* context, FuriString* output) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderLandRoverV0* instance = context;
|
||||
SubGhzProtocolDecoderHondaV2* instance = context;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
@@ -679,7 +687,7 @@ void subghz_protocol_decoder_land_rover_v0_get_string(void* context, FuriString*
|
||||
(unsigned long long)instance->key,
|
||||
(unsigned long)instance->serial,
|
||||
instance->button,
|
||||
land_rover_v0_button_name(instance->button),
|
||||
honda_v2_button_name(instance->button),
|
||||
(unsigned long)instance->command_signature,
|
||||
(unsigned long)instance->count,
|
||||
instance->check,
|
||||
@@ -688,20 +696,20 @@ void subghz_protocol_decoder_land_rover_v0_get_string(void* context, FuriString*
|
||||
instance->tail_ok ? "OK" : "BAD");
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static uint32_t land_rover_v0_signature_from_button(uint8_t button) {
|
||||
static uint32_t honda_v2_signature_from_button(uint8_t button) {
|
||||
switch(button) {
|
||||
case LAND_ROVER_V0_BTN_LOCK:
|
||||
return LAND_ROVER_V0_SIG_LOCK;
|
||||
case LAND_ROVER_V0_BTN_UNLOCK:
|
||||
return LAND_ROVER_V0_SIG_UNLOCK;
|
||||
case HONDA_V2_BTN_LOCK:
|
||||
return HONDA_V2_SIG_LOCK;
|
||||
case HONDA_V2_BTN_UNLOCK:
|
||||
return HONDA_V2_SIG_UNLOCK;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t land_rover_v0_build_key(uint32_t signature, uint32_t serial, uint32_t count) {
|
||||
static uint64_t honda_v2_build_key(uint32_t signature, uint32_t serial, uint32_t count) {
|
||||
uint8_t key_bytes[8] = {0};
|
||||
key_bytes[0] = (uint8_t)((signature >> 16) & 0xFFU);
|
||||
key_bytes[1] = (uint8_t)((signature >> 8) & 0xFFU);
|
||||
@@ -712,39 +720,39 @@ static uint64_t land_rover_v0_build_key(uint32_t signature, uint32_t serial, uin
|
||||
key_bytes[6] = (uint8_t)((count >> 1) & 0xFFU);
|
||||
|
||||
const bool counter_lsb = (count & 1U) != 0;
|
||||
const uint8_t check = land_rover_v0_calculate_check(count);
|
||||
const uint8_t check = honda_v2_calculate_check(count);
|
||||
key_bytes[7] = (counter_lsb ? 0x80U : 0x00U) | check;
|
||||
|
||||
return pp_bytes_to_u64_be(key_bytes);
|
||||
}
|
||||
|
||||
void* subghz_protocol_encoder_land_rover_v0_alloc(SubGhzEnvironment* environment) {
|
||||
void* subghz_protocol_encoder_honda_v2_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderLandRoverV0* instance =
|
||||
calloc(1, sizeof(SubGhzProtocolEncoderLandRoverV0));
|
||||
SubGhzProtocolEncoderHondaV2* instance =
|
||||
calloc(1, sizeof(SubGhzProtocolEncoderHondaV2));
|
||||
furi_check(instance);
|
||||
|
||||
instance->base.protocol = &land_rover_v0_protocol;
|
||||
instance->base.protocol = &honda_v2_protocol;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 0;
|
||||
instance->encoder.front = 0;
|
||||
instance->encoder.is_running = false;
|
||||
pp_encoder_buffer_ensure(instance, LAND_ROVER_V0_UPLOAD_CAPACITY);
|
||||
pp_encoder_buffer_ensure(instance, HONDA_V2_UPLOAD_CAPACITY);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_land_rover_v0_free(void* context) {
|
||||
void subghz_protocol_encoder_honda_v2_free(void* context) {
|
||||
furi_check(context);
|
||||
pp_encoder_free(context);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_encoder_land_rover_v0_deserialize(
|
||||
SubGhzProtocolStatus subghz_protocol_encoder_honda_v2_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolEncoderLandRoverV0* instance = context;
|
||||
SubGhzProtocolEncoderHondaV2* instance = context;
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
|
||||
instance->encoder.is_running = false;
|
||||
@@ -760,7 +768,7 @@ SubGhzProtocolStatus subghz_protocol_encoder_land_rover_v0_deserialize(
|
||||
SubGhzProtocolStatus load_status = subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic,
|
||||
flipper_format,
|
||||
subghz_protocol_land_rover_v0_const.min_count_bit_for_found);
|
||||
subghz_protocol_honda_v2_const.min_count_bit_for_found);
|
||||
if(load_status != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
@@ -778,7 +786,7 @@ SubGhzProtocolStatus subghz_protocol_encoder_land_rover_v0_deserialize(
|
||||
}
|
||||
|
||||
if(have_key) {
|
||||
land_rover_v0_parse_key_fields(
|
||||
honda_v2_parse_key_fields(
|
||||
instance->key,
|
||||
&instance->command_signature,
|
||||
&instance->serial,
|
||||
@@ -807,12 +815,12 @@ SubGhzProtocolStatus subghz_protocol_encoder_land_rover_v0_deserialize(
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, LAND_ROVER_V0_FF_BTNSIG, &u32, 1)) {
|
||||
if(flipper_format_read_uint32(flipper_format, HONDA_V2_FF_BTNSIG, &u32, 1)) {
|
||||
instance->command_signature = u32 & 0xFFFFFFU;
|
||||
}
|
||||
|
||||
if(have_button) {
|
||||
const uint32_t signature = land_rover_v0_signature_from_button(instance->button);
|
||||
const uint32_t signature = honda_v2_signature_from_button(instance->button);
|
||||
if(signature != 0U) {
|
||||
instance->command_signature = signature;
|
||||
}
|
||||
@@ -822,13 +830,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_land_rover_v0_deserialize(
|
||||
break;
|
||||
}
|
||||
|
||||
instance->key = land_rover_v0_build_key(
|
||||
instance->key = honda_v2_build_key(
|
||||
instance->command_signature, instance->serial, instance->count);
|
||||
|
||||
pp_u64_to_bytes_be(instance->key, key_bytes);
|
||||
instance->tail = land_rover_v0_calculate_tail(instance->count);
|
||||
instance->tail = honda_v2_calculate_tail(instance->count);
|
||||
|
||||
land_rover_v0_parse_key_fields(
|
||||
honda_v2_parse_key_fields(
|
||||
instance->key,
|
||||
&instance->command_signature,
|
||||
&instance->serial,
|
||||
@@ -838,7 +846,7 @@ SubGhzProtocolStatus subghz_protocol_encoder_land_rover_v0_deserialize(
|
||||
|
||||
instance->generic.data = instance->key;
|
||||
instance->generic.data_count_bit =
|
||||
subghz_protocol_land_rover_v0_const.min_count_bit_for_found;
|
||||
subghz_protocol_honda_v2_const.min_count_bit_for_found;
|
||||
instance->generic.serial = instance->serial;
|
||||
instance->generic.btn = instance->button;
|
||||
instance->generic.cnt = instance->count;
|
||||
@@ -846,7 +854,7 @@ SubGhzProtocolStatus subghz_protocol_encoder_land_rover_v0_deserialize(
|
||||
flipper_format_rewind(flipper_format);
|
||||
instance->encoder.repeat = pp_encoder_read_repeat(flipper_format, 10);
|
||||
|
||||
if(!land_rover_v0_build_upload(instance) || instance->encoder.size_upload == 0U) {
|
||||
if(!honda_v2_build_upload(instance) || instance->encoder.size_upload == 0U) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -855,11 +863,11 @@ SubGhzProtocolStatus subghz_protocol_encoder_land_rover_v0_deserialize(
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_SERIAL, instance->serial);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_BTN, instance->button);
|
||||
pp_flipper_update_or_insert_u32(
|
||||
flipper_format, LAND_ROVER_V0_FF_BTNSIG, instance->command_signature);
|
||||
flipper_format, HONDA_V2_FF_BTNSIG, instance->command_signature);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, FF_CNT, instance->count);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, LAND_ROVER_V0_FF_CHECK, instance->check);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, LAND_ROVER_V0_FF_TAIL, instance->tail);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, LAND_ROVER_V0_FF_EXTRA_BIT, 1U);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, HONDA_V2_FF_CHECK, instance->check);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, HONDA_V2_FF_TAIL, instance->tail);
|
||||
pp_flipper_update_or_insert_u32(flipper_format, HONDA_V2_FF_EXTRA_BIT, 1U);
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
@@ -868,11 +876,11 @@ SubGhzProtocolStatus subghz_protocol_encoder_land_rover_v0_deserialize(
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_land_rover_v0_stop(void* context) {
|
||||
void subghz_protocol_encoder_honda_v2_stop(void* context) {
|
||||
pp_encoder_stop(context);
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_land_rover_v0_yield(void* context) {
|
||||
LevelDuration subghz_protocol_encoder_honda_v2_yield(void* context) {
|
||||
return pp_encoder_yield(context);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#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/level_duration.h>
|
||||
|
||||
#include "../defines.h"
|
||||
|
||||
#define HONDA_V2_PROTOCOL_NAME "Honda V2"
|
||||
|
||||
extern const SubGhzProtocol honda_v2_protocol;
|
||||
|
||||
void* subghz_protocol_decoder_honda_v2_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_honda_v2_free(void* context);
|
||||
void subghz_protocol_decoder_honda_v2_reset(void* context);
|
||||
void subghz_protocol_decoder_honda_v2_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_honda_v2_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_v2_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_honda_v2_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_honda_v2_get_string(void* context, FuriString* output);
|
||||
|
||||
void* subghz_protocol_encoder_honda_v2_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_encoder_honda_v2_free(void* context);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_honda_v2_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_encoder_honda_v2_stop(void* context);
|
||||
LevelDuration subghz_protocol_encoder_honda_v2_yield(void* context);
|
||||
@@ -10,26 +10,63 @@ uint64_t kia_v6_a_key = 0;
|
||||
uint64_t kia_v6_b_key = 0;
|
||||
uint64_t kia_v5_key = 0;
|
||||
|
||||
void protopirate_keys_load(SubGhzEnvironment* environment) {
|
||||
static bool kia_mf_key_loaded = false;
|
||||
static bool kia_v6_a_key_loaded = false;
|
||||
static bool kia_v6_b_key_loaded = false;
|
||||
static bool kia_v5_key_loaded = false;
|
||||
|
||||
static void protopirate_keys_reset(void) {
|
||||
kia_mf_key = 0;
|
||||
kia_v6_a_key = 0;
|
||||
kia_v6_b_key = 0;
|
||||
kia_v5_key = 0;
|
||||
kia_mf_key_loaded = false;
|
||||
kia_v6_a_key_loaded = false;
|
||||
kia_v6_b_key_loaded = false;
|
||||
kia_v5_key_loaded = false;
|
||||
}
|
||||
|
||||
bool protopirate_keys_load(SubGhzEnvironment* environment) {
|
||||
protopirate_keys_reset();
|
||||
if(!environment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
case KIA_KEY4:
|
||||
kia_v5_key = manufacture_code->key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!keystore) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SubGhzKeyArray_t* key_data = subghz_keystore_get_data(keystore);
|
||||
if(!key_data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for
|
||||
M_EACH(manufacture_code, *key_data, SubGhzKeyArray_t) {
|
||||
switch(manufacture_code->type) {
|
||||
case KIA_KEY1:
|
||||
kia_mf_key = manufacture_code->key;
|
||||
kia_mf_key_loaded = true;
|
||||
break;
|
||||
case KIA_KEY2:
|
||||
kia_v6_a_key = manufacture_code->key;
|
||||
kia_v6_a_key_loaded = true;
|
||||
break;
|
||||
case KIA_KEY3:
|
||||
kia_v6_b_key = manufacture_code->key;
|
||||
kia_v6_b_key_loaded = true;
|
||||
break;
|
||||
case KIA_KEY4:
|
||||
kia_v5_key = manufacture_code->key;
|
||||
kia_v5_key_loaded = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return kia_mf_key_loaded || kia_v6_a_key_loaded || kia_v6_b_key_loaded || kia_v5_key_loaded;
|
||||
}
|
||||
|
||||
uint64_t get_kia_mf_key() {
|
||||
@@ -47,3 +84,19 @@ uint64_t get_kia_v6_keystore_b() {
|
||||
uint64_t get_kia_v5_key() {
|
||||
return kia_v5_key;
|
||||
}
|
||||
|
||||
bool protopirate_keys_has_kia_mf_key(void) {
|
||||
return kia_mf_key_loaded;
|
||||
}
|
||||
|
||||
bool protopirate_keys_has_kia_v6_keystore_a(void) {
|
||||
return kia_v6_a_key_loaded;
|
||||
}
|
||||
|
||||
bool protopirate_keys_has_kia_v6_keystore_b(void) {
|
||||
return kia_v6_b_key_loaded;
|
||||
}
|
||||
|
||||
bool protopirate_keys_has_kia_v5_key(void) {
|
||||
return kia_v5_key_loaded;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lib/subghz/environment.h>
|
||||
@@ -18,4 +19,9 @@ uint64_t get_kia_v6_keystore_b();
|
||||
|
||||
uint64_t get_kia_v5_key();
|
||||
|
||||
void protopirate_keys_load(SubGhzEnvironment* environment);
|
||||
bool protopirate_keys_has_kia_mf_key(void);
|
||||
bool protopirate_keys_has_kia_v6_keystore_a(void);
|
||||
bool protopirate_keys_has_kia_v6_keystore_b(void);
|
||||
bool protopirate_keys_has_kia_v5_key(void);
|
||||
|
||||
bool protopirate_keys_load(SubGhzEnvironment* environment);
|
||||
|
||||
@@ -458,7 +458,7 @@ static bool kia_v0_decoder_try_honda(SubGhzProtocolDecoderKIA* instance) {
|
||||
kia_v0_decoder_commit(instance, key, KIA_V0_TYPE_HONDA, KIA_V0_BIT_COUNT_HONDA);
|
||||
return true;
|
||||
}
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static size_t kia_v0_append_short_pairs(LevelDuration* upload, size_t index, size_t count) {
|
||||
return pp_emit_short_pairs(
|
||||
@@ -466,7 +466,7 @@ static size_t kia_v0_append_short_pairs(LevelDuration* upload, size_t index, siz
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static size_t kia_v0_append_data_pairs(
|
||||
LevelDuration* upload,
|
||||
@@ -484,7 +484,7 @@ static size_t kia_v0_append_data_pairs(
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void kia_v0_build_honda_upload(SubGhzProtocolEncoderKIA* instance, uint64_t raw) {
|
||||
size_t index = 0;
|
||||
@@ -507,7 +507,7 @@ static void kia_v0_build_honda_upload(SubGhzProtocolEncoderKIA* instance, uint64
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void kia_v0_build_kia_upload(SubGhzProtocolEncoderKIA* instance, uint64_t raw) {
|
||||
size_t index = 0;
|
||||
@@ -528,7 +528,7 @@ static void kia_v0_build_kia_upload(SubGhzProtocolEncoderKIA* instance, uint64_t
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void kia_v0_build_suzuki_upload(SubGhzProtocolEncoderKIA* instance, uint64_t shifted) {
|
||||
size_t index = 0;
|
||||
@@ -557,7 +557,7 @@ static uint8_t kia_v0_infer_type_from_bits(uint32_t bits) {
|
||||
if(bits == KIA_V0_BIT_COUNT_HONDA) return KIA_V0_TYPE_HONDA;
|
||||
return KIA_V0_TYPE_KIA;
|
||||
}
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void kia_v0_encoder_apply_fields(SubGhzProtocolEncoderKIA* instance) {
|
||||
instance->generic.serial = instance->fields.serial;
|
||||
@@ -611,7 +611,7 @@ static void kia_v0_encoder_apply_fields(SubGhzProtocolEncoderKIA* instance) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void kia_v0_encoder_sync_from_generic(SubGhzProtocolEncoderKIA* instance) {
|
||||
instance->fields.serial = instance->generic.serial;
|
||||
@@ -625,7 +625,7 @@ static void kia_v0_encoder_sync_from_generic(SubGhzProtocolEncoderKIA* instance)
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void kia_v0_encoder_apply_flipper_fields(
|
||||
SubGhzProtocolEncoderKIA* instance,
|
||||
@@ -668,7 +668,7 @@ static void kia_v0_encoder_apply_flipper_fields(
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void* subghz_protocol_encoder_kia_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
@@ -686,7 +686,7 @@ void* subghz_protocol_encoder_kia_alloc(SubGhzEnvironment* environment) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_kia_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
@@ -764,7 +764,7 @@ SubGhzProtocolStatus
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void subghz_protocol_encoder_kia_set_button(void* context, uint8_t button) {
|
||||
furi_check(context);
|
||||
@@ -778,7 +778,7 @@ void subghz_protocol_encoder_kia_set_button(void* context, uint8_t button) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void subghz_protocol_encoder_kia_set_counter(void* context, uint16_t counter) {
|
||||
furi_check(context);
|
||||
@@ -788,7 +788,7 @@ void subghz_protocol_encoder_kia_set_counter(void* context, uint16_t counter) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void subghz_protocol_encoder_kia_increment_counter(void* context) {
|
||||
furi_check(context);
|
||||
@@ -798,7 +798,7 @@ void subghz_protocol_encoder_kia_increment_counter(void* context) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
uint16_t subghz_protocol_encoder_kia_get_counter(void* context) {
|
||||
furi_check(context);
|
||||
@@ -807,7 +807,7 @@ uint16_t subghz_protocol_encoder_kia_get_counter(void* context) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
uint8_t subghz_protocol_encoder_kia_get_button(void* context) {
|
||||
furi_check(context);
|
||||
@@ -1106,7 +1106,7 @@ const SubGhzProtocolDecoder subghz_protocol_kia_decoder = {
|
||||
.get_string = subghz_protocol_decoder_kia_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_kia_encoder = {
|
||||
.alloc = subghz_protocol_encoder_kia_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -1130,6 +1130,14 @@ const SubGhzProtocol kia_protocol_v0 = {
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_kia_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_kia_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -60,7 +60,7 @@ const SubGhzProtocolDecoder kia_protocol_v1_decoder = {
|
||||
.get_string = kia_protocol_decoder_v1_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder kia_protocol_v1_encoder = {
|
||||
.alloc = kia_protocol_encoder_v1_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -86,8 +86,20 @@ const SubGhzProtocol kia_protocol_v1 = {
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
|
||||
.decoder = &kia_protocol_v1_decoder,
|
||||
|
||||
#else
|
||||
|
||||
.decoder = NULL,
|
||||
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &kia_protocol_v1_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void kia_v1_check_remote_controller(SubGhzProtocolDecoderKiaV1* instance);
|
||||
@@ -144,10 +156,13 @@ static const char* kia_v1_get_button_name(uint8_t btn) {
|
||||
}
|
||||
return name;
|
||||
}
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* kia_protocol_encoder_v1_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderKiaV1* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV1));
|
||||
SubGhzProtocolEncoderKiaV1* instance = calloc(1, sizeof(SubGhzProtocolEncoderKiaV1));
|
||||
if(!instance) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
instance->base.protocol = &kia_protocol_v1;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
@@ -161,12 +176,14 @@ void* kia_protocol_encoder_v1_alloc(SubGhzEnvironment* environment) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void kia_protocol_encoder_v1_get_upload(SubGhzProtocolEncoderKiaV1* instance) {
|
||||
furi_check(instance);
|
||||
if(instance->encoder.upload == NULL) return; // lazy buffer not yet allocated
|
||||
size_t index = 0;
|
||||
LevelDuration* up = instance->encoder.upload;
|
||||
const size_t cap = KIA_V1_UPLOAD_CAPACITY;
|
||||
|
||||
uint8_t cnt_high = (instance->generic.cnt >> 8) & 0xF;
|
||||
uint8_t char_data[7];
|
||||
@@ -184,34 +201,25 @@ static void kia_protocol_encoder_v1_get_upload(SubGhzProtocolEncoderKiaV1* insta
|
||||
instance->generic.btn << 16 | (instance->generic.cnt & 0xFF) << 8 |
|
||||
((instance->generic.cnt >> 8) & 0xF) << 4 | crc;
|
||||
|
||||
const uint32_t te_short = (uint32_t)kia_protocol_v1_const.te_short;
|
||||
const uint32_t te_long = (uint32_t)kia_protocol_v1_const.te_long;
|
||||
|
||||
for(uint8_t burst = 0; burst < KIA_V1_TOTAL_BURSTS; burst++) {
|
||||
if(burst > 0) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, KIA_V1_INTER_BURST_GAP_US);
|
||||
index = pp_emit(up, index, cap, false, KIA_V1_INTER_BURST_GAP_US);
|
||||
}
|
||||
|
||||
for(int i = 0; i < KIA_V1_HEADER_PULSES; i++) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)kia_protocol_v1_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)kia_protocol_v1_const.te_long);
|
||||
index = pp_emit(up, index, cap, false, te_long);
|
||||
index = pp_emit(up, index, cap, true, te_long);
|
||||
}
|
||||
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)kia_protocol_v1_const.te_short);
|
||||
index = pp_emit(up, index, cap, false, te_short);
|
||||
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {
|
||||
if(bit_read(instance->generic.data, i - 2)) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)kia_protocol_v1_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)kia_protocol_v1_const.te_short);
|
||||
} else {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)kia_protocol_v1_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)kia_protocol_v1_const.te_short);
|
||||
}
|
||||
bool bit = bit_read(instance->generic.data, i - 2);
|
||||
index = pp_emit(up, index, cap, bit, te_short);
|
||||
index = pp_emit(up, index, cap, !bit, te_short);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +236,7 @@ static void kia_protocol_encoder_v1_get_upload(SubGhzProtocolEncoderKiaV1* insta
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
SubGhzProtocolStatus
|
||||
kia_protocol_encoder_v1_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
@@ -287,7 +295,7 @@ SubGhzProtocolStatus
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void kia_protocol_encoder_v1_set_button(void* context, uint8_t button) {
|
||||
furi_check(context);
|
||||
@@ -298,7 +306,7 @@ void kia_protocol_encoder_v1_set_button(void* context, uint8_t button) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void kia_protocol_encoder_v1_set_counter(void* context, uint16_t counter) {
|
||||
furi_check(context);
|
||||
@@ -312,7 +320,7 @@ void kia_protocol_encoder_v1_set_counter(void* context, uint16_t counter) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void kia_protocol_encoder_v1_increment_counter(void* context) {
|
||||
furi_check(context);
|
||||
@@ -326,7 +334,7 @@ void kia_protocol_encoder_v1_increment_counter(void* context) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
uint16_t kia_protocol_encoder_v1_get_counter(void* context) {
|
||||
furi_check(context);
|
||||
@@ -335,7 +343,7 @@ uint16_t kia_protocol_encoder_v1_get_counter(void* context) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
uint8_t kia_protocol_encoder_v1_get_button(void* context) {
|
||||
furi_check(context);
|
||||
@@ -347,7 +355,10 @@ uint8_t kia_protocol_encoder_v1_get_button(void* context) {
|
||||
|
||||
void* kia_protocol_decoder_v1_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderKiaV1* instance = malloc(sizeof(SubGhzProtocolDecoderKiaV1));
|
||||
SubGhzProtocolDecoderKiaV1* instance = calloc(1, sizeof(SubGhzProtocolDecoderKiaV1));
|
||||
if(!instance) {
|
||||
return NULL;
|
||||
}
|
||||
instance->base.protocol = &kia_protocol_v1;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
|
||||
@@ -52,7 +52,7 @@ const SubGhzProtocolDecoder kia_protocol_v2_decoder = {
|
||||
.get_string = kia_protocol_decoder_v2_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder kia_protocol_v2_encoder = {
|
||||
.alloc = kia_protocol_encoder_v2_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -76,8 +76,16 @@ const SubGhzProtocol kia_protocol_v2 = {
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &kia_protocol_v2_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &kia_protocol_v2_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
static uint8_t kia_v2_calculate_crc(uint64_t data) {
|
||||
@@ -100,41 +108,33 @@ static uint8_t kia_v2_calculate_crc(uint64_t data) {
|
||||
|
||||
return (crc + 1) & 0x0F;
|
||||
}
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void kia_protocol_encoder_v2_get_upload(SubGhzProtocolEncoderKiaV2* instance) {
|
||||
furi_check(instance);
|
||||
if(instance->encoder.upload == NULL) return;
|
||||
size_t index = 0;
|
||||
LevelDuration* up = instance->encoder.upload;
|
||||
const size_t cap = KIA_V2_UPLOAD_CAPACITY;
|
||||
|
||||
const uint32_t te_short = (uint32_t)kia_protocol_v2_const.te_short;
|
||||
const uint32_t te_long = (uint32_t)kia_protocol_v2_const.te_long;
|
||||
|
||||
uint8_t crc = kia_v2_calculate_crc(instance->generic.data);
|
||||
instance->generic.data = (instance->generic.data & ~0x0FULL) | crc;
|
||||
|
||||
for(uint8_t burst = 0; burst < KIA_V2_TOTAL_BURSTS; burst++) {
|
||||
for(int i = 0; i < KIA_V2_HEADER_PAIRS; i++) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)kia_protocol_v2_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)kia_protocol_v2_const.te_long);
|
||||
index = pp_emit(up, index, cap, false, te_long);
|
||||
index = pp_emit(up, index, cap, true, te_long);
|
||||
}
|
||||
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)kia_protocol_v2_const.te_short);
|
||||
index = pp_emit(up, index, cap, false, te_short);
|
||||
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {
|
||||
bool bit = bit_read(instance->generic.data, i - 2);
|
||||
|
||||
if(bit) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)kia_protocol_v2_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)kia_protocol_v2_const.te_short);
|
||||
} else {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)kia_protocol_v2_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)kia_protocol_v2_const.te_short);
|
||||
}
|
||||
index = pp_emit(up, index, cap, bit, te_short);
|
||||
index = pp_emit(up, index, cap, !bit, te_short);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,11 +151,14 @@ static void kia_protocol_encoder_v2_get_upload(SubGhzProtocolEncoderKiaV2* insta
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void* kia_protocol_encoder_v2_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderKiaV2* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV2));
|
||||
SubGhzProtocolEncoderKiaV2* instance = calloc(1, sizeof(SubGhzProtocolEncoderKiaV2));
|
||||
if(!instance) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
instance->base.protocol = &kia_protocol_v2;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
@@ -170,7 +173,7 @@ void* kia_protocol_encoder_v2_alloc(SubGhzEnvironment* environment) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
SubGhzProtocolStatus
|
||||
kia_protocol_encoder_v2_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
@@ -256,7 +259,10 @@ SubGhzProtocolStatus
|
||||
|
||||
void* kia_protocol_decoder_v2_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderKiaV2* instance = malloc(sizeof(SubGhzProtocolDecoderKiaV2));
|
||||
SubGhzProtocolDecoderKiaV2* instance = calloc(1, sizeof(SubGhzProtocolDecoderKiaV2));
|
||||
if(!instance) {
|
||||
return NULL;
|
||||
}
|
||||
instance->base.protocol = &kia_protocol_v2;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
|
||||
@@ -87,7 +87,7 @@ static void kia_v3_v4_add_raw_bit(SubGhzProtocolDecoderKiaV3V4* instance, bool b
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static inline void kia_v3_v4_emit_bit_pwm(LevelDuration* upload, size_t* idx, bool bit, bool v4) {
|
||||
const uint32_t te_short = kia_protocol_v3_v4_const.te_short;
|
||||
const uint32_t te_long = kia_protocol_v3_v4_const.te_long;
|
||||
@@ -173,7 +173,7 @@ const SubGhzProtocolDecoder kia_protocol_v3_v4_decoder = {
|
||||
.get_string = kia_protocol_decoder_v3_v4_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder kia_protocol_v3_v4_encoder = {
|
||||
.alloc = kia_protocol_encoder_v3_v4_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -197,22 +197,34 @@ const SubGhzProtocol kia_protocol_v3_v4 = {
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &kia_protocol_v3_v4_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &kia_protocol_v3_v4_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ENCODER IMPLEMENTATION
|
||||
// ============================================================================
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void* kia_protocol_encoder_v3_v4_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolEncoderKiaV3V4* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV3V4));
|
||||
furi_check(instance);
|
||||
|
||||
if(environment) {
|
||||
protopirate_keys_load(environment);
|
||||
}
|
||||
if(!protopirate_keys_has_kia_mf_key()) {
|
||||
FURI_LOG_E(TAG, "Kia V3/V4 encoder missing KIA_KEY1 keystore entry");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SubGhzProtocolEncoderKiaV3V4* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV3V4));
|
||||
furi_check(instance);
|
||||
|
||||
instance->base.protocol = &kia_protocol_v3_v4;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
@@ -234,7 +246,7 @@ void* kia_protocol_encoder_v3_v4_alloc(SubGhzEnvironment* environment) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void kia_protocol_encoder_v3_v4_build_packet(
|
||||
SubGhzProtocolEncoderKiaV3V4* instance,
|
||||
@@ -303,7 +315,7 @@ static void kia_protocol_encoder_v3_v4_patch_crc(SubGhzProtocolEncoderKiaV3V4* i
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void kia_protocol_encoder_v3_v4_get_upload(SubGhzProtocolEncoderKiaV3V4* instance) {
|
||||
furi_check(instance);
|
||||
@@ -370,7 +382,7 @@ static void kia_protocol_encoder_v3_v4_get_upload(SubGhzProtocolEncoderKiaV3V4*
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
SubGhzProtocolStatus
|
||||
kia_protocol_encoder_v3_v4_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
@@ -474,7 +486,7 @@ SubGhzProtocolStatus
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void kia_protocol_encoder_v3_v4_stop(void* context) {
|
||||
if(!context) return;
|
||||
@@ -484,7 +496,7 @@ void kia_protocol_encoder_v3_v4_stop(void* context) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
LevelDuration kia_protocol_encoder_v3_v4_yield(void* context) {
|
||||
SubGhzProtocolEncoderKiaV3V4* instance = context;
|
||||
@@ -537,7 +549,7 @@ LevelDuration kia_protocol_encoder_v3_v4_yield(void* context) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void kia_protocol_encoder_v3_v4_set_button(void* context, uint8_t button) {
|
||||
furi_check(context);
|
||||
@@ -549,7 +561,7 @@ void kia_protocol_encoder_v3_v4_set_button(void* context, uint8_t button) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void kia_protocol_encoder_v3_v4_set_counter(void* context, uint16_t counter) {
|
||||
furi_check(context);
|
||||
@@ -561,7 +573,7 @@ void kia_protocol_encoder_v3_v4_set_counter(void* context, uint16_t counter) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void kia_protocol_encoder_v3_v4_increment_counter(void* context) {
|
||||
furi_check(context);
|
||||
@@ -573,7 +585,7 @@ void kia_protocol_encoder_v3_v4_increment_counter(void* context) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
uint16_t kia_protocol_encoder_v3_v4_get_counter(void* context) {
|
||||
furi_check(context);
|
||||
@@ -582,7 +594,7 @@ uint16_t kia_protocol_encoder_v3_v4_get_counter(void* context) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
uint8_t kia_protocol_encoder_v3_v4_get_button(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
@@ -79,7 +79,7 @@ static uint16_t mixer_decode(uint32_t encrypted) {
|
||||
return (s0 + (s1 << 8)) & 0xFFFF;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static uint32_t mixer_encode(uint32_t serial, uint16_t counter, uint8_t button) {
|
||||
build_keystore_from_mfkey(keystore_bytes);
|
||||
|
||||
@@ -196,7 +196,7 @@ const SubGhzProtocolDecoder kia_protocol_v5_decoder = {
|
||||
.get_string = kia_protocol_decoder_v5_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder kia_protocol_v5_encoder = {
|
||||
.alloc = kia_protocol_encoder_v5_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -219,15 +219,23 @@ const SubGhzProtocol kia_protocol_v5 = {
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
| SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Send
|
||||
#endif
|
||||
,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &kia_protocol_v5_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &kia_protocol_v5_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static uint8_t kia_v5_calculate_crc(uint64_t data) {
|
||||
uint8_t crc = 0;
|
||||
@@ -243,12 +251,16 @@ static uint8_t kia_v5_calculate_crc(uint64_t data) {
|
||||
}
|
||||
|
||||
void* kia_protocol_encoder_v5_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolEncoderKiaV5* instance = calloc(1, sizeof(SubGhzProtocolEncoderKiaV5));
|
||||
furi_check(instance);
|
||||
|
||||
if(environment) {
|
||||
protopirate_keys_load(environment);
|
||||
}
|
||||
if(!protopirate_keys_has_kia_v5_key()) {
|
||||
FURI_LOG_E(TAG, "Kia V5 encoder missing KIA_KEY4 keystore entry");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SubGhzProtocolEncoderKiaV5* instance = calloc(1, sizeof(SubGhzProtocolEncoderKiaV5));
|
||||
furi_check(instance);
|
||||
|
||||
instance->base.protocol = &kia_protocol_v5;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
@@ -114,7 +114,7 @@ const SubGhzProtocolDecoder kia_protocol_v6_decoder = {
|
||||
.get_string = kia_protocol_decoder_v6_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder kia_protocol_v6_encoder = {
|
||||
.alloc = kia_protocol_encoder_v6_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -137,8 +137,16 @@ const SubGhzProtocol kia_protocol_v6 = {
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Load,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &kia_protocol_v6_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &kia_protocol_v6_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
#define kia_v6_crc8(data, len) subghz_protocol_blocks_crc8((data), (len), 0x07, 0xFF)
|
||||
@@ -215,7 +223,7 @@ static void aes_addroundkey(uint8_t* state, const uint8_t* round_key) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void aes_subbytes(uint8_t* state) {
|
||||
for(int row = 0; row < 4; row++) {
|
||||
for(int col = 0; col < 4; col++) {
|
||||
@@ -358,7 +366,7 @@ static void get_kia_v6_aes_key(uint8_t* aes_key) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void kia_v6_encrypt_payload(
|
||||
uint8_t fx_field,
|
||||
uint32_t serial,
|
||||
@@ -812,7 +820,7 @@ void kia_protocol_decoder_v6_get_string(void* context, FuriString* output) {
|
||||
instance->crc2_field);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
#define KIA_V6_PREAMBLE_PAIRS_1 640
|
||||
#define KIA_V6_PREAMBLE_PAIRS_2 38
|
||||
@@ -928,13 +936,17 @@ static void kia_protocol_encoder_v6_build_upload(SubGhzProtocolEncoderKiaV6* ins
|
||||
}
|
||||
|
||||
void* kia_protocol_encoder_v6_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolEncoderKiaV6* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV6));
|
||||
if(!instance) return NULL;
|
||||
memset(instance, 0, sizeof(SubGhzProtocolEncoderKiaV6));
|
||||
|
||||
if(environment) {
|
||||
protopirate_keys_load(environment);
|
||||
}
|
||||
if(!protopirate_keys_has_kia_v6_keystore_a() || !protopirate_keys_has_kia_v6_keystore_b()) {
|
||||
FURI_LOG_E(TAG, "Kia V6 encoder missing KIA_KEY2/KIA_KEY3 keystore entries");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SubGhzProtocolEncoderKiaV6* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV6));
|
||||
if(!instance) return NULL;
|
||||
memset(instance, 0, sizeof(SubGhzProtocolEncoderKiaV6));
|
||||
|
||||
instance->base.protocol = &kia_protocol_v6;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
@@ -985,4 +997,4 @@ SubGhzProtocolStatus
|
||||
return SubGhzProtocolStatusOk;
|
||||
}
|
||||
|
||||
#endif // ENABLE_EMULATE_FEATURE
|
||||
#endif // PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
@@ -37,7 +37,7 @@ SubGhzProtocolStatus
|
||||
kia_protocol_decoder_v6_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void kia_protocol_decoder_v6_get_string(void* context, FuriString* output);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* kia_protocol_encoder_v6_alloc(SubGhzEnvironment* environment);
|
||||
SubGhzProtocolStatus
|
||||
kia_protocol_encoder_v6_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
@@ -43,7 +43,7 @@ struct SubGhzProtocolDecoderKiaV7 {
|
||||
bool crc_valid;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
struct SubGhzProtocolEncoderKiaV7 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
@@ -155,7 +155,7 @@ static uint64_t kia_v7_encode_key(
|
||||
return pp_bytes_to_u64_be(bytes);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void kia_v7_decode_key_encoder(SubGhzProtocolEncoderKiaV7* instance) {
|
||||
kia_v7_decode_key_common(
|
||||
&instance->generic,
|
||||
@@ -233,7 +233,7 @@ const SubGhzProtocolDecoder kia_protocol_v7_decoder = {
|
||||
.get_string = kia_protocol_decoder_v7_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder kia_protocol_v7_encoder = {
|
||||
.alloc = kia_protocol_encoder_v7_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -257,11 +257,19 @@ const SubGhzProtocol kia_protocol_v7 = {
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &kia_protocol_v7_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &kia_protocol_v7_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* kia_protocol_encoder_v7_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ SubGhzProtocolStatus
|
||||
kia_protocol_decoder_v7_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void kia_protocol_decoder_v7_get_string(void* context, FuriString* output);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* kia_protocol_encoder_v7_alloc(SubGhzEnvironment* environment);
|
||||
SubGhzProtocolStatus
|
||||
kia_protocol_encoder_v7_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#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/level_duration.h>
|
||||
|
||||
#include "../defines.h"
|
||||
|
||||
#define LAND_ROVER_PROTOCOL_V0_NAME "Land Rover V0"
|
||||
|
||||
extern const SubGhzProtocol land_rover_v0_protocol;
|
||||
|
||||
void* subghz_protocol_decoder_land_rover_v0_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_land_rover_v0_free(void* context);
|
||||
void subghz_protocol_decoder_land_rover_v0_reset(void* context);
|
||||
void subghz_protocol_decoder_land_rover_v0_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_land_rover_v0_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_land_rover_v0_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_land_rover_v0_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_land_rover_v0_get_string(void* context, FuriString* output);
|
||||
|
||||
void* subghz_protocol_encoder_land_rover_v0_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_encoder_land_rover_v0_free(void* context);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_land_rover_v0_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_encoder_land_rover_v0_stop(void* context);
|
||||
LevelDuration subghz_protocol_encoder_land_rover_v0_yield(void* context);
|
||||
@@ -41,7 +41,7 @@ typedef struct SubGhzProtocolDecoderMazdaV0 {
|
||||
uint32_t count;
|
||||
} SubGhzProtocolDecoderMazdaV0;
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
typedef struct SubGhzProtocolEncoderMazdaV0 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
@@ -65,7 +65,7 @@ typedef enum {
|
||||
|
||||
static bool mazda_v0_get_event(uint32_t duration, bool level, ManchesterEvent* event);
|
||||
static void mazda_v0_decode_key(SubGhzBlockGeneric* generic);
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static uint64_t mazda_v0_encode_key(uint32_t serial, uint8_t button, uint32_t counter);
|
||||
static bool mazda_v0_encoder_add_level(
|
||||
SubGhzProtocolEncoderMazdaV0* instance,
|
||||
@@ -96,7 +96,7 @@ const SubGhzProtocolDecoder subghz_protocol_mazda_v0_decoder = {
|
||||
.get_string = subghz_protocol_decoder_mazda_v0_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_mazda_v0_encoder = {
|
||||
.alloc = subghz_protocol_encoder_mazda_v0_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -117,11 +117,20 @@ const SubGhzProtocolEncoder subghz_protocol_mazda_v0_encoder = {
|
||||
const SubGhzProtocol mazda_v0_protocol = {
|
||||
.name = MAZDA_PROTOCOL_V0_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_mazda_v0_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_mazda_v0_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
@@ -193,7 +202,7 @@ static void mazda_v0_decode_key(SubGhzBlockGeneric* generic) {
|
||||
generic->data_count_bit = subghz_protocol_mazda_v0_const.min_count_bit_for_found;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static uint64_t mazda_v0_encode_key(uint32_t serial, uint8_t button, uint32_t counter) {
|
||||
uint8_t data[8];
|
||||
|
||||
@@ -305,7 +314,7 @@ static SubGhzProtocolStatus mazda_v0_write_display(
|
||||
// ENCODER
|
||||
// =============================================================================
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* subghz_protocol_encoder_mazda_v0_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
|
||||
@@ -122,8 +122,16 @@ const SubGhzProtocol mitsubishi_v0_protocol = {
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_mitsubishi_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_mitsubishi_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
void* subghz_protocol_decoder_mitsubishi_alloc(SubGhzEnvironment* environment) {
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
#include "../protopirate_protocol_plugins.h"
|
||||
#include "../protocols_common.h"
|
||||
#include "../chrysler_v0.h"
|
||||
#include "../fiat_v0.h"
|
||||
#include "../fiat_v1.h"
|
||||
#include "../fiat_v2.h"
|
||||
#include "../ford_v0.h"
|
||||
#include "../ford_v3.h"
|
||||
#include "../kia_v1.h"
|
||||
#include "../kia_v2.h"
|
||||
#include "../mazda_v0.h"
|
||||
#include "../porsche_touareg.h"
|
||||
#include "../psa.h"
|
||||
#include "../renault_v0.h"
|
||||
#include "../subaru.h"
|
||||
#include "../vag.h"
|
||||
#include "../star_line.h"
|
||||
#include "../honda_v1.h"
|
||||
|
||||
@@ -16,14 +20,17 @@ static const SubGhzProtocol* const protopirate_protocol_registry_am_items[] = {
|
||||
&chrysler_protocol_v0,
|
||||
&fiat_protocol_v0,
|
||||
&fiat_v1_protocol,
|
||||
&fiat_v2_protocol,
|
||||
&ford_protocol_v0,
|
||||
&ford_protocol_v3,
|
||||
&honda_v1_protocol,
|
||||
&kia_protocol_v1,
|
||||
&kia_protocol_v2,
|
||||
&mazda_v0_protocol,
|
||||
&porsche_touareg_protocol,
|
||||
&psa_protocol,
|
||||
&renault_v0_protocol,
|
||||
&subaru_protocol,
|
||||
&vag_protocol,
|
||||
&subghz_protocol_star_line,
|
||||
};
|
||||
|
||||
@@ -34,9 +41,11 @@ static const SubGhzProtocolRegistry protopirate_protocol_registry_am = {
|
||||
};
|
||||
|
||||
static const ProtoPirateProtocolPlugin protopirate_am_plugin = {
|
||||
.plugin_name = "ProtoPirate AM Registry",
|
||||
.filter = ProtoPirateProtocolRegistryFilterAM,
|
||||
.plugin_name = "ProtoPirate AM Default Registry",
|
||||
.kind = ProtoPirateProtocolPluginKindRx,
|
||||
.route = ProtoPirateProtocolRegistryRouteAMDefault,
|
||||
.registry = &protopirate_protocol_registry_am,
|
||||
.release = NULL,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor protopirate_am_plugin_descriptor = {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "../protopirate_protocol_plugins.h"
|
||||
#include "../protocols_common.h"
|
||||
#include "../vag.h"
|
||||
|
||||
static const SubGhzProtocol* const protopirate_protocol_registry_am_vag_items[] = {
|
||||
&vag_protocol,
|
||||
};
|
||||
|
||||
static const SubGhzProtocolRegistry protopirate_protocol_registry_am_vag = {
|
||||
.items = protopirate_protocol_registry_am_vag_items,
|
||||
.size = sizeof(protopirate_protocol_registry_am_vag_items) /
|
||||
sizeof(protopirate_protocol_registry_am_vag_items[0]),
|
||||
};
|
||||
|
||||
static const ProtoPirateProtocolPlugin protopirate_am_vag_plugin = {
|
||||
.plugin_name = "ProtoPirate AM VAG Registry",
|
||||
.kind = ProtoPirateProtocolPluginKindRx,
|
||||
.route = ProtoPirateProtocolRegistryRouteAMVag,
|
||||
.registry = &protopirate_protocol_registry_am_vag,
|
||||
.release = NULL,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor protopirate_am_vag_plugin_descriptor = {
|
||||
.appid = PROTOPIRATE_PROTOCOL_PLUGIN_APP_ID,
|
||||
.ep_api_version = PROTOPIRATE_PROTOCOL_PLUGIN_API_VERSION,
|
||||
.entry_point = &protopirate_am_vag_plugin,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* protopirate_am_vag_plugin_ep(void) {
|
||||
return &protopirate_am_vag_plugin_descriptor;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "../protopirate_protocol_plugins.h"
|
||||
#include "../protocols_common.h"
|
||||
#include "../ford_v1.h"
|
||||
#include "../ford_v2.h"
|
||||
#include "../ford_v3.h"
|
||||
#include "../honda_v2.h"
|
||||
|
||||
static const SubGhzProtocol* const protopirate_protocol_registry_fm_f4_items[] = {
|
||||
&ford_protocol_v1,
|
||||
&ford_protocol_v2,
|
||||
&ford_protocol_v3,
|
||||
&honda_v2_protocol,
|
||||
};
|
||||
|
||||
static const SubGhzProtocolRegistry protopirate_protocol_registry_fm_f4 = {
|
||||
.items = protopirate_protocol_registry_fm_f4_items,
|
||||
.size = sizeof(protopirate_protocol_registry_fm_f4_items) /
|
||||
sizeof(protopirate_protocol_registry_fm_f4_items[0]),
|
||||
};
|
||||
|
||||
static const ProtoPirateProtocolPlugin protopirate_fm_f4_plugin = {
|
||||
.plugin_name = "ProtoPirate FM F4 Registry",
|
||||
.kind = ProtoPirateProtocolPluginKindRx,
|
||||
.route = ProtoPirateProtocolRegistryRouteFMF4,
|
||||
.registry = &protopirate_protocol_registry_fm_f4,
|
||||
.release = NULL,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor protopirate_fm_f4_plugin_descriptor = {
|
||||
.appid = PROTOPIRATE_PROTOCOL_PLUGIN_APP_ID,
|
||||
.ep_api_version = PROTOPIRATE_PROTOCOL_PLUGIN_API_VERSION,
|
||||
.entry_point = &protopirate_fm_f4_plugin,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* protopirate_fm_f4_plugin_ep(void) {
|
||||
return &protopirate_fm_f4_plugin_descriptor;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "../protopirate_protocol_plugins.h"
|
||||
#include "../protocols_common.h"
|
||||
#include "../honda_static.h"
|
||||
|
||||
static const SubGhzProtocol* const protopirate_protocol_registry_fm_honda1_items[] = {
|
||||
&honda_static_protocol,
|
||||
};
|
||||
|
||||
static const SubGhzProtocolRegistry protopirate_protocol_registry_fm_honda1 = {
|
||||
.items = protopirate_protocol_registry_fm_honda1_items,
|
||||
.size = sizeof(protopirate_protocol_registry_fm_honda1_items) /
|
||||
sizeof(protopirate_protocol_registry_fm_honda1_items[0]),
|
||||
};
|
||||
|
||||
static const ProtoPirateProtocolPlugin protopirate_fm_honda1_plugin = {
|
||||
.plugin_name = "ProtoPirate FM Honda1 Registry",
|
||||
.kind = ProtoPirateProtocolPluginKindRx,
|
||||
.route = ProtoPirateProtocolRegistryRouteFMHonda1,
|
||||
.registry = &protopirate_protocol_registry_fm_honda1,
|
||||
.release = NULL,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor protopirate_fm_honda1_plugin_descriptor = {
|
||||
.appid = PROTOPIRATE_PROTOCOL_PLUGIN_APP_ID,
|
||||
.ep_api_version = PROTOPIRATE_PROTOCOL_PLUGIN_API_VERSION,
|
||||
.entry_point = &protopirate_fm_honda1_plugin,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* protopirate_fm_honda1_plugin_ep(void) {
|
||||
return &protopirate_fm_honda1_plugin_descriptor;
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
#include "../protopirate_protocol_plugins.h"
|
||||
#include "../protocols_common.h"
|
||||
#include "../scher_khan.h"
|
||||
#include "../kia_v0.h"
|
||||
#include "../kia_v3_v4.h"
|
||||
#include "../kia_v5.h"
|
||||
#include "../kia_v6.h"
|
||||
#include "../kia_v7.h"
|
||||
#include "../ford_v1.h"
|
||||
#include "../ford_v2.h"
|
||||
#include "../ford_v3.h"
|
||||
#include "../honda_static.h"
|
||||
#include "../land_rover_v0.h"
|
||||
#include "../mazda_v0.h"
|
||||
#include "../mitsubishi_v0.h"
|
||||
#include "../psa.h"
|
||||
@@ -20,11 +16,6 @@ static const SubGhzProtocol* const protopirate_protocol_registry_fm_items[] = {
|
||||
&kia_protocol_v3_v4,
|
||||
&kia_protocol_v5,
|
||||
&kia_protocol_v6,
|
||||
&ford_protocol_v1,
|
||||
&ford_protocol_v2,
|
||||
&ford_protocol_v3,
|
||||
&honda_static_protocol,
|
||||
&land_rover_v0_protocol,
|
||||
&mazda_v0_protocol,
|
||||
&mitsubishi_v0_protocol,
|
||||
&kia_protocol_v7,
|
||||
@@ -38,9 +29,11 @@ static const SubGhzProtocolRegistry protopirate_protocol_registry_fm = {
|
||||
};
|
||||
|
||||
static const ProtoPirateProtocolPlugin protopirate_fm_plugin = {
|
||||
.plugin_name = "ProtoPirate FM Registry",
|
||||
.filter = ProtoPirateProtocolRegistryFilterFM,
|
||||
.plugin_name = "ProtoPirate FM Default Registry",
|
||||
.kind = ProtoPirateProtocolPluginKindRx,
|
||||
.route = ProtoPirateProtocolRegistryRouteFMDefault,
|
||||
.registry = &protopirate_protocol_registry_fm,
|
||||
.release = NULL,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor protopirate_fm_plugin_descriptor = {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "../protopirate_protocol_plugins.h"
|
||||
#include "../protocols_common.h"
|
||||
|
||||
#ifndef PP_TX_PROTOCOL_HEADER
|
||||
#error "PP_TX_PROTOCOL_HEADER must be defined for TX protocol plugins"
|
||||
#endif
|
||||
|
||||
#include PP_TX_PROTOCOL_HEADER
|
||||
|
||||
#ifndef PP_TX_PROTOCOL_NAME
|
||||
#error "PP_TX_PROTOCOL_NAME must be defined for TX protocol plugins"
|
||||
#endif
|
||||
|
||||
#ifndef PP_TX_PROTOCOL_ITEM
|
||||
#error "PP_TX_PROTOCOL_ITEM must be defined for TX protocol plugins"
|
||||
#endif
|
||||
|
||||
static const SubGhzProtocol* const protopirate_tx_protocol_registry_items[] = {
|
||||
&PP_TX_PROTOCOL_ITEM,
|
||||
};
|
||||
|
||||
static const SubGhzProtocolRegistry protopirate_tx_protocol_registry = {
|
||||
.items = protopirate_tx_protocol_registry_items,
|
||||
.size = 1U,
|
||||
};
|
||||
|
||||
static const ProtoPirateProtocolPlugin protopirate_tx_protocol_plugin = {
|
||||
.plugin_name = PP_TX_PROTOCOL_NAME,
|
||||
.kind = ProtoPirateProtocolPluginKindTx,
|
||||
.route = ProtoPirateProtocolRegistryRouteAMDefault,
|
||||
.protocol_name = PP_TX_PROTOCOL_NAME,
|
||||
.registry = &protopirate_tx_protocol_registry,
|
||||
.release = pp_shared_upload_release,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor protopirate_tx_protocol_plugin_descriptor = {
|
||||
.appid = PROTOPIRATE_PROTOCOL_PLUGIN_APP_ID,
|
||||
.ep_api_version = PROTOPIRATE_PROTOCOL_PLUGIN_API_VERSION,
|
||||
.entry_point = &protopirate_tx_protocol_plugin,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* protopirate_tx_protocol_plugin_ep(void) {
|
||||
return &protopirate_tx_protocol_plugin_descriptor;
|
||||
}
|
||||
@@ -159,8 +159,16 @@ const SubGhzProtocol porsche_touareg_protocol = {
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_porsche_cayenne_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_porsche_cayenne_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
void* subghz_protocol_decoder_porsche_cayenne_alloc(SubGhzEnvironment* environment) {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#include "protocol_items.h"
|
||||
#include <furi.h>
|
||||
#ifdef ENABLE_TIMING_TUNER_SCENE
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#define TAG "ProtoPirateRegistry"
|
||||
#include <furi.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "ProtoPirateCatalog"
|
||||
|
||||
#define PROTOPIRATE_CC1101_REG_MDMCFG2 0x12U
|
||||
#define PROTOPIRATE_CC1101_MOD_FORMAT_MASK 0x70U
|
||||
@@ -13,6 +12,178 @@
|
||||
#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]))
|
||||
|
||||
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, "chrysler_v0"},
|
||||
{"Fiat V0", ProtoPirateProtocolCatalogRouteAMDefault, "fiat_v0"},
|
||||
{"Fiat V1", ProtoPirateProtocolCatalogRouteAMDefault, "fiat_v1"},
|
||||
{"Fiat V2", ProtoPirateProtocolCatalogRouteAMDefault, NULL},
|
||||
{"Ford V0", ProtoPirateProtocolCatalogRouteAMDefault, "ford_v0"},
|
||||
{"Ford V1", ProtoPirateProtocolCatalogRouteFMF4, "ford_v1"},
|
||||
{"Ford V2", ProtoPirateProtocolCatalogRouteFMF4, "ford_v2"},
|
||||
{"Ford V3", ProtoPirateProtocolCatalogRouteFMF4, NULL},
|
||||
{"Honda Static", ProtoPirateProtocolCatalogRouteFMHonda1, "honda_static"},
|
||||
{"Honda V1", ProtoPirateProtocolCatalogRouteAMDefault, "honda_v1"},
|
||||
{"Kia V0", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v0"},
|
||||
{"Kia V1", ProtoPirateProtocolCatalogRouteAMDefault, "kia_v1"},
|
||||
{"Kia V2", ProtoPirateProtocolCatalogRouteAMDefault, "kia_v2"},
|
||||
{"Kia V3/V4", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v3_v4"},
|
||||
{"Kia V5", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v5"},
|
||||
{"Kia V6", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v6"},
|
||||
{"Kia V7", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v7"},
|
||||
{"Honda V2", ProtoPirateProtocolCatalogRouteFMF4, "honda_v2"},
|
||||
{"Mazda V0", ProtoPirateProtocolCatalogRouteByModulation, "mazda_v0"},
|
||||
{"Mitsubishi V0", ProtoPirateProtocolCatalogRouteFMDefault, NULL},
|
||||
{"Porsche Touareg", ProtoPirateProtocolCatalogRouteAMDefault, NULL},
|
||||
{"PSA", ProtoPirateProtocolCatalogRouteByModulation, "psa"},
|
||||
{"Renault V0", ProtoPirateProtocolCatalogRouteAMDefault, "renault_v0"},
|
||||
{"Scher-Khan", ProtoPirateProtocolCatalogRouteFMDefault, NULL},
|
||||
{"Star Line", ProtoPirateProtocolCatalogRouteAMDefault, "star_line"},
|
||||
{"Subaru", ProtoPirateProtocolCatalogRouteAMDefault, "subaru"},
|
||||
{"VAG", ProtoPirateProtocolCatalogRouteAMVag, "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,
|
||||
@@ -40,7 +211,7 @@ static bool protopirate_preset_try_get_register(
|
||||
return false;
|
||||
}
|
||||
|
||||
ProtoPirateProtocolRegistryFilter protopirate_get_protocol_registry_filter_for_preset(
|
||||
static ProtoPirateProtocolCatalogModulation protopirate_protocol_catalog_get_modulation(
|
||||
const uint8_t* preset_data,
|
||||
size_t preset_data_size) {
|
||||
uint8_t mdmcfg2 = 0U;
|
||||
@@ -48,267 +219,114 @@ ProtoPirateProtocolRegistryFilter protopirate_get_protocol_registry_filter_for_p
|
||||
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 ProtoPirateProtocolRegistryFilterAM;
|
||||
return ProtoPirateProtocolCatalogModulationAM;
|
||||
}
|
||||
|
||||
// MDMCFG2[6:4] stores the CC1101 modulation format.
|
||||
// ASK/OOK maps to our AM decoder set; the FSK-family formats map to FM.
|
||||
switch(mdmcfg2 & PROTOPIRATE_CC1101_MOD_FORMAT_MASK) {
|
||||
case PROTOPIRATE_CC1101_MOD_FORMAT_ASK_OOK:
|
||||
return ProtoPirateProtocolRegistryFilterAM;
|
||||
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 ProtoPirateProtocolRegistryFilterFM;
|
||||
return ProtoPirateProtocolCatalogModulationFM;
|
||||
default:
|
||||
FURI_LOG_W(TAG, "Unknown MDMCFG2 0x%02X, defaulting to AM registry", mdmcfg2);
|
||||
return ProtoPirateProtocolRegistryFilterAM;
|
||||
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_filter_name(ProtoPirateProtocolRegistryFilter filter) {
|
||||
return (filter == ProtoPirateProtocolRegistryFilterFM) ? "FM" : "AM";
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TIMING_TUNER_SCENE
|
||||
// Protocol timing definitions - mirrors the SubGhzBlockConst in each protocol
|
||||
static const ProtoPirateProtocolTiming protocol_timings[] = {
|
||||
// Honda Static
|
||||
{
|
||||
.name = HONDA_STATIC_PROTOCOL_NAME,
|
||||
.te_short = 63,
|
||||
.te_long = 700,
|
||||
.te_delta = 120,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// Honda V1: Manchester 1000/2000µs
|
||||
{
|
||||
.name = HONDA_V1_PROTOCOL_NAME,
|
||||
.te_short = 1000,
|
||||
.te_long = 2000,
|
||||
.te_delta = 400,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// Kia V0: PWM 250/500µs — Kia 61bit, Suzuki 64bit, Honda V0 72bit
|
||||
{
|
||||
.name = "Kia V0",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 61,
|
||||
},
|
||||
// Kia V1: OOK PCM 800µs timing
|
||||
{
|
||||
.name = "Kia V1",
|
||||
.te_short = 800,
|
||||
.te_long = 1600,
|
||||
.te_delta = 200,
|
||||
.min_count_bit = 56,
|
||||
},
|
||||
// Kia V2: Manchester 500/1000µs
|
||||
{
|
||||
.name = "Kia V2",
|
||||
.te_short = 500,
|
||||
.te_long = 1000,
|
||||
.te_delta = 150,
|
||||
.min_count_bit = 51,
|
||||
},
|
||||
// Kia V3/V4: PWM 400/800µs
|
||||
{
|
||||
.name = "Kia V3/V4",
|
||||
.te_short = 400,
|
||||
.te_long = 800,
|
||||
.te_delta = 150,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// Kia V5: PWM 400/800µs (same as V3/V4)
|
||||
{
|
||||
.name = "Kia V5",
|
||||
.te_short = 400,
|
||||
.te_long = 800,
|
||||
.te_delta = 150,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// Kia V6: Manchester 200/400µs
|
||||
{
|
||||
.name = "Kia V6",
|
||||
.te_short = 200,
|
||||
.te_long = 400,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 144,
|
||||
},
|
||||
// Kia V7: Manchester 250/500µs
|
||||
{
|
||||
.name = KIA_PROTOCOL_V7_NAME,
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// Ford V0: Manchester 250/500µs
|
||||
{
|
||||
.name = "Ford V0",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// Chrysler V0: PWM short/long
|
||||
{
|
||||
.name = "Chrysler V0",
|
||||
.te_short = 300,
|
||||
.te_long = 3700,
|
||||
.te_delta = 400,
|
||||
.min_count_bit = 80,
|
||||
},
|
||||
// Ford V1: Manchester 65/130us
|
||||
{
|
||||
.name = FORD_PROTOCOL_V1_NAME,
|
||||
.te_short = 65,
|
||||
.te_long = 130,
|
||||
.te_delta = 39,
|
||||
.min_count_bit = 136,
|
||||
},
|
||||
// Ford V2: Manchester 200/400us
|
||||
{
|
||||
.name = FORD_PROTOCOL_V2_NAME,
|
||||
.te_short = 200,
|
||||
.te_long = 400,
|
||||
.te_delta = 260,
|
||||
.min_count_bit = 104,
|
||||
},
|
||||
// Ford V3: Manchester 240/480us
|
||||
{
|
||||
.name = FORD_PROTOCOL_V3_NAME,
|
||||
.te_short = 240,
|
||||
.te_long = 480,
|
||||
.te_delta = 60,
|
||||
.min_count_bit = 104,
|
||||
},
|
||||
// Fiat V0: Manchester 200/400µs
|
||||
{
|
||||
.name = "Fiat V0",
|
||||
.te_short = 200,
|
||||
.te_long = 400,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// Fiat V1: Manchester dynamic (baseline Type A 260/520us)
|
||||
{
|
||||
.name = "Fiat V1",
|
||||
.te_short = 260,
|
||||
.te_long = 520,
|
||||
.te_delta = 80,
|
||||
.min_count_bit = 80,
|
||||
},
|
||||
// Mazda V0: 250/500us
|
||||
{
|
||||
.name = "Mazda V0",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// Land Rover V0: Differential PWM 250/500us + sync 750us
|
||||
{
|
||||
.name = LAND_ROVER_PROTOCOL_V0_NAME,
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 81,
|
||||
},
|
||||
// Porsche Touareg: 1680/3370us
|
||||
{
|
||||
.name = "Porsche Touareg",
|
||||
.te_short = 1680,
|
||||
.te_long = 3370,
|
||||
.te_delta = 500,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// Subaru: PPM 800/1600µs
|
||||
{
|
||||
.name = "Subaru",
|
||||
.te_short = 800,
|
||||
.te_long = 1600,
|
||||
.te_delta = 200,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// VW: Manchester 500/1000µs
|
||||
{
|
||||
.name = "VW",
|
||||
.te_short = 500,
|
||||
.te_long = 1000,
|
||||
.te_delta = 120,
|
||||
.min_count_bit = 80,
|
||||
},
|
||||
// Scher-Khan: PWM 750/1100µs
|
||||
{
|
||||
.name = "Scher-Khan",
|
||||
.te_short = 750,
|
||||
.te_long = 1100,
|
||||
.te_delta = 180,
|
||||
.min_count_bit = 35,
|
||||
},
|
||||
// Star Line: PWM 250/500µs
|
||||
{
|
||||
.name = "Star Line",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 120,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
// PSA: Manchester 250/500µs (Pattern 1) or 125/250µs (Pattern 2)
|
||||
{
|
||||
.name = "PSA",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 128,
|
||||
},
|
||||
};
|
||||
|
||||
static const size_t protocol_timings_count = COUNT_OF(protocol_timings);
|
||||
|
||||
const ProtoPirateProtocolTiming* protopirate_get_protocol_timing(const char* protocol_name) {
|
||||
if(!protocol_name) return NULL;
|
||||
|
||||
for(size_t i = 0; i < protocol_timings_count; i++) {
|
||||
// Check for exact match or if the protocol name contains our timing name
|
||||
if(strcmp(protocol_name, protocol_timings[i].name) == 0 ||
|
||||
strstr(protocol_name, protocol_timings[i].name) != NULL) {
|
||||
return &protocol_timings[i];
|
||||
}
|
||||
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";
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char* alias;
|
||||
const char* canonical;
|
||||
} aliases[] = {
|
||||
{"Honda V0", "Kia V0"},
|
||||
{"Suzuki", "Kia V0"},
|
||||
{"V3", "Kia V3/V4"},
|
||||
{"V4", "Kia V3/V4"},
|
||||
};
|
||||
for(size_t a = 0; a < COUNT_OF(aliases); a++) {
|
||||
if(strstr(protocol_name, aliases[a].alias) == NULL) continue;
|
||||
for(size_t i = 0; i < protocol_timings_count; i++) {
|
||||
if(strstr(protocol_timings[i].name, aliases[a].canonical) != NULL) {
|
||||
return &protocol_timings[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const ProtoPirateProtocolTiming* protopirate_get_protocol_timing_by_index(size_t index) {
|
||||
if(index >= protocol_timings_count) return NULL;
|
||||
return &protocol_timings[index];
|
||||
}
|
||||
|
||||
size_t protopirate_get_protocol_timing_count(void) {
|
||||
return protocol_timings_count;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,62 +1,57 @@
|
||||
// protocols/protocol_items.h
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/types.h>
|
||||
|
||||
#include "kia_generic.h"
|
||||
#include "scher_khan.h"
|
||||
#include "kia_v0.h"
|
||||
#include "kia_v1.h"
|
||||
#include "kia_v2.h"
|
||||
#include "kia_v3_v4.h"
|
||||
#include "kia_v5.h"
|
||||
#include "kia_v6.h"
|
||||
#include "kia_v7.h"
|
||||
#include "ford_v0.h"
|
||||
#include "ford_v1.h"
|
||||
#include "ford_v2.h"
|
||||
#include "ford_v3.h"
|
||||
#include "chrysler_v0.h"
|
||||
#include "fiat_v0.h"
|
||||
#include "fiat_v1.h"
|
||||
#include "land_rover_v0.h"
|
||||
#include "mazda_v0.h"
|
||||
#include "porsche_touareg.h"
|
||||
#include "subaru.h"
|
||||
#include "vag.h"
|
||||
#include "star_line.h"
|
||||
#include "psa.h"
|
||||
#include "honda_static.h"
|
||||
#include "honda_v1.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
ProtoPirateProtocolRegistryFilterAM = 0,
|
||||
ProtoPirateProtocolRegistryFilterFM,
|
||||
} ProtoPirateProtocolRegistryFilter;
|
||||
ProtoPirateProtocolRegistryRouteAMDefault = 0,
|
||||
ProtoPirateProtocolRegistryRouteAMVag,
|
||||
ProtoPirateProtocolRegistryRouteFMDefault,
|
||||
ProtoPirateProtocolRegistryRouteFMF4,
|
||||
ProtoPirateProtocolRegistryRouteFMHonda1,
|
||||
} ProtoPirateProtocolRegistryRoute;
|
||||
|
||||
ProtoPirateProtocolRegistryFilter protopirate_get_protocol_registry_filter_for_preset(
|
||||
const uint8_t* preset_data,
|
||||
size_t preset_data_size);
|
||||
typedef enum {
|
||||
ProtoPirateProtocolCatalogRouteAMDefault = 0,
|
||||
ProtoPirateProtocolCatalogRouteAMVag,
|
||||
ProtoPirateProtocolCatalogRouteFMDefault,
|
||||
ProtoPirateProtocolCatalogRouteFMF4,
|
||||
ProtoPirateProtocolCatalogRouteFMHonda1,
|
||||
ProtoPirateProtocolCatalogRouteByModulation,
|
||||
} ProtoPirateProtocolCatalogRoutePolicy;
|
||||
|
||||
typedef struct {
|
||||
const char* canonical_name;
|
||||
ProtoPirateProtocolCatalogRoutePolicy route_policy;
|
||||
const char* tx_key;
|
||||
} ProtoPirateProtocolCatalogEntry;
|
||||
|
||||
const ProtoPirateProtocolCatalogEntry*
|
||||
protopirate_protocol_catalog_find(const char* protocol_name);
|
||||
|
||||
const char* protopirate_protocol_catalog_canonical_name(const char* protocol_name);
|
||||
|
||||
bool protopirate_protocol_catalog_can_tx(const char* protocol_name);
|
||||
|
||||
const char* protopirate_protocol_catalog_tx_key(const char* protocol_name);
|
||||
|
||||
const char*
|
||||
protopirate_get_protocol_registry_filter_name(ProtoPirateProtocolRegistryFilter filter);
|
||||
protopirate_protocol_catalog_display_name(const char* protocol_name, uint32_t protocol_type);
|
||||
|
||||
#ifdef ENABLE_TIMING_TUNER_SCENE
|
||||
// Timing information for protocol analysis
|
||||
typedef struct {
|
||||
const char* name;
|
||||
uint32_t te_short;
|
||||
uint32_t te_long;
|
||||
uint32_t te_delta;
|
||||
uint32_t min_count_bit;
|
||||
} ProtoPirateProtocolTiming;
|
||||
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);
|
||||
|
||||
// Get timing info for a protocol by name (returns NULL if not found)
|
||||
const ProtoPirateProtocolTiming* protopirate_get_protocol_timing(const char* protocol_name);
|
||||
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);
|
||||
|
||||
// Get timing info by index (for iteration)
|
||||
const ProtoPirateProtocolTiming* protopirate_get_protocol_timing_by_index(size_t index);
|
||||
|
||||
// Get number of protocols with timing info
|
||||
size_t protopirate_get_protocol_timing_count(void);
|
||||
#endif
|
||||
const char*
|
||||
protopirate_get_protocol_registry_route_name(ProtoPirateProtocolRegistryRoute route);
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
#include "protocol_timings.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <string.h>
|
||||
|
||||
static const ProtoPirateProtocolTiming protocol_timings[] = {
|
||||
{
|
||||
.name = "Honda Static",
|
||||
.te_short = 63,
|
||||
.te_long = 700,
|
||||
.te_delta = 120,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "Honda V1",
|
||||
.te_short = 1000,
|
||||
.te_long = 2000,
|
||||
.te_delta = 400,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "Kia V0",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 61,
|
||||
},
|
||||
{
|
||||
.name = "Kia V1",
|
||||
.te_short = 800,
|
||||
.te_long = 1600,
|
||||
.te_delta = 200,
|
||||
.min_count_bit = 56,
|
||||
},
|
||||
{
|
||||
.name = "Kia V2",
|
||||
.te_short = 500,
|
||||
.te_long = 1000,
|
||||
.te_delta = 150,
|
||||
.min_count_bit = 51,
|
||||
},
|
||||
{
|
||||
.name = "Kia V3/V4",
|
||||
.te_short = 400,
|
||||
.te_long = 800,
|
||||
.te_delta = 150,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "Kia V5",
|
||||
.te_short = 400,
|
||||
.te_long = 800,
|
||||
.te_delta = 150,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "Kia V6",
|
||||
.te_short = 200,
|
||||
.te_long = 400,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 144,
|
||||
},
|
||||
{
|
||||
.name = "Kia V7",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "Ford V0",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "Chrysler V0",
|
||||
.te_short = 300,
|
||||
.te_long = 3700,
|
||||
.te_delta = 400,
|
||||
.min_count_bit = 80,
|
||||
},
|
||||
{
|
||||
.name = "Ford V1",
|
||||
.te_short = 65,
|
||||
.te_long = 130,
|
||||
.te_delta = 39,
|
||||
.min_count_bit = 136,
|
||||
},
|
||||
{
|
||||
.name = "Ford V2",
|
||||
.te_short = 200,
|
||||
.te_long = 400,
|
||||
.te_delta = 260,
|
||||
.min_count_bit = 104,
|
||||
},
|
||||
{
|
||||
.name = "Ford V3",
|
||||
.te_short = 240,
|
||||
.te_long = 480,
|
||||
.te_delta = 60,
|
||||
.min_count_bit = 104,
|
||||
},
|
||||
{
|
||||
.name = "Fiat V0",
|
||||
.te_short = 200,
|
||||
.te_long = 400,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "Fiat V1",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 102,
|
||||
},
|
||||
{
|
||||
.name = "Fiat V2",
|
||||
.te_short = 210,
|
||||
.te_long = 420,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 112,
|
||||
},
|
||||
{
|
||||
.name = "Renault V0",
|
||||
.te_short = 125,
|
||||
.te_long = 250,
|
||||
.te_delta = 60,
|
||||
.min_count_bit = 82,
|
||||
},
|
||||
{
|
||||
.name = "Mazda V0",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "Honda V2",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 81,
|
||||
},
|
||||
{
|
||||
.name = "Porsche Touareg",
|
||||
.te_short = 1680,
|
||||
.te_long = 3370,
|
||||
.te_delta = 500,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "Subaru",
|
||||
.te_short = 800,
|
||||
.te_long = 1600,
|
||||
.te_delta = 200,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "VW",
|
||||
.te_short = 500,
|
||||
.te_long = 1000,
|
||||
.te_delta = 120,
|
||||
.min_count_bit = 80,
|
||||
},
|
||||
{
|
||||
.name = "Scher-Khan",
|
||||
.te_short = 750,
|
||||
.te_long = 1100,
|
||||
.te_delta = 180,
|
||||
.min_count_bit = 35,
|
||||
},
|
||||
{
|
||||
.name = "Star Line",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 120,
|
||||
.min_count_bit = 64,
|
||||
},
|
||||
{
|
||||
.name = "PSA",
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit = 128,
|
||||
},
|
||||
};
|
||||
|
||||
static const size_t protocol_timings_count = COUNT_OF(protocol_timings);
|
||||
|
||||
const ProtoPirateProtocolTiming* protopirate_get_protocol_timing(const char* protocol_name) {
|
||||
if(!protocol_name) return NULL;
|
||||
|
||||
for(size_t i = 0; i < protocol_timings_count; i++) {
|
||||
if(strcmp(protocol_name, protocol_timings[i].name) == 0 ||
|
||||
strstr(protocol_name, protocol_timings[i].name) != NULL) {
|
||||
return &protocol_timings[i];
|
||||
}
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char* alias;
|
||||
const char* canonical;
|
||||
} aliases[] = {
|
||||
{"Honda V0", "Kia V0"},
|
||||
{"Land Rover V0", "Honda V2"},
|
||||
{"Suzuki", "Kia V0"},
|
||||
{"V3", "Kia V3/V4"},
|
||||
{"V4", "Kia V3/V4"},
|
||||
};
|
||||
for(size_t a = 0; a < COUNT_OF(aliases); a++) {
|
||||
if(strstr(protocol_name, aliases[a].alias) == NULL) continue;
|
||||
for(size_t i = 0; i < protocol_timings_count; i++) {
|
||||
if(strstr(protocol_timings[i].name, aliases[a].canonical) != NULL) {
|
||||
return &protocol_timings[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const ProtoPirateProtocolTiming* protopirate_get_protocol_timing_by_index(size_t index) {
|
||||
if(index >= protocol_timings_count) return NULL;
|
||||
return &protocol_timings[index];
|
||||
}
|
||||
|
||||
size_t protopirate_get_protocol_timing_count(void) {
|
||||
return protocol_timings_count;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
uint32_t te_short;
|
||||
uint32_t te_long;
|
||||
uint32_t te_delta;
|
||||
uint32_t min_count_bit;
|
||||
} ProtoPirateProtocolTiming;
|
||||
|
||||
const ProtoPirateProtocolTiming* protopirate_get_protocol_timing(const char* protocol_name);
|
||||
const ProtoPirateProtocolTiming* protopirate_get_protocol_timing_by_index(size_t index);
|
||||
size_t protopirate_get_protocol_timing_count(void);
|
||||
@@ -174,7 +174,17 @@ uint32_t pp_encoder_read_repeat(FlipperFormat* ff, uint32_t default_repeat) {
|
||||
if(!ff) return default_repeat;
|
||||
flipper_format_rewind(ff);
|
||||
uint32_t tmp = 0;
|
||||
return flipper_format_read_uint32(ff, FF_REPEAT, &tmp, 1) ? tmp : default_repeat;
|
||||
if(!flipper_format_read_uint32(ff, FF_REPEAT, &tmp, 1)) {
|
||||
return default_repeat;
|
||||
}
|
||||
|
||||
if(tmp == 0) {
|
||||
return default_repeat;
|
||||
}
|
||||
if(tmp > PP_ENCODER_REPEAT_MAX) {
|
||||
tmp = PP_ENCODER_REPEAT_MAX;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus pp_serialize_fields(
|
||||
@@ -247,6 +257,8 @@ size_t
|
||||
return i;
|
||||
}
|
||||
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void pp_encoder_free(void* context) {
|
||||
furi_check(context);
|
||||
ProtoPirateEncoderHeader* hdr = context;
|
||||
@@ -304,6 +316,8 @@ void pp_encoder_buffer_ensure(void* context, size_t capacity) {
|
||||
hdr->encoder.size_upload = capacity;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uint8_t pp_decoder_hash_blocks(void* context) {
|
||||
furi_check(context);
|
||||
ProtoPirateDecoderHeader* hdr = context;
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
#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/subghz/protocols/base.h>
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
|
||||
#include "../defines.h"
|
||||
|
||||
extern const char FF_BIT[];
|
||||
extern const char FF_KEY[];
|
||||
extern const char FF_SERIAL[];
|
||||
@@ -52,6 +53,8 @@ void pp_encoder_read_fields(
|
||||
uint32_t* cnt_out,
|
||||
uint32_t* type_out);
|
||||
|
||||
#define PP_ENCODER_REPEAT_MAX 50U
|
||||
|
||||
uint32_t pp_encoder_read_repeat(FlipperFormat* ff, uint32_t default_repeat);
|
||||
|
||||
SubGhzProtocolStatus pp_serialize_fields(
|
||||
@@ -118,6 +121,12 @@ uint8_t pp_decoder_hash_blocks(void* context);
|
||||
|
||||
void pp_decoder_free_default(void* context);
|
||||
|
||||
#define PP_SHARED_UPLOAD_CAPACITY 2048U
|
||||
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
|
||||
typedef struct {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
@@ -127,10 +136,10 @@ void pp_encoder_free(void* context);
|
||||
void pp_encoder_stop(void* context);
|
||||
LevelDuration pp_encoder_yield(void* context);
|
||||
|
||||
#define PP_SHARED_UPLOAD_CAPACITY 2048U
|
||||
|
||||
void pp_encoder_buffer_ensure(void* context, size_t capacity);
|
||||
|
||||
LevelDuration* pp_shared_upload_buffer(void);
|
||||
size_t pp_shared_upload_capacity(void);
|
||||
void pp_shared_upload_release(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,10 +5,18 @@
|
||||
#include "protocol_items.h"
|
||||
|
||||
#define PROTOPIRATE_PROTOCOL_PLUGIN_APP_ID "protopirate_protocol_plugins"
|
||||
#define PROTOPIRATE_PROTOCOL_PLUGIN_API_VERSION 1U
|
||||
#define PROTOPIRATE_PROTOCOL_PLUGIN_API_VERSION 2U
|
||||
|
||||
typedef enum {
|
||||
ProtoPirateProtocolPluginKindRx = 0,
|
||||
ProtoPirateProtocolPluginKindTx,
|
||||
} ProtoPirateProtocolPluginKind;
|
||||
|
||||
typedef struct {
|
||||
const char* plugin_name;
|
||||
ProtoPirateProtocolRegistryFilter filter;
|
||||
ProtoPirateProtocolPluginKind kind;
|
||||
ProtoPirateProtocolRegistryRoute route;
|
||||
const char* protocol_name;
|
||||
const SubGhzProtocolRegistry* registry;
|
||||
void (*release)(void);
|
||||
} ProtoPirateProtocolPlugin;
|
||||
|
||||
@@ -72,7 +72,7 @@ struct SubGhzProtocolDecoderPSA {
|
||||
uint8_t decrypted_type;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
struct SubGhzProtocolEncoderPSA {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
@@ -102,7 +102,7 @@ const SubGhzProtocolDecoder subghz_protocol_psa_decoder = {
|
||||
.deserialize = subghz_protocol_decoder_psa_deserialize,
|
||||
.get_string = subghz_protocol_decoder_psa_get_string,
|
||||
};
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_psa_encoder = {
|
||||
.alloc = subghz_protocol_encoder_psa_alloc,
|
||||
.free = subghz_protocol_encoder_psa_free,
|
||||
@@ -125,13 +125,21 @@ const SubGhzProtocol psa_protocol = {
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Load,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_psa_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_psa_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void psa_calculate_checksum(uint8_t* buffer);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void psa_second_stage_xor_encrypt(uint8_t* buffer) {
|
||||
uint8_t E6 = buffer[8];
|
||||
uint8_t E7 = buffer[9];
|
||||
|
||||
@@ -19,7 +19,7 @@ const uint32_t psa_crypto_bf2_key_schedule[4] = {
|
||||
0x02192A04U,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void psa_crypto_tea_encrypt(uint32_t* v0, uint32_t* v1, const uint32_t* key) {
|
||||
uint32_t sum = 0;
|
||||
for(int i = 0; i < TEA_ROUNDS; i++) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "psa_bf_types.h"
|
||||
#include "../defines.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define PSA_CRYPTO_BF1_CONST_U4 0x0E0F5C41U
|
||||
@@ -24,6 +25,6 @@ uint8_t psa_crypto_tea_crc(uint32_t v0, uint32_t v1);
|
||||
uint16_t psa_crypto_crc16_bf2(uint8_t* buffer, int length);
|
||||
void psa_crypto_unpack_tea_result_to_buffer(uint8_t* buffer, uint32_t v0, uint32_t v1);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void psa_crypto_tea_encrypt(uint32_t* v0, uint32_t* v1, const uint32_t* key);
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
#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 "../defines.h"
|
||||
|
||||
#define RENAULT_PROTOCOL_V0_NAME "Renault V0"
|
||||
|
||||
extern const SubGhzProtocol renault_v0_protocol;
|
||||
|
||||
bool renault_v0_flipper_is_rolling(FlipperFormat* flipper_format);
|
||||
|
||||
void* subghz_protocol_decoder_renault_v0_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_renault_v0_reset(void* context);
|
||||
void subghz_protocol_decoder_renault_v0_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_renault_v0_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_renault_v0_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_renault_v0_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_renault_v0_get_string(void* context, FuriString* output);
|
||||
|
||||
void* subghz_protocol_encoder_renault_v0_alloc(SubGhzEnvironment* environment);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_renault_v0_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
@@ -61,13 +61,28 @@ const SubGhzProtocol subghz_protocol_scher_khan = {
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,
|
||||
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
|
||||
.decoder = &subghz_protocol_scher_khan_decoder,
|
||||
|
||||
#else
|
||||
|
||||
.decoder = NULL,
|
||||
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_scher_khan_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderScherKhan* instance = malloc(sizeof(SubGhzProtocolDecoderScherKhan));
|
||||
SubGhzProtocolDecoderScherKhan* instance = calloc(1, sizeof(SubGhzProtocolDecoderScherKhan));
|
||||
if(!instance) {
|
||||
return NULL;
|
||||
}
|
||||
instance->base.protocol = &subghz_protocol_scher_khan;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
@@ -314,7 +329,17 @@ SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderScherKhan* instance = context;
|
||||
return subghz_block_generic_deserialize(&instance->generic, flipper_format);
|
||||
SubGhzProtocolStatus status =
|
||||
subghz_block_generic_deserialize(&instance->generic, flipper_format);
|
||||
if(status != SubGhzProtocolStatusOk) {
|
||||
return status;
|
||||
}
|
||||
if(instance->generic.data_count_bit <
|
||||
subghz_protocol_scher_khan_const.min_count_bit_for_found ||
|
||||
instance->generic.data_count_bit > 64U) {
|
||||
return SubGhzProtocolStatusErrorValueBitCount;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output) {
|
||||
|
||||
@@ -66,7 +66,7 @@ const SubGhzProtocolDecoder subghz_protocol_star_line_decoder = {
|
||||
.get_string = subghz_protocol_decoder_star_line_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_star_line_encoder = {
|
||||
.alloc = subghz_protocol_encoder_star_line_alloc,
|
||||
.free = subghz_protocol_encoder_star_line_free,
|
||||
@@ -91,8 +91,20 @@ const SubGhzProtocol subghz_protocol_star_line = {
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
|
||||
.decoder = &subghz_protocol_star_line_decoder,
|
||||
|
||||
#else
|
||||
|
||||
.decoder = NULL,
|
||||
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_star_line_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -105,10 +117,10 @@ static void subghz_protocol_star_line_check_remote_controller(
|
||||
SubGhzBlockGeneric* instance,
|
||||
SubGhzKeystore* keystore,
|
||||
const char** manufacture_name);
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void* subghz_protocol_encoder_star_line_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolEncoderStarLine* instance = malloc(sizeof(SubGhzProtocolEncoderStarLine));
|
||||
SubGhzProtocolEncoderStarLine* instance = calloc(1, sizeof(SubGhzProtocolEncoderStarLine));
|
||||
furi_check(instance);
|
||||
|
||||
instance->base.protocol = &subghz_protocol_star_line;
|
||||
@@ -116,6 +128,7 @@ void* subghz_protocol_encoder_star_line_alloc(SubGhzEnvironment* environment) {
|
||||
instance->keystore = subghz_environment_get_keystore(environment);
|
||||
|
||||
instance->manufacture_from_file = furi_string_alloc();
|
||||
furi_check(instance->manufacture_from_file);
|
||||
|
||||
instance->encoder.repeat = 40;
|
||||
pp_encoder_buffer_ensure(instance, STAR_LINE_UPLOAD_CAPACITY);
|
||||
@@ -125,7 +138,7 @@ void* subghz_protocol_encoder_star_line_alloc(SubGhzEnvironment* environment) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
void subghz_protocol_encoder_star_line_free(void* context) {
|
||||
furi_check(context);
|
||||
@@ -241,7 +254,7 @@ bool subghz_protocol_star_line_create_data(
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance
|
||||
* @return true On success
|
||||
*/
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static bool subghz_protocol_encoder_star_line_get_upload(
|
||||
SubGhzProtocolEncoderStarLine* instance,
|
||||
uint8_t btn) {
|
||||
@@ -278,7 +291,7 @@ static bool subghz_protocol_encoder_star_line_get_upload(
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static SubGhzProtocolStatus subghz_protocol_encoder_star_line_serialize(
|
||||
SubGhzProtocolEncoderStarLine* instance,
|
||||
@@ -333,7 +346,7 @@ static SubGhzProtocolStatus subghz_protocol_encoder_star_line_serialize(
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
|
||||
@@ -69,7 +69,7 @@ const SubGhzProtocolDecoder subghz_protocol_subaru_decoder = {
|
||||
.get_string = subghz_protocol_decoder_subaru_get_string,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_subaru_encoder = {
|
||||
.alloc = subghz_protocol_encoder_subaru_alloc,
|
||||
.free = pp_encoder_free,
|
||||
@@ -93,8 +93,16 @@ const SubGhzProtocol subaru_protocol = {
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_subaru_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_subaru_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@@ -195,8 +203,11 @@ static uint8_t subghz_protocol_decoder_subaru_get_hash_data(void* context) {
|
||||
SubGhzProtocolDecoderSubaru* instance = context;
|
||||
const uint8_t* p = (const uint8_t*)&instance->decoder.decode_data;
|
||||
uint8_t hash = 0;
|
||||
const uint8_t bytes = (uint8_t)(instance->decoder.decode_count_bit >> 3);
|
||||
for(uint8_t i = 0; i <= bytes; i++) {
|
||||
size_t bytes = (size_t)(instance->decoder.decode_count_bit >> 3) + 1U;
|
||||
if(bytes > sizeof(instance->decoder.decode_data)) {
|
||||
bytes = sizeof(instance->decoder.decode_data);
|
||||
}
|
||||
for(size_t i = 0; i < bytes; i++) {
|
||||
hash ^= p[i];
|
||||
}
|
||||
return hash;
|
||||
@@ -205,7 +216,7 @@ static uint8_t subghz_protocol_decoder_subaru_get_hash_data(void* context) {
|
||||
// ============================================================================
|
||||
// ENCODER IMPLEMENTATION
|
||||
// ============================================================================
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void subaru_encode_count(uint8_t* KB, uint16_t count) {
|
||||
uint8_t lo = count & 0xFF;
|
||||
@@ -272,7 +283,10 @@ static void subaru_encode_count(uint8_t* KB, uint16_t count) {
|
||||
|
||||
void* subghz_protocol_encoder_subaru_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderSubaru* instance = malloc(sizeof(SubGhzProtocolEncoderSubaru));
|
||||
SubGhzProtocolEncoderSubaru* instance = calloc(1, sizeof(SubGhzProtocolEncoderSubaru));
|
||||
if(!instance) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
instance->base.protocol = &subaru_protocol;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
@@ -291,7 +305,7 @@ void* subghz_protocol_encoder_subaru_alloc(SubGhzEnvironment* environment) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
static void subghz_protocol_encoder_subaru_get_upload(SubGhzProtocolEncoderSubaru* instance) {
|
||||
furi_check(instance);
|
||||
@@ -342,7 +356,7 @@ static void subghz_protocol_encoder_subaru_get_upload(SubGhzProtocolEncoderSubar
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_subaru_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
@@ -423,7 +437,10 @@ SubGhzProtocolStatus
|
||||
|
||||
void* subghz_protocol_decoder_subaru_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderSubaru* instance = malloc(sizeof(SubGhzProtocolDecoderSubaru));
|
||||
SubGhzProtocolDecoderSubaru* instance = calloc(1, sizeof(SubGhzProtocolDecoderSubaru));
|
||||
if(!instance) {
|
||||
return NULL;
|
||||
}
|
||||
instance->base.protocol = &subaru_protocol;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
|
||||
@@ -13,13 +13,6 @@ _Static_assert(
|
||||
VAG_ENCODER_UPLOAD_MAX_SIZE <= PP_SHARED_UPLOAD_CAPACITY,
|
||||
"VAG_ENCODER_UPLOAD_MAX_SIZE exceeds shared upload slab");
|
||||
|
||||
static inline size_t
|
||||
vag_emit_manchester_inv(LevelDuration* up, size_t i, size_t cap, bool bit_value, uint32_t te) {
|
||||
i = pp_emit(up, i, cap, bit_value, te);
|
||||
i = pp_emit(up, i, cap, !bit_value, te);
|
||||
return i;
|
||||
}
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_vag_const = {
|
||||
.te_short = 500,
|
||||
.te_long = 1000,
|
||||
@@ -178,7 +171,7 @@ static void vag_tea_decrypt(uint32_t* v0, uint32_t* v1, const uint32_t* key_sche
|
||||
*v0 -= (((*v1 << 4) ^ (*v1 >> 5)) + *v1) ^ (sum + key_schedule[sum & 3]);
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
static void vag_tea_encrypt(uint32_t* v0, uint32_t* v1, const uint32_t* key_schedule) {
|
||||
uint32_t sum = 0;
|
||||
for(int i = 0; i < VAG_TEA_ROUNDS; i++) {
|
||||
@@ -511,7 +504,7 @@ const SubGhzProtocolDecoder subghz_protocol_vag_decoder = {
|
||||
.deserialize = subghz_protocol_decoder_vag_deserialize,
|
||||
.get_string = subghz_protocol_decoder_vag_get_string,
|
||||
};
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
const SubGhzProtocolEncoder subghz_protocol_vag_encoder = {
|
||||
.alloc = subghz_protocol_encoder_vag_alloc,
|
||||
.free = subghz_protocol_encoder_vag_free,
|
||||
@@ -534,8 +527,16 @@ const SubGhzProtocol vag_protocol = {
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
#if PROTOPIRATE_WITH_DECODER
|
||||
.decoder = &subghz_protocol_vag_decoder,
|
||||
#else
|
||||
.decoder = NULL,
|
||||
#endif
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
.encoder = &subghz_protocol_vag_encoder,
|
||||
#else
|
||||
.encoder = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
void* subghz_protocol_decoder_vag_alloc(SubGhzEnvironment* environment) {
|
||||
@@ -957,7 +958,7 @@ SubGhzProtocolStatus
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
|
||||
typedef struct SubGhzProtocolEncoderVAG {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
@@ -1132,7 +1133,7 @@ static void vag_encoder_build_type1(SubGhzProtocolEncoderVAG* instance) {
|
||||
#endif
|
||||
for(int i = 15; i >= 0; i--) {
|
||||
bool bit = (prefix >> i) & 1;
|
||||
index = vag_emit_manchester_inv(upload, index, cap, bit, 300);
|
||||
index = pp_emit_manchester_bit(upload, index, cap, bit, 300);
|
||||
}
|
||||
FURI_LOG_D(TAG, "Prefix 0x%04X: %zu pulses", prefix, index - prefix_start);
|
||||
|
||||
@@ -1151,7 +1152,7 @@ static void vag_encoder_build_type1(SubGhzProtocolEncoderVAG* instance) {
|
||||
#endif
|
||||
for(int i = 63; i >= 0; i--) {
|
||||
bool bit = (key1_inv >> i) & 1;
|
||||
index = vag_emit_manchester_inv(upload, index, cap, bit, 300);
|
||||
index = pp_emit_manchester_bit(upload, index, cap, bit, 300);
|
||||
}
|
||||
FURI_LOG_D(TAG, "Key1: %zu pulses (64 bits)", index - key1_start);
|
||||
|
||||
@@ -1163,7 +1164,7 @@ static void vag_encoder_build_type1(SubGhzProtocolEncoderVAG* instance) {
|
||||
#endif
|
||||
for(int i = 15; i >= 0; i--) {
|
||||
bool bit = (key2_inv >> i) & 1;
|
||||
index = vag_emit_manchester_inv(upload, index, cap, bit, 300);
|
||||
index = pp_emit_manchester_bit(upload, index, cap, bit, 300);
|
||||
}
|
||||
FURI_LOG_D(TAG, "Key2: %zu pulses (16 bits)", index - key2_start);
|
||||
|
||||
@@ -1272,7 +1273,7 @@ static void vag_encoder_build_type2(SubGhzProtocolEncoderVAG* instance) {
|
||||
#endif
|
||||
for(int i = 15; i >= 0; i--) {
|
||||
bool bit = (prefix >> i) & 1;
|
||||
index = vag_emit_manchester_inv(upload, index, cap, bit, 300);
|
||||
index = pp_emit_manchester_bit(upload, index, cap, bit, 300);
|
||||
}
|
||||
FURI_LOG_D(TAG, "Prefix 0x%04X: %zu pulses", prefix, index - prefix_start);
|
||||
|
||||
@@ -1290,7 +1291,7 @@ static void vag_encoder_build_type2(SubGhzProtocolEncoderVAG* instance) {
|
||||
#endif
|
||||
for(int i = 63; i >= 0; i--) {
|
||||
bool bit = (key1_inv >> i) & 1;
|
||||
index = vag_emit_manchester_inv(upload, index, cap, bit, 300);
|
||||
index = pp_emit_manchester_bit(upload, index, cap, bit, 300);
|
||||
}
|
||||
FURI_LOG_D(TAG, "Key1: %zu pulses", index - key1_start);
|
||||
|
||||
@@ -1302,7 +1303,7 @@ static void vag_encoder_build_type2(SubGhzProtocolEncoderVAG* instance) {
|
||||
#endif
|
||||
for(int i = 15; i >= 0; i--) {
|
||||
bool bit = (key2_inv >> i) & 1;
|
||||
index = vag_emit_manchester_inv(upload, index, cap, bit, 300);
|
||||
index = pp_emit_manchester_bit(upload, index, cap, bit, 300);
|
||||
}
|
||||
FURI_LOG_D(TAG, "Key2: %zu pulses", index - key2_start);
|
||||
|
||||
@@ -1438,7 +1439,7 @@ static void vag_encoder_build_type3_4(SubGhzProtocolEncoderVAG* instance) {
|
||||
for(int i = 63; i >= 0; i--) {
|
||||
bool bit = (key1 >> i) & 1;
|
||||
|
||||
index = vag_emit_manchester_inv(upload, index, cap, bit, 500);
|
||||
index = pp_emit_manchester_bit(upload, index, cap, bit, 500);
|
||||
}
|
||||
FURI_LOG_D(TAG, "Repeat %d: Key1 %zu pulses (64 bits)", repeat + 1, index - key1_start);
|
||||
|
||||
@@ -1447,7 +1448,7 @@ static void vag_encoder_build_type3_4(SubGhzProtocolEncoderVAG* instance) {
|
||||
#endif
|
||||
for(int i = 15; i >= 0; i--) {
|
||||
bool bit = (key2 >> i) & 1;
|
||||
index = vag_emit_manchester_inv(upload, index, cap, bit, 500);
|
||||
index = pp_emit_manchester_bit(upload, index, cap, bit, 500);
|
||||
}
|
||||
FURI_LOG_D(TAG, "Repeat %d: Key2 %zu pulses (16 bits)", repeat + 1, index - key2_start);
|
||||
|
||||
@@ -1528,7 +1529,7 @@ void subghz_protocol_decoder_vag_get_string(void* context, FuriString* output) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#if PROTOPIRATE_WITH_ENCODER
|
||||
void* subghz_protocol_encoder_vag_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
FURI_LOG_I(TAG, "VAG encoder alloc");
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "protocols/protocol_items.h"
|
||||
#include "protocols/protocols_common.h"
|
||||
#include "helpers/protopirate_settings.h"
|
||||
#include "helpers/protopirate_storage.h"
|
||||
#include "helpers/protopirate_psa_bf_host.h"
|
||||
#include "protocols/keys.h"
|
||||
#include "helpers/protopirate_views.h"
|
||||
#include "helpers/protopirate_radio.h"
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "ProtoPirateApp"
|
||||
@@ -31,132 +30,6 @@ static void protopirate_app_tick_event_callback(void* context) {
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
bool protopirate_ensure_variable_item_list(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(app->variable_item_list) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->variable_item_list = variable_item_list_alloc();
|
||||
if(!app->variable_item_list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
ProtoPirateViewVariableItemList,
|
||||
variable_item_list_get_view(app->variable_item_list));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_ensure_widget(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(app->widget) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->widget = widget_alloc();
|
||||
if(!app->widget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, ProtoPirateViewWidget, widget_get_view(app->widget));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_ensure_text_input(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(app->text_input) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->text_input = text_input_alloc();
|
||||
if(!app->text_input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, ProtoPirateViewTextInput, text_input_get_view(app->text_input));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_ensure_view_about(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(app->view_about) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->view_about = view_alloc();
|
||||
if(!app->view_about) {
|
||||
return false;
|
||||
}
|
||||
|
||||
view_dispatcher_add_view(app->view_dispatcher, ProtoPirateViewAbout, app->view_about);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_ensure_receiver_view(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(app->protopirate_receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
app->protopirate_receiver = protopirate_view_receiver_alloc(app->auto_save);
|
||||
if(!app->protopirate_receiver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
ProtoPirateViewReceiver,
|
||||
protopirate_view_receiver_get_view(app->protopirate_receiver));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void protopirate_radio_init_cleanup(ProtoPirateApp* app, bool devices_initialized) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
if(app->txrx->receiver) {
|
||||
subghz_receiver_free(app->txrx->receiver);
|
||||
app->txrx->receiver = NULL;
|
||||
}
|
||||
|
||||
if(app->txrx->radio_device) {
|
||||
if(devices_initialized) {
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
}
|
||||
radio_device_loader_end(app->txrx->radio_device);
|
||||
app->txrx->radio_device = NULL;
|
||||
}
|
||||
|
||||
if(app->txrx->environment) {
|
||||
subghz_environment_free(app->txrx->environment);
|
||||
app->txrx->environment = NULL;
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_plugin_manager) {
|
||||
plugin_manager_free(app->txrx->protocol_plugin_manager);
|
||||
app->txrx->protocol_plugin_manager = NULL;
|
||||
}
|
||||
|
||||
if(app->txrx->plugin_resolver) {
|
||||
composite_api_resolver_free(app->txrx->plugin_resolver);
|
||||
app->txrx->plugin_resolver = NULL;
|
||||
}
|
||||
|
||||
if(devices_initialized) {
|
||||
subghz_devices_deinit();
|
||||
}
|
||||
|
||||
app->txrx->protocol_registry = NULL;
|
||||
app->txrx->protocol_plugin = NULL;
|
||||
app->txrx->protocol_registry_filter = ProtoPirateProtocolRegistryFilterAM;
|
||||
app->txrx->txrx_state = ProtoPirateTxRxStateIDLE;
|
||||
app->radio_initialized = false;
|
||||
}
|
||||
|
||||
ProtoPirateApp* protopirate_app_alloc() {
|
||||
protopirate_storage_purge_temp_history_at_startup();
|
||||
ProtoPirateApp* app = malloc(sizeof(ProtoPirateApp));
|
||||
@@ -215,6 +88,7 @@ ProtoPirateApp* protopirate_app_alloc() {
|
||||
|
||||
// Apply auto-save setting
|
||||
app->auto_save = settings.auto_save;
|
||||
app->check_saved = settings.check_saved;
|
||||
app->tx_power = settings.tx_power;
|
||||
app->emulate_feature_enabled = settings.emulate_feature_enabled;
|
||||
|
||||
@@ -259,7 +133,7 @@ ProtoPirateApp* protopirate_app_alloc() {
|
||||
furi_check(app->txrx->preset->name);
|
||||
app->txrx->txrx_state = ProtoPirateTxRxStateIDLE;
|
||||
app->txrx->rx_key_state = ProtoPirateRxKeyStateIDLE;
|
||||
app->txrx->protocol_registry_filter = ProtoPirateProtocolRegistryFilterAM;
|
||||
app->txrx->protocol_registry_route = ProtoPirateProtocolRegistryRouteAMDefault;
|
||||
|
||||
// Get preset name and data
|
||||
const char* preset_name = subghz_setting_get_preset_name(app->setting, preset_index);
|
||||
@@ -288,190 +162,6 @@ ProtoPirateApp* protopirate_app_alloc() {
|
||||
return app;
|
||||
}
|
||||
|
||||
bool protopirate_radio_init(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
furi_check(app->txrx);
|
||||
|
||||
FURI_LOG_I(TAG, "=== protopirate_radio_init called ===");
|
||||
FURI_LOG_D(TAG, "State: radio_initialized=%d", app->radio_initialized);
|
||||
|
||||
if(app->radio_initialized) {
|
||||
const bool radio_ready = (app->txrx->environment != NULL) &&
|
||||
(app->txrx->radio_device != NULL);
|
||||
if(radio_ready) {
|
||||
FURI_LOG_D(TAG, "Radio already initialized, returning true");
|
||||
return true;
|
||||
}
|
||||
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
"Radio marked initialized but resources missing (env=%p device=%p), repairing",
|
||||
app->txrx->environment,
|
||||
app->txrx->radio_device);
|
||||
protopirate_radio_deinit(app);
|
||||
}
|
||||
|
||||
// Fresh radio init - nothing was initialized before
|
||||
FURI_LOG_I(TAG, "Fresh radio init - allocating all components");
|
||||
|
||||
// Create environment with our custom protocols
|
||||
app->txrx->environment = subghz_environment_alloc();
|
||||
if(!app->txrx->environment) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate environment!");
|
||||
protopirate_radio_init_cleanup(app, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
app->txrx->protocol_registry = NULL;
|
||||
|
||||
if(!protopirate_refresh_protocol_registry(app, false)) {
|
||||
FURI_LOG_E(TAG, "Failed to configure protocol registry");
|
||||
protopirate_radio_init_cleanup(app, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load keystores
|
||||
subghz_environment_load_keystore(app->txrx->environment, PROTOPIRATE_KEYSTORE_DIR_NAME);
|
||||
|
||||
// Load ProtoPirate specific keys
|
||||
protopirate_keys_load(app->txrx->environment);
|
||||
FURI_LOG_I(TAG, "Loaded ProtoPirate secure keys");
|
||||
|
||||
// Initialize SubGhz devices
|
||||
subghz_devices_init();
|
||||
FURI_LOG_D(TAG, "SubGhz devices initialized");
|
||||
|
||||
// Try external CC1101 first
|
||||
app->txrx->radio_device = radio_device_loader_set(NULL, SubGhzRadioDeviceTypeExternalCC1101);
|
||||
|
||||
// if not loading, fallback to internal
|
||||
if(!app->txrx->radio_device) {
|
||||
FURI_LOG_W(TAG, "External CC1101 not found, trying internal radio");
|
||||
app->txrx->radio_device = radio_device_loader_set(NULL, SubGhzRadioDeviceTypeInternal);
|
||||
}
|
||||
|
||||
if(!app->txrx->radio_device) {
|
||||
FURI_LOG_E(TAG, "Failed to initialize any radio device!");
|
||||
protopirate_radio_init_cleanup(app, true);
|
||||
return false;
|
||||
}
|
||||
#ifndef REMOVE_LOGS
|
||||
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)",
|
||||
device_name ? device_name : "unknown",
|
||||
is_external ? "external" : "internal");
|
||||
#endif
|
||||
subghz_devices_reset(app->txrx->radio_device);
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
|
||||
app->radio_initialized = true;
|
||||
|
||||
FURI_LOG_D(TAG, "Final state: radio_initialized=%d", app->radio_initialized);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Deinitialize radio subsystem
|
||||
void protopirate_radio_deinit(ProtoPirateApp* app) {
|
||||
FURI_LOG_I(TAG, "=== protopirate_radio_deinit called ===");
|
||||
FURI_LOG_D(TAG, "State: radio_initialized=%d", app->radio_initialized);
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Pointers: worker=%p, environment=%p, receiver=%p, history=%p, radio_device=%p",
|
||||
app->txrx->worker,
|
||||
app->txrx->environment,
|
||||
app->txrx->receiver,
|
||||
app->txrx->history,
|
||||
app->txrx->radio_device);
|
||||
|
||||
bool has_radio_resources = app->radio_initialized || app->txrx->worker ||
|
||||
app->txrx->environment || app->txrx->receiver ||
|
||||
app->txrx->history || app->txrx->radio_device;
|
||||
if(!has_radio_resources) {
|
||||
FURI_LOG_D(TAG, "Radio resources were not initialized, returning");
|
||||
return;
|
||||
}
|
||||
|
||||
bool devices_initialized = app->radio_initialized || (app->txrx->radio_device != NULL);
|
||||
|
||||
// Make sure we're not receiving
|
||||
if(app->txrx->worker && app->txrx->txrx_state == ProtoPirateTxRxStateRx) {
|
||||
FURI_LOG_D(TAG, "Stopping active RX, state=%d", app->txrx->txrx_state);
|
||||
subghz_worker_stop(app->txrx->worker);
|
||||
if(app->txrx->radio_device) {
|
||||
subghz_devices_stop_async_rx(app->txrx->radio_device);
|
||||
}
|
||||
}
|
||||
|
||||
if(app->txrx->radio_device) {
|
||||
FURI_LOG_D(TAG, "Putting radio device to sleep and ending: %p", app->txrx->radio_device);
|
||||
subghz_devices_sleep(app->txrx->radio_device);
|
||||
radio_device_loader_end(app->txrx->radio_device);
|
||||
app->txrx->radio_device = NULL;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Radio device was NULL, skipping sleep/end");
|
||||
}
|
||||
|
||||
if(devices_initialized) {
|
||||
FURI_LOG_D(TAG, "Calling subghz_devices_deinit");
|
||||
subghz_devices_deinit();
|
||||
}
|
||||
|
||||
if(app->txrx->receiver) {
|
||||
FURI_LOG_D(TAG, "Freeing receiver %p", app->txrx->receiver);
|
||||
subghz_receiver_free(app->txrx->receiver);
|
||||
app->txrx->receiver = NULL;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Receiver was NULL, skipping free");
|
||||
}
|
||||
|
||||
if(app->txrx->environment) {
|
||||
FURI_LOG_D(TAG, "Freeing environment %p", app->txrx->environment);
|
||||
subghz_environment_free(app->txrx->environment);
|
||||
app->txrx->environment = NULL;
|
||||
app->txrx->protocol_registry = NULL;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Environment was NULL, skipping free");
|
||||
}
|
||||
|
||||
if(app->txrx->protocol_plugin_manager) {
|
||||
FURI_LOG_D(TAG, "Freeing protocol plugin manager %p", app->txrx->protocol_plugin_manager);
|
||||
plugin_manager_free(app->txrx->protocol_plugin_manager);
|
||||
app->txrx->protocol_plugin_manager = NULL;
|
||||
}
|
||||
|
||||
if(app->txrx->plugin_resolver) {
|
||||
FURI_LOG_D(TAG, "Freeing plugin resolver %p", app->txrx->plugin_resolver);
|
||||
composite_api_resolver_free(app->txrx->plugin_resolver);
|
||||
app->txrx->plugin_resolver = NULL;
|
||||
}
|
||||
app->txrx->protocol_plugin = NULL;
|
||||
|
||||
if(app->txrx->history) {
|
||||
FURI_LOG_D(TAG, "Freeing history %p", app->txrx->history);
|
||||
protopirate_history_free(app->txrx->history);
|
||||
app->txrx->history = NULL;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "History was NULL, skipping free");
|
||||
}
|
||||
|
||||
if(app->txrx->worker) {
|
||||
FURI_LOG_D(TAG, "Freeing worker %p", app->txrx->worker);
|
||||
subghz_worker_free(app->txrx->worker);
|
||||
app->txrx->worker = NULL;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Worker was NULL, skipping free");
|
||||
}
|
||||
|
||||
app->txrx->txrx_state = ProtoPirateTxRxStateIDLE;
|
||||
app->radio_initialized = false;
|
||||
|
||||
FURI_LOG_D(TAG, "Final state: radio_initialized=%d", app->radio_initialized);
|
||||
}
|
||||
|
||||
void protopirate_app_free(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
|
||||
@@ -482,6 +172,7 @@ void protopirate_app_free(ProtoPirateApp* app) {
|
||||
ProtoPirateSettings settings;
|
||||
settings.frequency = app->txrx->preset->frequency;
|
||||
settings.auto_save = app->auto_save;
|
||||
settings.check_saved = app->check_saved;
|
||||
settings.tx_power = app->tx_power;
|
||||
settings.hopping_enabled = (app->txrx->hopper_state != ProtoPirateHopperStateOFF);
|
||||
settings.emulate_feature_enabled = app->emulate_feature_enabled;
|
||||
@@ -507,7 +198,11 @@ void protopirate_app_free(ProtoPirateApp* app) {
|
||||
|
||||
protopirate_settings_save(&settings);
|
||||
|
||||
// Deinitialize whichever is active - NULL checks inside handle all cases
|
||||
protopirate_tool_scene_plugin_release(app);
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
protopirate_emulate_context_release(app);
|
||||
#endif
|
||||
|
||||
FURI_LOG_D(TAG, "Calling radio_deinit");
|
||||
protopirate_radio_deinit(app);
|
||||
|
||||
@@ -517,93 +212,42 @@ void protopirate_app_free(ProtoPirateApp* app) {
|
||||
app->loaded_file_path = NULL;
|
||||
}
|
||||
|
||||
// Submenu
|
||||
if(app->submenu) {
|
||||
FURI_LOG_D(TAG, "Removing submenu view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
}
|
||||
protopirate_views_free(app);
|
||||
|
||||
// Variable Item List
|
||||
if(app->variable_item_list) {
|
||||
FURI_LOG_D(TAG, "Removing variable_item_list view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewVariableItemList);
|
||||
variable_item_list_free(app->variable_item_list);
|
||||
}
|
||||
|
||||
// About View
|
||||
if(app->view_about) {
|
||||
FURI_LOG_D(TAG, "Removing about view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewAbout);
|
||||
view_free(app->view_about);
|
||||
}
|
||||
|
||||
// File path
|
||||
if(app->file_path) {
|
||||
FURI_LOG_D(TAG, "Freeing file_path");
|
||||
furi_string_free(app->file_path);
|
||||
app->file_path = NULL;
|
||||
}
|
||||
|
||||
// Widget
|
||||
if(app->widget) {
|
||||
FURI_LOG_D(TAG, "Removing widget view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewWidget);
|
||||
widget_free(app->widget);
|
||||
}
|
||||
|
||||
// Text Input
|
||||
if(app->text_input) {
|
||||
FURI_LOG_D(TAG, "Removing text_input view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewTextInput);
|
||||
text_input_free(app->text_input);
|
||||
}
|
||||
if(app->save_protocol) {
|
||||
furi_string_free(app->save_protocol);
|
||||
app->save_protocol = NULL;
|
||||
}
|
||||
|
||||
// Receiver
|
||||
if(app->protopirate_receiver) {
|
||||
FURI_LOG_D(TAG, "Removing receiver view");
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ProtoPirateViewReceiver);
|
||||
protopirate_view_receiver_free(app->protopirate_receiver);
|
||||
}
|
||||
|
||||
protopirate_psa_bf_context_release(app);
|
||||
|
||||
// Setting
|
||||
FURI_LOG_D(TAG, "Freeing subghz_setting");
|
||||
subghz_setting_free(app->setting);
|
||||
|
||||
// Free preset
|
||||
FURI_LOG_D(TAG, "Freeing preset");
|
||||
furi_string_free(app->txrx->preset->name);
|
||||
free(app->txrx->preset);
|
||||
|
||||
free(app->txrx);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
protopirate_emulate_context_release(app);
|
||||
#endif
|
||||
|
||||
pp_shared_upload_release();
|
||||
|
||||
// View dispatcher
|
||||
FURI_LOG_D(TAG, "Freeing view_dispatcher and scene_manager");
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// Close Dialogs
|
||||
FURI_LOG_D(TAG, "Closing dialogs record");
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
app->dialogs = NULL;
|
||||
|
||||
// Notifications
|
||||
FURI_LOG_D(TAG, "Closing notifications record");
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
app->notifications = NULL;
|
||||
|
||||
// Close records
|
||||
FURI_LOG_D(TAG, "Closing GUI record");
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
@@ -616,7 +260,6 @@ int32_t protopirate_app(char* p) {
|
||||
|
||||
ProtoPirateApp* protopirate_app = protopirate_app_alloc();
|
||||
if(!protopirate_app) {
|
||||
// logging is already done in protopirate_app_alloc()
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -34,12 +34,17 @@
|
||||
#include "scenes/plugins/protopirate_emulate_plugin.h"
|
||||
#endif
|
||||
#include "scenes/plugins/protopirate_psa_bf_plugin.h"
|
||||
#include "scenes/plugins/protopirate_tool_scene_plugin.h"
|
||||
#include "helpers/protopirate_views.h"
|
||||
#include "helpers/protopirate_radio.h"
|
||||
#include "helpers/protopirate_protocol_plugin_host.h"
|
||||
#include "helpers/protopirate_txrx.h"
|
||||
|
||||
#define PROTOPIRATE_KEYSTORE_DIR_NAME APP_ASSETS_PATH("encrypted")
|
||||
|
||||
typedef struct ProtoPirateApp ProtoPirateApp;
|
||||
|
||||
typedef struct {
|
||||
typedef struct ProtoPirateTxRx {
|
||||
SubGhzWorker* worker;
|
||||
SubGhzEnvironment* environment;
|
||||
SubGhzReceiver* receiver;
|
||||
@@ -48,7 +53,7 @@ typedef struct {
|
||||
CompositeApiResolver* plugin_resolver;
|
||||
PluginManager* protocol_plugin_manager;
|
||||
const ProtoPirateProtocolPlugin* protocol_plugin;
|
||||
ProtoPirateProtocolRegistryFilter protocol_registry_filter;
|
||||
ProtoPirateProtocolRegistryRoute protocol_registry_route;
|
||||
ProtoPirateHistory* history;
|
||||
const SubGhzDevice* radio_device;
|
||||
ProtoPirateTxRxState txrx_state;
|
||||
@@ -77,8 +82,8 @@ struct ProtoPirateApp {
|
||||
ProtoPirateLock lock;
|
||||
FuriString* loaded_file_path;
|
||||
bool auto_save;
|
||||
bool check_saved;
|
||||
bool radio_initialized;
|
||||
ProtoPirateSettings settings;
|
||||
uint32_t start_tx_time;
|
||||
uint8_t tx_power;
|
||||
char save_filename[64];
|
||||
@@ -99,6 +104,17 @@ struct ProtoPirateApp {
|
||||
CompositeApiResolver* psa_bf_plugin_resolver;
|
||||
PluginManager* psa_bf_plugin_manager;
|
||||
const ProtoPiratePsaBfPlugin* psa_bf_plugin;
|
||||
CompositeApiResolver* tool_scene_plugin_resolver;
|
||||
PluginManager* tool_scene_plugin_manager;
|
||||
const ProtoPirateToolScenePlugin* tool_scene_plugin;
|
||||
ProtoPirateToolScenePluginKind tool_scene_plugin_kind;
|
||||
|
||||
#define TOOL_SCENE_NAV_NONE 0U
|
||||
#define TOOL_SCENE_NAV_POP 1U
|
||||
#define TOOL_SCENE_NAV_NEXT 2U
|
||||
#define TOOL_SCENE_NAV_SEARCH_PREVIOUS 3U
|
||||
uint8_t tool_scene_nav_pending;
|
||||
uint32_t tool_scene_nav_target;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
@@ -110,49 +126,10 @@ typedef enum {
|
||||
ProtoPirateSetTypeMAX,
|
||||
} ProtoPirateSetType;
|
||||
|
||||
void protopirate_preset_init(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint8_t* preset_data,
|
||||
size_t preset_data_size);
|
||||
|
||||
void protopirate_get_frequency_modulation(
|
||||
ProtoPirateApp* app,
|
||||
FuriString* frequency,
|
||||
FuriString* modulation);
|
||||
void protopirate_get_frequency_modulation_str(
|
||||
ProtoPirateApp* app,
|
||||
char* frequency,
|
||||
size_t frequency_size,
|
||||
char* modulation,
|
||||
size_t modulation_size);
|
||||
|
||||
void protopirate_begin(ProtoPirateApp* app, uint8_t* preset_data);
|
||||
uint32_t protopirate_rx(ProtoPirateApp* app, uint32_t frequency);
|
||||
void protopirate_idle(ProtoPirateApp* app);
|
||||
void protopirate_rx_end(ProtoPirateApp* app);
|
||||
void protopirate_sleep(ProtoPirateApp* app);
|
||||
void protopirate_hopper_update(ProtoPirateApp* app);
|
||||
void protopirate_tx(ProtoPirateApp* app, uint32_t frequency);
|
||||
void protopirate_tx_stop(ProtoPirateApp* app);
|
||||
bool protopirate_radio_init(ProtoPirateApp* app);
|
||||
void protopirate_radio_deinit(ProtoPirateApp* app);
|
||||
bool protopirate_refresh_protocol_registry(ProtoPirateApp* app, bool ensure_receiver_ready);
|
||||
bool protopirate_apply_protocol_registry_for_preset_data(
|
||||
ProtoPirateApp* app,
|
||||
const uint8_t* preset_data,
|
||||
size_t preset_data_size);
|
||||
bool protopirate_ensure_variable_item_list(ProtoPirateApp* app);
|
||||
bool protopirate_ensure_widget(ProtoPirateApp* app);
|
||||
bool protopirate_ensure_text_input(ProtoPirateApp* app);
|
||||
bool protopirate_ensure_view_about(ProtoPirateApp* app);
|
||||
bool protopirate_ensure_receiver_view(ProtoPirateApp* app);
|
||||
void protopirate_release_shared_radio_state(ProtoPirateApp* app);
|
||||
|
||||
void protopirate_rx_stack_suspend_for_tx(ProtoPirateApp* app);
|
||||
|
||||
void protopirate_rx_stack_resume_after_tx(ProtoPirateApp* app);
|
||||
bool protopirate_tool_scene_on_enter(void* app, ProtoPirateToolScenePluginKind kind);
|
||||
bool protopirate_tool_scene_on_event(void* app, SceneManagerEvent event);
|
||||
void protopirate_tool_scene_on_exit(void* app);
|
||||
void protopirate_tool_scene_plugin_release(ProtoPirateApp* app);
|
||||
|
||||
void protopirate_app_free(ProtoPirateApp* app);
|
||||
|
||||
|
||||
@@ -12,12 +12,29 @@
|
||||
#define HISTORY_SCRATCH_TEXT_RESERVE 256U
|
||||
#define HISTORY_SCRATCH_PATH_RESERVE 128U
|
||||
#define HISTORY_ARENA_RESERVE 1024U
|
||||
#define HISTORY_DUPLICATE_WINDOW 500U
|
||||
|
||||
typedef enum {
|
||||
ProtoPirateSavedMatchStateNone = 0,
|
||||
ProtoPirateSavedMatchStatePending,
|
||||
ProtoPirateSavedMatchStateDone,
|
||||
} ProtoPirateSavedMatchState;
|
||||
|
||||
typedef enum {
|
||||
ProtoPirateAutoSaveStateNone = 0,
|
||||
ProtoPirateAutoSaveStatePending,
|
||||
ProtoPirateAutoSaveStateDone,
|
||||
} ProtoPirateAutoSaveState;
|
||||
|
||||
typedef struct {
|
||||
uint32_t seq_id;
|
||||
uint16_t text_offset;
|
||||
uint16_t text_len;
|
||||
uint8_t type;
|
||||
uint8_t saved_match_state;
|
||||
uint8_t auto_save_state;
|
||||
FuriString* matched_saved_path;
|
||||
FuriString* matched_name;
|
||||
} ProtoPirateHistoryItem;
|
||||
|
||||
ARRAY_DEF(ProtoPirateHistoryItemArray, ProtoPirateHistoryItem, M_POD_OPLIST)
|
||||
@@ -58,6 +75,26 @@ static void
|
||||
protopirate_storage_delete_file(furi_string_get_cstr(instance->scratch_path));
|
||||
}
|
||||
|
||||
static void protopirate_history_item_clear_matched(ProtoPirateHistoryItem* item) {
|
||||
if(!item) return;
|
||||
if(item->matched_saved_path) {
|
||||
furi_string_free(item->matched_saved_path);
|
||||
item->matched_saved_path = NULL;
|
||||
}
|
||||
if(item->matched_name) {
|
||||
furi_string_free(item->matched_name);
|
||||
item->matched_name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void protopirate_history_clear_all_matched(ProtoPirateHistory* instance) {
|
||||
size_t n = ProtoPirateHistoryItemArray_size(instance->data);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
ProtoPirateHistoryItem* item = ProtoPirateHistoryItemArray_get(instance->data, i);
|
||||
protopirate_history_item_clear_matched(item);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
protopirate_history_arena_remove(ProtoPirateHistory* instance, uint16_t offset, uint16_t len) {
|
||||
if(len == 0) return;
|
||||
@@ -116,6 +153,7 @@ ProtoPirateHistory* protopirate_history_alloc(void) {
|
||||
void protopirate_history_free(ProtoPirateHistory* instance) {
|
||||
furi_check(instance);
|
||||
protopirate_history_release_scratch(instance);
|
||||
protopirate_history_clear_all_matched(instance);
|
||||
ProtoPirateHistoryItemArray_clear(instance->data);
|
||||
protopirate_storage_wipe_history_cache();
|
||||
|
||||
@@ -142,6 +180,7 @@ void protopirate_history_free(ProtoPirateHistory* instance) {
|
||||
void protopirate_history_reset(ProtoPirateHistory* instance) {
|
||||
furi_check(instance);
|
||||
protopirate_history_release_scratch(instance);
|
||||
protopirate_history_clear_all_matched(instance);
|
||||
ProtoPirateHistoryItemArray_reset(instance->data);
|
||||
furi_string_reset(instance->text_arena);
|
||||
instance->last_index = 0;
|
||||
@@ -216,10 +255,11 @@ bool protopirate_history_capture_path_equals(
|
||||
return strcmp(furi_string_get_cstr(instance->scratch_path), path) == 0;
|
||||
}
|
||||
|
||||
bool protopirate_history_add_to_history(
|
||||
bool protopirate_history_add_to_history_at(
|
||||
ProtoPirateHistory* instance,
|
||||
void* context,
|
||||
SubGhzRadioPreset* preset) {
|
||||
SubGhzRadioPreset* preset,
|
||||
uint32_t update_timestamp) {
|
||||
furi_check(instance);
|
||||
furi_check(context);
|
||||
|
||||
@@ -229,10 +269,11 @@ bool protopirate_history_add_to_history(
|
||||
|
||||
SubGhzProtocolDecoderBase* decoder_base = context;
|
||||
|
||||
if((instance->code_last_hash_data ==
|
||||
if((ProtoPirateHistoryItemArray_size(instance->data) > 0) &&
|
||||
(instance->code_last_hash_data ==
|
||||
subghz_protocol_decoder_base_get_hash_data(decoder_base)) &&
|
||||
((furi_get_tick() - instance->last_update_timestamp) < 500)) {
|
||||
instance->last_update_timestamp = furi_get_tick();
|
||||
((update_timestamp - instance->last_update_timestamp) < HISTORY_DUPLICATE_WINDOW)) {
|
||||
instance->last_update_timestamp = update_timestamp;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -264,7 +305,7 @@ bool protopirate_history_add_to_history(
|
||||
}
|
||||
|
||||
instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base);
|
||||
instance->last_update_timestamp = furi_get_tick();
|
||||
instance->last_update_timestamp = update_timestamp;
|
||||
|
||||
const char* text_cstr = furi_string_get_cstr(instance->scratch_text);
|
||||
size_t text_len = furi_string_size(instance->scratch_text);
|
||||
@@ -278,6 +319,10 @@ bool protopirate_history_add_to_history(
|
||||
item->text_offset = (uint16_t)offset;
|
||||
item->text_len = (uint16_t)text_len;
|
||||
item->type = 0;
|
||||
item->saved_match_state = ProtoPirateSavedMatchStateNone;
|
||||
item->auto_save_state = ProtoPirateAutoSaveStateNone;
|
||||
item->matched_saved_path = NULL;
|
||||
item->matched_name = NULL;
|
||||
|
||||
instance->last_index++;
|
||||
|
||||
@@ -291,6 +336,13 @@ bool protopirate_history_add_to_history(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protopirate_history_add_to_history(
|
||||
ProtoPirateHistory* instance,
|
||||
void* context,
|
||||
SubGhzRadioPreset* preset) {
|
||||
return protopirate_history_add_to_history_at(instance, context, preset, furi_get_tick());
|
||||
}
|
||||
|
||||
void protopirate_history_delete_item(ProtoPirateHistory* instance, uint16_t idx) {
|
||||
furi_check(instance);
|
||||
|
||||
@@ -312,6 +364,7 @@ void protopirate_history_delete_item(ProtoPirateHistory* instance, uint16_t idx)
|
||||
uint16_t text_offset = item->text_offset;
|
||||
uint16_t text_len = item->text_len;
|
||||
|
||||
protopirate_history_item_clear_matched(item);
|
||||
protopirate_history_delete_capture_file(instance, seq_id);
|
||||
ProtoPirateHistoryItemArray_pop_at(NULL, instance->data, idx);
|
||||
protopirate_history_arena_remove(instance, text_offset, text_len);
|
||||
@@ -398,6 +451,135 @@ FlipperFormat* protopirate_history_get_raw_data(ProtoPirateHistory* instance, ui
|
||||
return instance->loaded_ff;
|
||||
}
|
||||
|
||||
void protopirate_history_set_matched_saved(
|
||||
ProtoPirateHistory* instance,
|
||||
uint16_t idx,
|
||||
const char* name,
|
||||
const char* path) {
|
||||
furi_check(instance);
|
||||
|
||||
if(idx >= ProtoPirateHistoryItemArray_size(instance->data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProtoPirateHistoryItem* item = ProtoPirateHistoryItemArray_get(instance->data, idx);
|
||||
|
||||
if(!item->matched_name) item->matched_name = furi_string_alloc();
|
||||
if(!item->matched_saved_path) item->matched_saved_path = furi_string_alloc();
|
||||
|
||||
furi_string_set_str(item->matched_name, name ? name : "");
|
||||
furi_string_set_str(item->matched_saved_path, path ? path : "");
|
||||
item->saved_match_state = ProtoPirateSavedMatchStateDone;
|
||||
}
|
||||
|
||||
const char* protopirate_history_get_matched_saved_path(
|
||||
ProtoPirateHistory* instance,
|
||||
uint16_t idx) {
|
||||
furi_check(instance);
|
||||
|
||||
if(idx >= ProtoPirateHistoryItemArray_size(instance->data)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ProtoPirateHistoryItem* item = ProtoPirateHistoryItemArray_get(instance->data, idx);
|
||||
if(!item->matched_saved_path || furi_string_size(item->matched_saved_path) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return furi_string_get_cstr(item->matched_saved_path);
|
||||
}
|
||||
|
||||
const char* protopirate_history_get_matched_name(ProtoPirateHistory* instance, uint16_t idx) {
|
||||
furi_check(instance);
|
||||
|
||||
if(idx >= ProtoPirateHistoryItemArray_size(instance->data)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ProtoPirateHistoryItem* item = ProtoPirateHistoryItemArray_get(instance->data, idx);
|
||||
if(!item->matched_name || furi_string_size(item->matched_name) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return furi_string_get_cstr(item->matched_name);
|
||||
}
|
||||
|
||||
bool protopirate_history_has_matched_saved(ProtoPirateHistory* instance, uint16_t idx) {
|
||||
return protopirate_history_get_matched_saved_path(instance, idx) != NULL;
|
||||
}
|
||||
|
||||
void protopirate_history_mark_auto_save_pending(ProtoPirateHistory* instance, uint16_t idx) {
|
||||
furi_check(instance);
|
||||
|
||||
if(idx >= ProtoPirateHistoryItemArray_size(instance->data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProtoPirateHistoryItem* item = ProtoPirateHistoryItemArray_get(instance->data, idx);
|
||||
item->auto_save_state = ProtoPirateAutoSaveStatePending;
|
||||
}
|
||||
|
||||
bool protopirate_history_find_pending_auto_save(ProtoPirateHistory* instance, uint16_t* idx) {
|
||||
furi_check(instance);
|
||||
furi_check(idx);
|
||||
|
||||
const size_t item_count = ProtoPirateHistoryItemArray_size(instance->data);
|
||||
for(size_t i = 0; i < item_count; i++) {
|
||||
ProtoPirateHistoryItem* item = ProtoPirateHistoryItemArray_get(instance->data, i);
|
||||
if(item->auto_save_state == ProtoPirateAutoSaveStatePending) {
|
||||
*idx = (uint16_t)i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void protopirate_history_mark_auto_save_done(ProtoPirateHistory* instance, uint16_t idx) {
|
||||
furi_check(instance);
|
||||
|
||||
if(idx >= ProtoPirateHistoryItemArray_size(instance->data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProtoPirateHistoryItem* item = ProtoPirateHistoryItemArray_get(instance->data, idx);
|
||||
item->auto_save_state = ProtoPirateAutoSaveStateDone;
|
||||
}
|
||||
|
||||
void protopirate_history_mark_saved_match_pending(ProtoPirateHistory* instance, uint16_t idx) {
|
||||
furi_check(instance);
|
||||
|
||||
if(idx >= ProtoPirateHistoryItemArray_size(instance->data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProtoPirateHistoryItem* item = ProtoPirateHistoryItemArray_get(instance->data, idx);
|
||||
item->saved_match_state = ProtoPirateSavedMatchStatePending;
|
||||
}
|
||||
|
||||
bool protopirate_history_find_pending_saved_match(ProtoPirateHistory* instance, uint16_t* idx) {
|
||||
furi_check(instance);
|
||||
furi_check(idx);
|
||||
|
||||
const size_t item_count = ProtoPirateHistoryItemArray_size(instance->data);
|
||||
for(size_t i = 0; i < item_count; i++) {
|
||||
ProtoPirateHistoryItem* item = ProtoPirateHistoryItemArray_get(instance->data, i);
|
||||
if(item->saved_match_state == ProtoPirateSavedMatchStatePending) {
|
||||
*idx = (uint16_t)i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void protopirate_history_mark_saved_match_done(ProtoPirateHistory* instance, uint16_t idx) {
|
||||
furi_check(instance);
|
||||
|
||||
if(idx >= ProtoPirateHistoryItemArray_size(instance->data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProtoPirateHistoryItem* item = ProtoPirateHistoryItemArray_get(instance->data, idx);
|
||||
item->saved_match_state = ProtoPirateSavedMatchStateDone;
|
||||
}
|
||||
|
||||
void protopirate_history_set_item_str(ProtoPirateHistory* instance, uint16_t idx, const char* str) {
|
||||
furi_check(instance);
|
||||
furi_check(str);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <lib/subghz/receiver.h>
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
|
||||
#define PROTOPIRATE_HISTORY_MAX 10
|
||||
#define PROTOPIRATE_HISTORY_MAX 20
|
||||
|
||||
typedef struct SubGhzEnvironment SubGhzEnvironment;
|
||||
typedef struct ProtoPirateHistory ProtoPirateHistory;
|
||||
@@ -34,6 +34,11 @@ bool protopirate_history_add_to_history(
|
||||
ProtoPirateHistory* instance,
|
||||
void* context,
|
||||
SubGhzRadioPreset* preset);
|
||||
bool protopirate_history_add_to_history_at(
|
||||
ProtoPirateHistory* instance,
|
||||
void* context,
|
||||
SubGhzRadioPreset* preset,
|
||||
uint32_t update_timestamp);
|
||||
void protopirate_history_delete_item(ProtoPirateHistory* instance, uint16_t idx);
|
||||
void protopirate_history_get_text_item_menu(
|
||||
ProtoPirateHistory* instance,
|
||||
@@ -49,3 +54,22 @@ FlipperFormat* protopirate_history_get_raw_data(ProtoPirateHistory* instance, ui
|
||||
void protopirate_history_release_scratch(ProtoPirateHistory* instance);
|
||||
|
||||
void protopirate_history_set_item_str(ProtoPirateHistory* instance, uint16_t idx, const char* str);
|
||||
|
||||
void protopirate_history_set_matched_saved(
|
||||
ProtoPirateHistory* instance,
|
||||
uint16_t idx,
|
||||
const char* name,
|
||||
const char* path);
|
||||
|
||||
const char* protopirate_history_get_matched_saved_path(ProtoPirateHistory* instance, uint16_t idx);
|
||||
|
||||
const char* protopirate_history_get_matched_name(ProtoPirateHistory* instance, uint16_t idx);
|
||||
|
||||
bool protopirate_history_has_matched_saved(ProtoPirateHistory* instance, uint16_t idx);
|
||||
|
||||
void protopirate_history_mark_auto_save_pending(ProtoPirateHistory* instance, uint16_t idx);
|
||||
bool protopirate_history_find_pending_auto_save(ProtoPirateHistory* instance, uint16_t* idx);
|
||||
void protopirate_history_mark_auto_save_done(ProtoPirateHistory* instance, uint16_t idx);
|
||||
void protopirate_history_mark_saved_match_pending(ProtoPirateHistory* instance, uint16_t idx);
|
||||
bool protopirate_history_find_pending_saved_match(ProtoPirateHistory* instance, uint16_t* idx);
|
||||
void protopirate_history_mark_saved_match_done(ProtoPirateHistory* instance, uint16_t idx);
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
|
||||
#include "../../protocols/protocols_common.h"
|
||||
#include "../../protocols/protocol_items.h"
|
||||
#include "../../protocols/fiat_v1.h"
|
||||
#include "../../protocols/ford_v1.h"
|
||||
#include "../../protocols/kia_v0.h"
|
||||
#include "../../protocols/kia_v3_v4.h"
|
||||
#include "../../protocols/kia_v7.h"
|
||||
#include "../../protocols/psa.h"
|
||||
#include "../../protocols/renault_v0.h"
|
||||
|
||||
#include <input/input.h>
|
||||
#include <gui/canvas.h>
|
||||
@@ -27,6 +34,7 @@
|
||||
|
||||
#define MIN_TX_TIME 666U
|
||||
#define MIN_TX_TIME_KIA_V3_V4 1600U
|
||||
#define TX_PRESET_PATCH_MAX_SIZE 128U
|
||||
|
||||
#define EMU_PRESET_KEY_PROTOCOL "Protocol"
|
||||
#define EMU_PRESET_KEY_FREQUENCY "Frequency"
|
||||
@@ -35,7 +43,10 @@
|
||||
#define EMU_PRESET_KEY_BTN "Btn"
|
||||
#define EMU_PRESET_KEY_CNT "Cnt"
|
||||
#define EMU_PRESET_KEY_TYPE "Type"
|
||||
#define EMU_PRESET_KEY_HITAG2_KEY "Hitag2 Key"
|
||||
#define EMU_PRESET_KEY_HITAG2_EPOCH "Hitag2 Epoch"
|
||||
#define EMU_CUSTOM_PRESET_KEY "Custom_preset_data"
|
||||
#define EMU_FIAT_V1_KEY_TEXT_LEN 12U
|
||||
|
||||
typedef struct {
|
||||
uint32_t original_counter;
|
||||
@@ -50,7 +61,9 @@ typedef struct {
|
||||
SubGhzTransmitter* transmitter;
|
||||
bool is_transmitting;
|
||||
bool flag_stop_called;
|
||||
bool replay_only;
|
||||
Storage* storage;
|
||||
char hitag2_key_text[EMU_FIAT_V1_KEY_TEXT_LEN + 1U];
|
||||
} EmulateContext;
|
||||
|
||||
typedef struct {
|
||||
@@ -62,6 +75,52 @@ typedef struct {
|
||||
static EmulateContext* emulate_context = NULL;
|
||||
static const ProtoPirateEmulateHostApi* g_host_api = NULL;
|
||||
|
||||
static bool emulate_hex_nibble(char c, uint8_t* nibble) {
|
||||
if(c >= '0' && c <= '9') {
|
||||
*nibble = (uint8_t)(c - '0');
|
||||
return true;
|
||||
}
|
||||
if(c >= 'A' && c <= 'F') {
|
||||
*nibble = (uint8_t)(c - 'A' + 10);
|
||||
return true;
|
||||
}
|
||||
if(c >= 'a' && c <= 'f') {
|
||||
*nibble = (uint8_t)(c - 'a' + 10);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool emulate_parse_hitag2_key_text(const char* text, uint8_t key[6]) {
|
||||
if(!text || !key) return false;
|
||||
uint8_t hex_count = 0U;
|
||||
uint8_t high_nibble = 0U;
|
||||
|
||||
for(size_t i = 0U; text[i] != '\0'; i++) {
|
||||
if(text[i] == ' ') continue;
|
||||
|
||||
uint8_t nibble = 0U;
|
||||
if(!emulate_hex_nibble(text[i], &nibble) || hex_count >= EMU_FIAT_V1_KEY_TEXT_LEN) {
|
||||
return false;
|
||||
}
|
||||
if((hex_count & 1U) == 0U) {
|
||||
high_nibble = nibble;
|
||||
} else {
|
||||
key[hex_count >> 1U] = (uint8_t)((high_nibble << 4U) | nibble);
|
||||
}
|
||||
hex_count++;
|
||||
}
|
||||
|
||||
return hex_count == EMU_FIAT_V1_KEY_TEXT_LEN;
|
||||
}
|
||||
|
||||
static bool emulate_has_hitag2_key(FlipperFormat* flipper_format) {
|
||||
if(!flipper_format) return false;
|
||||
uint8_t key[6] = {0};
|
||||
flipper_format_rewind(flipper_format);
|
||||
return flipper_format_read_hex(flipper_format, EMU_PRESET_KEY_HITAG2_KEY, key, sizeof(key));
|
||||
}
|
||||
|
||||
static void emulate_request_nav_pop(ProtoPirateApp* app) {
|
||||
app->emulate_nav_pending = EMULATE_NAV_POP;
|
||||
}
|
||||
@@ -74,6 +133,68 @@ static void emulate_request_nav_after_exit(ProtoPirateApp* app) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool emulate_prompt_fiat_v1_key(ProtoPirateApp* app, EmulateContext* ctx);
|
||||
|
||||
static void emulate_fiat_v1_key_input_callback(void* context) {
|
||||
ProtoPirateApp* app = context;
|
||||
EmulateContext* ctx = emulate_context;
|
||||
uint8_t key[6] = {0};
|
||||
|
||||
if(!app || !ctx || !ctx->flipper_format ||
|
||||
!emulate_parse_hitag2_key_text(ctx->hitag2_key_text, key)) {
|
||||
if(app && app->notifications) {
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
}
|
||||
if(app && ctx) {
|
||||
(void)emulate_prompt_fiat_v1_key(app, ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
flipper_format_rewind(ctx->flipper_format);
|
||||
if(!flipper_format_insert_or_update_hex(
|
||||
ctx->flipper_format, EMU_PRESET_KEY_HITAG2_KEY, key, sizeof(key))) {
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
emulate_request_nav_pop(app);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t epoch = 0U;
|
||||
flipper_format_rewind(ctx->flipper_format);
|
||||
if(!flipper_format_read_uint32(ctx->flipper_format, EMU_PRESET_KEY_HITAG2_EPOCH, &epoch, 1U)) {
|
||||
flipper_format_rewind(ctx->flipper_format);
|
||||
flipper_format_insert_or_update_uint32(
|
||||
ctx->flipper_format, EMU_PRESET_KEY_HITAG2_EPOCH, &epoch, 1U);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ProtoPirateViewAbout);
|
||||
if(app->view_about) {
|
||||
view_commit_model(app->view_about, true);
|
||||
}
|
||||
}
|
||||
|
||||
static bool emulate_prompt_fiat_v1_key(ProtoPirateApp* app, EmulateContext* ctx) {
|
||||
furi_check(app);
|
||||
furi_check(ctx);
|
||||
|
||||
if(!g_host_api || !g_host_api->ensure_text_input || !g_host_api->ensure_text_input(app)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(ctx->hitag2_key_text, 0, sizeof(ctx->hitag2_key_text));
|
||||
text_input_reset(app->text_input);
|
||||
text_input_set_header_text(app->text_input, "HITAG2 key (12 hex):");
|
||||
text_input_set_result_callback(
|
||||
app->text_input,
|
||||
emulate_fiat_v1_key_input_callback,
|
||||
app,
|
||||
ctx->hitag2_key_text,
|
||||
sizeof(ctx->hitag2_key_text),
|
||||
true);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ProtoPirateViewTextInput);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool emu_preset_name_is_custom_marker(const char* preset_name) {
|
||||
return preset_name && (!strcmp(preset_name, "Custom") || !strcmp(preset_name, "CUSTOM") ||
|
||||
!strcmp(preset_name, "FuriHalSubGhzPresetCustom") ||
|
||||
@@ -148,9 +269,8 @@ static bool emulate_radio_ready(ProtoPirateApp* app) {
|
||||
static uint32_t emulate_min_tx_time(const EmulateContext* ctx) {
|
||||
if(!ctx || !ctx->protocol_name) return MIN_TX_TIME;
|
||||
const char* proto = furi_string_get_cstr(ctx->protocol_name);
|
||||
if(strcmp(proto, "Kia V3/V4") == 0 || strcmp(proto, "Kia V3") == 0 ||
|
||||
strcmp(proto, "Kia V4") == 0 || strcmp(proto, "KIA/HYU V3") == 0 ||
|
||||
strcmp(proto, "KIA/HYU V4") == 0) {
|
||||
proto = protopirate_protocol_catalog_canonical_name(proto);
|
||||
if(proto && strcmp(proto, KIA_PROTOCOL_V3_V4_NAME) == 0) {
|
||||
return MIN_TX_TIME_KIA_V3_V4;
|
||||
}
|
||||
return MIN_TX_TIME;
|
||||
@@ -335,11 +455,13 @@ static bool emulate_context_try_init_transmitter(ProtoPirateApp* app, EmulateCon
|
||||
if(!ctx->flipper_format || !ctx->protocol_name) return false;
|
||||
|
||||
const char* proto_name = furi_string_get_cstr(ctx->protocol_name);
|
||||
const char* registry_name = proto_name;
|
||||
if(strcmp(proto_name, "Kia V3") == 0 || strcmp(proto_name, "Kia V4") == 0 ||
|
||||
strcmp(proto_name, "KIA/HYU V3") == 0 || strcmp(proto_name, "KIA/HYU V4") == 0) {
|
||||
registry_name = "Kia V3/V4";
|
||||
FURI_LOG_I(TAG, "Protocol name %s mapped to Kia V3/V4 for registry", proto_name);
|
||||
const char* registry_name = protopirate_protocol_catalog_canonical_name(proto_name);
|
||||
if(!registry_name || !protopirate_protocol_catalog_can_tx(proto_name)) {
|
||||
FURI_LOG_E(TAG, "Protocol %s has no TX catalog entry", proto_name ? proto_name : "?");
|
||||
return false;
|
||||
}
|
||||
if(strcmp(proto_name, registry_name) != 0) {
|
||||
FURI_LOG_I(TAG, "Protocol name %s mapped to %s for registry", proto_name, registry_name);
|
||||
}
|
||||
|
||||
EmulateResolvedPreset resolved_preset;
|
||||
@@ -348,9 +470,14 @@ static bool emulate_context_try_init_transmitter(ProtoPirateApp* app, EmulateCon
|
||||
return false;
|
||||
}
|
||||
|
||||
bool registry_ready = g_host_api && g_host_api->apply_protocol_registry_for_preset_data &&
|
||||
g_host_api->apply_protocol_registry_for_preset_data(
|
||||
app, resolved_preset.data, resolved_preset.size);
|
||||
bool registry_ready = g_host_api && g_host_api->apply_protocol_registry_for_context &&
|
||||
g_host_api->apply_protocol_registry_for_context(
|
||||
app,
|
||||
ctx->preset,
|
||||
ctx->freq,
|
||||
resolved_preset.data,
|
||||
resolved_preset.size,
|
||||
registry_name);
|
||||
emulate_resolved_preset_release(&resolved_preset);
|
||||
if(!registry_ready) {
|
||||
FURI_LOG_E(TAG, "Failed to apply protocol registry for emulate preset");
|
||||
@@ -402,6 +529,12 @@ static uint8_t emu_button_for_protocol(
|
||||
InputKey key,
|
||||
uint8_t original,
|
||||
FlipperFormat* ff) {
|
||||
if(!protocol) {
|
||||
return original;
|
||||
}
|
||||
|
||||
protocol = protopirate_protocol_catalog_canonical_name(protocol);
|
||||
|
||||
if(strcmp(protocol, KIA_PROTOCOL_V7_NAME) == 0) {
|
||||
switch(key) {
|
||||
case InputKeyUp:
|
||||
@@ -416,9 +549,7 @@ static uint8_t emu_button_for_protocol(
|
||||
return original;
|
||||
}
|
||||
}
|
||||
if(strcmp(protocol, "Kia V3/V4") == 0 || strcmp(protocol, "Kia V3") == 0 ||
|
||||
strcmp(protocol, "Kia V4") == 0 || strcmp(protocol, "KIA/HYU V3") == 0 ||
|
||||
strcmp(protocol, "KIA/HYU V4") == 0) {
|
||||
if(strcmp(protocol, KIA_PROTOCOL_V3_V4_NAME) == 0) {
|
||||
switch(key) {
|
||||
case InputKeyUp:
|
||||
return 0x01;
|
||||
@@ -511,6 +642,23 @@ static uint8_t emu_button_for_protocol(
|
||||
default:
|
||||
return original;
|
||||
}
|
||||
} else if(strstr(protocol, RENAULT_PROTOCOL_V0_NAME)) {
|
||||
uint32_t renault_type = 0U;
|
||||
if(ff) {
|
||||
flipper_format_rewind(ff);
|
||||
flipper_format_read_uint32(ff, EMU_PRESET_KEY_TYPE, &renault_type, 1);
|
||||
}
|
||||
|
||||
const bool type_13 = renault_type == 0x13U;
|
||||
const bool type_high = (renault_type == 0x1AU) || (renault_type == 0x3BU);
|
||||
switch(key) {
|
||||
case InputKeyUp:
|
||||
return type_13 ? 0x06 : (type_high ? 0x44 : 0x04);
|
||||
case InputKeyOk:
|
||||
return type_13 ? 0x0A : (type_high ? 0x48 : 0x08);
|
||||
default:
|
||||
return original;
|
||||
}
|
||||
} else if(strstr(protocol, "Honda V1")) {
|
||||
switch(key) {
|
||||
case InputKeyUp:
|
||||
@@ -552,7 +700,7 @@ static uint8_t emu_button_for_protocol(
|
||||
default:
|
||||
return original;
|
||||
}
|
||||
} else if(strstr(protocol, "Land Rover")) {
|
||||
} else if(strstr(protocol, "Honda V2")) {
|
||||
switch(key) {
|
||||
case InputKeyUp:
|
||||
return 0x02; // Lock
|
||||
@@ -627,14 +775,16 @@ static uint8_t emu_button_for_protocol(
|
||||
default:
|
||||
return original;
|
||||
}
|
||||
} else if(strstr(protocol, "Fiat V1")) {
|
||||
} else if(strstr(protocol, FIAT_V1_PROTOCOL_NAME)) {
|
||||
switch(key) {
|
||||
case InputKeyUp:
|
||||
return 0x8; // Lock
|
||||
return 0x4; // Lock
|
||||
case InputKeyOk:
|
||||
return 0x0; // Unlock
|
||||
return 0x8; // Unlock
|
||||
case InputKeyDown:
|
||||
return 0xD; // Trunk
|
||||
return 0x2; // Trunk
|
||||
case InputKeyLeft:
|
||||
return 0x1; // Close
|
||||
default:
|
||||
return original;
|
||||
}
|
||||
@@ -650,6 +800,19 @@ static uint8_t emu_button_for_protocol(
|
||||
return original;
|
||||
}
|
||||
|
||||
static bool emulate_renault_is_rolling(FlipperFormat* ff) {
|
||||
if(!ff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t rolling = 0U;
|
||||
flipper_format_rewind(ff);
|
||||
if(flipper_format_read_uint32(ff, "Rolling", &rolling, 1)) {
|
||||
return rolling != 0U;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool emulate_update_data(EmulateContext* ctx, uint8_t button) {
|
||||
if(!ctx || !ctx->flipper_format) return false;
|
||||
|
||||
@@ -719,7 +882,7 @@ static void emulate_draw_callback(Canvas* canvas, void* model) {
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
char* unlock_text = "UNLOCK";
|
||||
const char* unlock_text = ctx->replay_only ? "REPLAY" : "UNLOCK";
|
||||
uint16_t width_button = canvas_string_width(canvas, unlock_text) + 8;
|
||||
uint16_t height_button = canvas_current_font_height(canvas);
|
||||
canvas_draw_rbox(
|
||||
@@ -780,14 +943,20 @@ static bool emulate_input_callback(InputEvent* event, void* context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t button = emu_button_for_protocol(
|
||||
furi_string_get_cstr(ctx->protocol_name),
|
||||
event->key,
|
||||
ctx->original_button,
|
||||
ctx->flipper_format);
|
||||
if(!ctx->replay_only) {
|
||||
uint8_t button = emu_button_for_protocol(
|
||||
furi_string_get_cstr(ctx->protocol_name),
|
||||
event->key,
|
||||
ctx->original_button,
|
||||
ctx->flipper_format);
|
||||
|
||||
ctx->current_counter++;
|
||||
emulate_update_data(ctx, button);
|
||||
if(furi_string_equal(ctx->protocol_name, RENAULT_PROTOCOL_V0_NAME)) {
|
||||
ctx->current_counter = (ctx->current_counter + 1U) & 0xFFU;
|
||||
} else {
|
||||
ctx->current_counter++;
|
||||
}
|
||||
emulate_update_data(ctx, button);
|
||||
}
|
||||
|
||||
ctx->is_transmitting = true;
|
||||
view_dispatcher_send_custom_event(
|
||||
@@ -806,13 +975,23 @@ static bool emulate_input_callback(InputEvent* event, void* context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint8_t get_tx_preset_byte(uint8_t* preset_data) {
|
||||
#define MAX_PRESET_SIZE 128
|
||||
uint8_t offset = 0;
|
||||
while(preset_data[offset] && (offset < MAX_PRESET_SIZE)) {
|
||||
static bool get_tx_preset_byte(const uint8_t* preset_data, size_t preset_size, uint8_t* preset_offset) {
|
||||
if(!preset_data || !preset_offset) return false;
|
||||
|
||||
size_t offset = 0;
|
||||
size_t scan_limit = preset_size < TX_PRESET_PATCH_MAX_SIZE ? preset_size :
|
||||
TX_PRESET_PATCH_MAX_SIZE;
|
||||
while((offset < scan_limit) && preset_data[offset]) {
|
||||
offset += 2;
|
||||
}
|
||||
return (!preset_data[offset] ? offset + 2 : 0);
|
||||
|
||||
if(offset >= scan_limit) return false;
|
||||
|
||||
size_t tx_offset = offset + 2U;
|
||||
if((tx_offset + 1U) >= scan_limit || (tx_offset + 1U) >= preset_size) return false;
|
||||
|
||||
*preset_offset = (uint8_t)tx_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void plugin_on_enter(void* context) {
|
||||
@@ -939,15 +1118,36 @@ static void plugin_on_enter(void* context) {
|
||||
furi_string_set(ctx->protocol_name, "Unknown");
|
||||
}
|
||||
|
||||
// Standalone Suzuki captures: merged into Kia V0 Type 2
|
||||
if(furi_string_equal(ctx->protocol_name, "Suzuki")) {
|
||||
uint32_t type_suzuki = 2;
|
||||
ctx->replay_only = furi_string_equal(ctx->protocol_name, RENAULT_PROTOCOL_V0_NAME) &&
|
||||
!emulate_renault_is_rolling(ctx->flipper_format);
|
||||
|
||||
// Standalone Suzuki/Honda V0 captures: merged into Kia V0
|
||||
if(furi_string_equal(ctx->protocol_name, "Suzuki") ||
|
||||
furi_string_equal(ctx->protocol_name, "Suzuki V0") ||
|
||||
furi_string_equal(ctx->protocol_name, "Honda V0")) {
|
||||
uint32_t kia_v0_type = furi_string_equal(ctx->protocol_name, "Honda V0") ? 3U : 2U;
|
||||
furi_string_set(ctx->protocol_name, KIA_PROTOCOL_V0_NAME);
|
||||
flipper_format_rewind(ctx->flipper_format);
|
||||
flipper_format_insert_or_update_string_cstr(
|
||||
ctx->flipper_format, EMU_PRESET_KEY_PROTOCOL, KIA_PROTOCOL_V0_NAME);
|
||||
flipper_format_insert_or_update_uint32(
|
||||
ctx->flipper_format, EMU_PRESET_KEY_TYPE, &type_suzuki, 1);
|
||||
ctx->flipper_format, EMU_PRESET_KEY_TYPE, &kia_v0_type, 1);
|
||||
}
|
||||
|
||||
if(furi_string_equal(ctx->protocol_name, "Land Rover V0")) {
|
||||
furi_string_set(ctx->protocol_name, "Honda V2");
|
||||
flipper_format_rewind(ctx->flipper_format);
|
||||
flipper_format_insert_or_update_string_cstr(
|
||||
ctx->flipper_format, EMU_PRESET_KEY_PROTOCOL, "Honda V2");
|
||||
}
|
||||
|
||||
const char* canonical_protocol =
|
||||
protopirate_protocol_catalog_canonical_name(furi_string_get_cstr(ctx->protocol_name));
|
||||
if(canonical_protocol && strcmp(furi_string_get_cstr(ctx->protocol_name), canonical_protocol) != 0) {
|
||||
furi_string_set(ctx->protocol_name, canonical_protocol);
|
||||
flipper_format_rewind(ctx->flipper_format);
|
||||
flipper_format_insert_or_update_string_cstr(
|
||||
ctx->flipper_format, EMU_PRESET_KEY_PROTOCOL, canonical_protocol);
|
||||
}
|
||||
|
||||
flipper_format_rewind(ctx->flipper_format);
|
||||
@@ -973,6 +1173,17 @@ static void plugin_on_enter(void* context) {
|
||||
view_set_context(app->view_about, app);
|
||||
view_set_previous_callback(app->view_about, NULL);
|
||||
|
||||
if(furi_string_equal(ctx->protocol_name, FIAT_V1_PROTOCOL_NAME) &&
|
||||
!emulate_has_hitag2_key(ctx->flipper_format)) {
|
||||
if(!emulate_prompt_fiat_v1_key(app, ctx)) {
|
||||
FURI_LOG_E(TAG, "Failed to show Fiat V1 HITAG2 key input");
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
emulate_context_free();
|
||||
emulate_request_nav_pop(app);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ProtoPirateViewAbout);
|
||||
}
|
||||
|
||||
@@ -1020,6 +1231,7 @@ static bool plugin_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
uint8_t* preset_data = resolved_preset.data;
|
||||
uint8_t preset_patch_buffer[TX_PRESET_PATCH_MAX_SIZE];
|
||||
|
||||
if(preset_data) {
|
||||
if(!emulate_radio_ready(app)) {
|
||||
@@ -1035,21 +1247,35 @@ static bool plugin_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
if(app->tx_power) {
|
||||
uint8_t preset_offset = get_tx_preset_byte(preset_data);
|
||||
uint8_t fm_byte = preset_data[preset_offset];
|
||||
uint8_t am_byte = preset_data[preset_offset + 1];
|
||||
bool patchable = true;
|
||||
if(!resolved_preset.should_free) {
|
||||
patchable = resolved_preset.size <= sizeof(preset_patch_buffer);
|
||||
if(patchable) {
|
||||
memcpy(preset_patch_buffer, preset_data, resolved_preset.size);
|
||||
preset_data = preset_patch_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
if(fm_byte && am_byte) {
|
||||
uint8_t preset_offset = 0;
|
||||
if(!patchable ||
|
||||
!get_tx_preset_byte(preset_data, resolved_preset.size, &preset_offset)) {
|
||||
FURI_LOG_I(TAG, INVALID_PRESET);
|
||||
} else if(fm_byte) {
|
||||
FURI_LOG_I(TAG, "FM PA table found.");
|
||||
preset_data[preset_offset] = tx_power_value[app->tx_power];
|
||||
} else if(am_byte) {
|
||||
FURI_LOG_I(TAG, "AM PA table found.");
|
||||
preset_data[preset_offset + 1] =
|
||||
tx_power_value[TX_PRESET_VALUES_AM + app->tx_power];
|
||||
} else {
|
||||
FURI_LOG_I(TAG, INVALID_PRESET);
|
||||
uint8_t fm_byte = preset_data[preset_offset];
|
||||
uint8_t am_byte = preset_data[preset_offset + 1];
|
||||
|
||||
if(fm_byte && am_byte) {
|
||||
FURI_LOG_I(TAG, INVALID_PRESET);
|
||||
} else if(fm_byte) {
|
||||
FURI_LOG_I(TAG, "FM PA table found.");
|
||||
preset_data[preset_offset] = tx_power_value[app->tx_power];
|
||||
} else if(am_byte) {
|
||||
FURI_LOG_I(TAG, "AM PA table found.");
|
||||
preset_data[preset_offset + 1] =
|
||||
tx_power_value[TX_PRESET_VALUES_AM + app->tx_power];
|
||||
} else {
|
||||
FURI_LOG_I(TAG, INVALID_PRESET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1144,6 +1370,11 @@ static bool plugin_on_event(void* context, SceneManagerEvent event) {
|
||||
static void plugin_on_exit(void* context) {
|
||||
ProtoPirateApp* app = context;
|
||||
|
||||
if(!app) {
|
||||
emulate_context_free();
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop any active transmission before tearing down callbacks.
|
||||
if(app->txrx && app->txrx->txrx_state == ProtoPirateTxRxStateTx) {
|
||||
FURI_LOG_I(TAG, "Stopping transmission on exit");
|
||||
@@ -1169,11 +1400,19 @@ static void plugin_on_exit(void* context) {
|
||||
if(g_host_api && g_host_api->storage_delete_temp) g_host_api->storage_delete_temp();
|
||||
|
||||
if(app->radio_initialized && app->txrx && app->txrx->environment && app->txrx->preset &&
|
||||
app->txrx->preset->data && g_host_api &&
|
||||
g_host_api->apply_protocol_registry_for_preset_data) {
|
||||
if(!g_host_api->apply_protocol_registry_for_preset_data(
|
||||
app, app->txrx->preset->data, app->txrx->preset->data_size)) {
|
||||
FURI_LOG_W(TAG, "Failed to restore session protocol registry on emulate exit");
|
||||
app->txrx->preset->data && app->txrx->preset->name &&
|
||||
g_host_api && g_host_api->apply_protocol_registry_for_context) {
|
||||
const char* preset_name = furi_string_get_cstr(app->txrx->preset->name);
|
||||
if(preset_name) {
|
||||
if(!g_host_api->apply_protocol_registry_for_context(
|
||||
app,
|
||||
preset_name,
|
||||
app->txrx->preset->frequency,
|
||||
app->txrx->preset->data,
|
||||
app->txrx->preset->data_size,
|
||||
NULL)) {
|
||||
FURI_LOG_W(TAG, "Failed to restore session protocol registry on emulate exit");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1183,7 +1422,9 @@ static void plugin_on_exit(void* context) {
|
||||
view_set_context(app->view_about, NULL);
|
||||
}
|
||||
|
||||
notification_message_block(app->notifications, &sequence_blink_stop);
|
||||
if(app->notifications) {
|
||||
notification_message_block(app->notifications, &sequence_blink_stop);
|
||||
}
|
||||
}
|
||||
|
||||
static void plugin_context_release(void* context) {
|
||||
|
||||
@@ -7,16 +7,20 @@
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
#define PROTOPIRATE_EMULATE_PLUGIN_APP_ID "protopirate_emulate_plugin"
|
||||
#define PROTOPIRATE_EMULATE_PLUGIN_API_VERSION 1U
|
||||
#define PROTOPIRATE_EMULATE_PLUGIN_API_VERSION 2U
|
||||
|
||||
typedef struct {
|
||||
bool (*radio_init)(void* app);
|
||||
bool (*apply_protocol_registry_for_preset_data)(
|
||||
bool (*apply_protocol_registry_for_context)(
|
||||
void* app,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
const uint8_t* preset_data,
|
||||
size_t preset_data_size);
|
||||
size_t preset_data_size,
|
||||
const char* protocol_name);
|
||||
void (*rx_stack_suspend_for_tx)(void* app);
|
||||
bool (*ensure_view_about)(void* app);
|
||||
bool (*ensure_text_input)(void* app);
|
||||
void (*idle)(void* app);
|
||||
void (*history_release_scratch)(void* app);
|
||||
void (*storage_delete_temp)(void);
|
||||
|
||||
@@ -27,12 +27,6 @@ static ProtoPiratePsaBfContext g_active_ctx = ProtoPiratePsaBfContextReceiverInf
|
||||
static void show_bf_result(void* app, uint8_t status, ButtonCallback callback);
|
||||
static void bf_finish_and_show_result(void* app, ButtonCallback result_callback);
|
||||
|
||||
static void psa_bf_done_cb(void* context) {
|
||||
if(g_host_api && g_host_api->send_custom_event) {
|
||||
g_host_api->send_custom_event(context, ProtoPirateCustomEventPsaBruteforceComplete);
|
||||
}
|
||||
}
|
||||
|
||||
static bool item_needs_bruteforce_from_ff(FlipperFormat* ff, bool require_psa_protocol) {
|
||||
if(!ff) return false;
|
||||
FuriString* s = furi_string_alloc();
|
||||
@@ -237,8 +231,8 @@ static bool start_bruteforce(void* app) {
|
||||
g_host_api->notification_error(app);
|
||||
return false;
|
||||
}
|
||||
state->on_done = psa_bf_done_cb;
|
||||
state->on_done_ctx = app;
|
||||
state->on_done = NULL;
|
||||
state->on_done_ctx = NULL;
|
||||
g_bf_state = state;
|
||||
g_bf_thread = furi_thread_alloc_ex("PsaBf", 2048, psa_brute_force_thread_entry, state);
|
||||
if(!g_bf_thread) {
|
||||
@@ -278,7 +272,11 @@ static bool
|
||||
if(bfst == PSA_BF_STATUS_IDLE || bfst == PSA_BF_STATUS_RUNNING) {
|
||||
show_bf_progress(app);
|
||||
} else {
|
||||
bf_finish_and_show_result(app, NULL);
|
||||
if(ctx == ProtoPiratePsaBfContextSubDecode) {
|
||||
g_host_api->send_custom_event(app, ProtoPirateCustomEventPsaBruteforceComplete);
|
||||
} else {
|
||||
bf_finish_and_show_result(app, NULL);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -335,6 +333,7 @@ static bool
|
||||
static void plugin_on_scene_exit(void* app, ProtoPiratePsaBfContext ctx) {
|
||||
UNUSED(app);
|
||||
UNUSED(ctx);
|
||||
bf_cancel_thread();
|
||||
}
|
||||
|
||||
static bool plugin_widget_left_should_bruteforce(void* app, ProtoPiratePsaBfContext ctx) {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
#include <lib/flipper_application/flipper_application.h>
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
|
||||
#include "../../defines.h"
|
||||
#include "../../protopirate_history.h"
|
||||
#include "../../views/protopirate_receiver.h"
|
||||
|
||||
#define PROTOPIRATE_TOOL_SCENE_PLUGIN_APP_ID "protopirate_tool_scene_plugins"
|
||||
#define PROTOPIRATE_TOOL_SCENE_PLUGIN_API_VERSION 1U
|
||||
|
||||
typedef enum {
|
||||
ProtoPirateToolScenePluginKindSubDecode = 0,
|
||||
#ifdef ENABLE_TIMING_TUNER_SCENE
|
||||
ProtoPirateToolScenePluginKindTimingTuner,
|
||||
#endif
|
||||
} ProtoPirateToolScenePluginKind;
|
||||
|
||||
typedef struct {
|
||||
bool (*ensure_receiver_view)(void* app);
|
||||
bool (*ensure_widget)(void* app);
|
||||
bool (*ensure_view_about)(void* app);
|
||||
bool (*radio_init)(void* app);
|
||||
void (*rx_stack_resume_after_tx)(void* app);
|
||||
void (*preset_init)(
|
||||
void* app,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint8_t* preset_data,
|
||||
size_t preset_data_size);
|
||||
bool (*refresh_protocol_registry)(void* app, bool ensure_receiver_ready);
|
||||
bool (*apply_protocol_registry_for_context)(
|
||||
void* app,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
const uint8_t* preset_data,
|
||||
size_t preset_data_size,
|
||||
const char* protocol_name);
|
||||
void (*begin)(void* app, uint8_t* preset_data);
|
||||
uint32_t (*rx)(void* app, uint32_t frequency);
|
||||
void (*rx_end)(void* app);
|
||||
void (*get_frequency_modulation_str)(
|
||||
void* app,
|
||||
char* frequency,
|
||||
size_t frequency_size,
|
||||
char* modulation,
|
||||
size_t modulation_size);
|
||||
|
||||
void (*history_release_scratch)(ProtoPirateHistory* history);
|
||||
bool (*radio_device_is_external)(const SubGhzDevice* radio_device);
|
||||
|
||||
void (*receiver_add_data_statusbar)(
|
||||
ProtoPirateReceiver* receiver,
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
const char* history_stat_str,
|
||||
bool external_radio);
|
||||
uint16_t (*receiver_get_idx_menu)(ProtoPirateReceiver* receiver);
|
||||
void (*receiver_set_idx_menu)(ProtoPirateReceiver* receiver, uint16_t idx);
|
||||
void (*receiver_set_callback)(
|
||||
ProtoPirateReceiver* receiver,
|
||||
ProtoPirateReceiverCallback callback,
|
||||
void* context);
|
||||
void (*receiver_set_sub_decode_mode)(ProtoPirateReceiver* receiver, bool sub_decode_mode);
|
||||
void (*receiver_set_sub_decode_progress)(ProtoPirateReceiver* receiver, uint8_t progress);
|
||||
void (*receiver_reset_menu)(ProtoPirateReceiver* receiver);
|
||||
void (*receiver_sync_menu_from_history)(
|
||||
ProtoPirateReceiver* receiver,
|
||||
ProtoPirateHistory* history);
|
||||
|
||||
bool (*psa_bf_plugin_ensure_loaded)(void* app);
|
||||
void (*psa_bf_context_release)(void* app);
|
||||
} ProtoPirateToolSceneHostApi;
|
||||
|
||||
typedef struct {
|
||||
const char* plugin_name;
|
||||
ProtoPirateToolScenePluginKind kind;
|
||||
void (*set_host_api)(const ProtoPirateToolSceneHostApi* host_api);
|
||||
void (*on_enter)(void* app);
|
||||
bool (*on_event)(void* app, SceneManagerEvent event);
|
||||
void (*on_exit)(void* app);
|
||||
void (*release)(void* app);
|
||||
} ProtoPirateToolScenePlugin;
|
||||
@@ -272,4 +272,5 @@ void protopirate_scene_about_on_exit(void* context) {
|
||||
|
||||
view_set_draw_callback(app->view_about, NULL);
|
||||
view_set_input_callback(app->view_about, NULL);
|
||||
view_set_context(app->view_about, NULL);
|
||||
}
|
||||
|
||||
@@ -19,12 +19,15 @@ static bool host_radio_init(void* app) {
|
||||
return protopirate_radio_init((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static bool host_apply_protocol_registry_for_preset_data(
|
||||
static bool host_apply_protocol_registry_for_context(
|
||||
void* app,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
const uint8_t* preset_data,
|
||||
size_t preset_data_size) {
|
||||
return protopirate_apply_protocol_registry_for_preset_data(
|
||||
(ProtoPirateApp*)app, preset_data, preset_data_size);
|
||||
size_t preset_data_size,
|
||||
const char* protocol_name) {
|
||||
return protopirate_apply_protocol_registry_for_context(
|
||||
(ProtoPirateApp*)app, preset_name, frequency, preset_data, preset_data_size, protocol_name);
|
||||
}
|
||||
|
||||
static void host_rx_stack_suspend_for_tx(void* app) {
|
||||
@@ -35,6 +38,10 @@ static bool host_ensure_view_about(void* app) {
|
||||
return protopirate_ensure_view_about((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static bool host_ensure_text_input(void* app) {
|
||||
return protopirate_ensure_text_input((ProtoPirateApp*)app);
|
||||
}
|
||||
|
||||
static void host_idle(void* app) {
|
||||
protopirate_idle((ProtoPirateApp*)app);
|
||||
}
|
||||
@@ -70,9 +77,10 @@ static void protopirate_emulate_apply_pending_nav(ProtoPirateApp* app) {
|
||||
|
||||
static const ProtoPirateEmulateHostApi protopirate_emulate_host_api = {
|
||||
.radio_init = host_radio_init,
|
||||
.apply_protocol_registry_for_preset_data = host_apply_protocol_registry_for_preset_data,
|
||||
.apply_protocol_registry_for_context = host_apply_protocol_registry_for_context,
|
||||
.rx_stack_suspend_for_tx = host_rx_stack_suspend_for_tx,
|
||||
.ensure_view_about = host_ensure_view_about,
|
||||
.ensure_text_input = host_ensure_text_input,
|
||||
.idle = host_idle,
|
||||
.history_release_scratch = host_history_release_scratch,
|
||||
.storage_delete_temp = host_storage_delete_temp,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// scenes/protopirate_scene_receiver.c
|
||||
#include "../protopirate_app_i.h"
|
||||
#include "../helpers/protopirate_storage.h"
|
||||
#include "../helpers/protopirate_saved_match.h"
|
||||
#include "views/protopirate_receiver.h"
|
||||
#include <notification/notification_messages.h>
|
||||
#include <stdio.h>
|
||||
@@ -72,43 +73,12 @@ static void protopirate_scene_receiver_callback(
|
||||
uint16_t last_index = protopirate_history_get_item(app->txrx->history) - 1;
|
||||
protopirate_view_receiver_set_idx_menu(app->protopirate_receiver, last_index);
|
||||
|
||||
uint16_t new_idx = protopirate_history_get_item(app->txrx->history) - 1;
|
||||
if(app->auto_save) {
|
||||
FlipperFormat* ff = protopirate_history_get_raw_data(
|
||||
app->txrx->history, protopirate_history_get_item(app->txrx->history) - 1);
|
||||
|
||||
if(ff) {
|
||||
FuriString* protocol = furi_string_alloc();
|
||||
if(!protocol) {
|
||||
FURI_LOG_E(TAG, "protocol allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
flipper_format_rewind(ff);
|
||||
if(!flipper_format_read_string(ff, FF_PROTOCOL, protocol)) {
|
||||
furi_string_set_str(protocol, "Unknown");
|
||||
}
|
||||
|
||||
furi_string_replace_all(protocol, "/", "_");
|
||||
furi_string_replace_all(protocol, " ", "_");
|
||||
|
||||
FuriString* saved_path = furi_string_alloc();
|
||||
if(!saved_path) {
|
||||
FURI_LOG_E(TAG, "saved_path allocation failed");
|
||||
furi_string_free(protocol);
|
||||
return;
|
||||
}
|
||||
|
||||
if(protopirate_storage_save_capture(
|
||||
ff, furi_string_get_cstr(protocol), saved_path)) {
|
||||
FURI_LOG_I(TAG, "Auto-saved: %s", furi_string_get_cstr(saved_path));
|
||||
notification_message(app->notifications, &sequence_double_vibro);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Auto-save failed");
|
||||
}
|
||||
|
||||
furi_string_free(protocol);
|
||||
furi_string_free(saved_path);
|
||||
}
|
||||
protopirate_history_mark_auto_save_pending(app->txrx->history, new_idx);
|
||||
}
|
||||
if(app->check_saved) {
|
||||
protopirate_history_mark_saved_match_pending(app->txrx->history, new_idx);
|
||||
}
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
@@ -123,17 +93,108 @@ static void protopirate_scene_receiver_callback(
|
||||
}
|
||||
}
|
||||
|
||||
static void protopirate_scene_receiver_start_rx_stack(ProtoPirateApp* app) {
|
||||
static bool protopirate_scene_receiver_process_auto_save(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(!app->radio_initialized) {
|
||||
|
||||
if(!app->txrx || !app->txrx->history) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t idx = 0;
|
||||
if(!protopirate_history_find_pending_auto_save(app->txrx->history, &idx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FlipperFormat* ff = protopirate_history_get_raw_data(app->txrx->history, idx);
|
||||
if(ff) {
|
||||
FuriString* protocol = furi_string_alloc();
|
||||
FuriString* saved_path = furi_string_alloc();
|
||||
|
||||
if(protocol && saved_path) {
|
||||
flipper_format_rewind(ff);
|
||||
if(!flipper_format_read_string(ff, FF_PROTOCOL, protocol)) {
|
||||
furi_string_set_str(protocol, "Unknown");
|
||||
}
|
||||
|
||||
furi_string_replace_all(protocol, "/", "_");
|
||||
furi_string_replace_all(protocol, " ", "_");
|
||||
|
||||
if(protopirate_storage_save_capture(ff, furi_string_get_cstr(protocol), saved_path)) {
|
||||
FURI_LOG_I(TAG, "Auto-saved: %s", furi_string_get_cstr(saved_path));
|
||||
notification_message(app->notifications, &sequence_double_vibro);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Auto-save failed");
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Auto-save allocation failed");
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
}
|
||||
|
||||
if(protocol) furi_string_free(protocol);
|
||||
if(saved_path) furi_string_free(saved_path);
|
||||
protopirate_history_release_scratch(app->txrx->history);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Auto-save skipped: history capture unavailable");
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
}
|
||||
|
||||
protopirate_history_mark_auto_save_done(app->txrx->history, idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void protopirate_scene_receiver_process_saved_match(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
|
||||
if(!app->check_saved || !app->txrx || !app->txrx->history) {
|
||||
return;
|
||||
}
|
||||
|
||||
protopirate_rx_stack_resume_after_tx(app);
|
||||
uint16_t idx = 0;
|
||||
if(!protopirate_history_find_pending_saved_match(app->txrx->history, &idx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlipperFormat* ff = protopirate_history_get_raw_data(app->txrx->history, idx);
|
||||
if(ff) {
|
||||
FuriString* matched_name = furi_string_alloc();
|
||||
FuriString* matched_path = furi_string_alloc();
|
||||
if(matched_name && matched_path) {
|
||||
flipper_format_rewind(ff);
|
||||
if(protopirate_saved_match_signal(ff, matched_name, matched_path)) {
|
||||
protopirate_history_set_matched_saved(
|
||||
app->txrx->history,
|
||||
idx,
|
||||
furi_string_get_cstr(matched_name),
|
||||
furi_string_get_cstr(matched_path));
|
||||
FURI_LOG_I(TAG, "Matched saved signal: %s", furi_string_get_cstr(matched_name));
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to allocate saved-match strings");
|
||||
}
|
||||
if(matched_name) furi_string_free(matched_name);
|
||||
if(matched_path) furi_string_free(matched_path);
|
||||
protopirate_history_release_scratch(app->txrx->history);
|
||||
}
|
||||
|
||||
protopirate_history_mark_saved_match_done(app->txrx->history, idx);
|
||||
}
|
||||
|
||||
static void protopirate_scene_receiver_process_deferred_storage(ProtoPirateApp* app) {
|
||||
if(protopirate_scene_receiver_process_auto_save(app)) {
|
||||
return;
|
||||
}
|
||||
|
||||
protopirate_scene_receiver_process_saved_match(app);
|
||||
}
|
||||
|
||||
static bool protopirate_scene_receiver_bind_rx_stack(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
|
||||
if(!app->txrx->receiver) {
|
||||
FURI_LOG_E(TAG, "SubGhz receiver unavailable — staying on receiver in degraded mode");
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!app->txrx->worker) {
|
||||
@@ -141,7 +202,7 @@ static void protopirate_scene_receiver_start_rx_stack(ProtoPirateApp* app) {
|
||||
if(!app->txrx->worker) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate worker — staying on receiver in degraded mode");
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
subghz_worker_set_overrun_callback(
|
||||
app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
|
||||
@@ -150,9 +211,16 @@ static void protopirate_scene_receiver_start_rx_stack(ProtoPirateApp* app) {
|
||||
}
|
||||
|
||||
subghz_receiver_reset(app->txrx->receiver);
|
||||
|
||||
subghz_worker_set_context(app->txrx->worker, app->txrx->receiver);
|
||||
subghz_receiver_set_rx_callback(app->txrx->receiver, protopirate_scene_receiver_callback, app);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void protopirate_scene_receiver_start_rx_stack(ProtoPirateApp* app) {
|
||||
furi_check(app);
|
||||
if(!app->radio_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(app->txrx->hopper_state != ProtoPirateHopperStateOFF) {
|
||||
app->txrx->hopper_state = ProtoPirateHopperStateRunning;
|
||||
@@ -172,6 +240,12 @@ static void protopirate_scene_receiver_start_rx_stack(ProtoPirateApp* app) {
|
||||
if(app->txrx->hopper_state == ProtoPirateHopperStateRunning) {
|
||||
frequency = subghz_setting_get_hopper_frequency(app->setting, 0);
|
||||
app->txrx->hopper_idx_frequency = 0;
|
||||
app->txrx->preset->frequency = frequency;
|
||||
}
|
||||
|
||||
protopirate_rx_stack_resume_after_tx(app);
|
||||
if(!protopirate_scene_receiver_bind_rx_stack(app)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Starting RX on %lu Hz", frequency);
|
||||
@@ -205,6 +279,8 @@ void protopirate_scene_receiver_on_enter(void* context) {
|
||||
app->txrx->history = protopirate_history_alloc();
|
||||
if(!app->txrx->history) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate history!");
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -328,8 +404,12 @@ bool protopirate_scene_receiver_on_event(void* context, SceneManagerEvent event)
|
||||
break;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
protopirate_scene_receiver_process_deferred_storage(app);
|
||||
|
||||
if(app->txrx->hopper_state != ProtoPirateHopperStateOFF) {
|
||||
protopirate_hopper_update(app);
|
||||
if(protopirate_hopper_update(app) && protopirate_scene_receiver_bind_rx_stack(app)) {
|
||||
protopirate_rx(app, app->txrx->preset->frequency);
|
||||
}
|
||||
static uint8_t hopper_statusbar_tick = 0;
|
||||
if(++hopper_statusbar_tick >= 8) {
|
||||
hopper_statusbar_tick = 0;
|
||||
|
||||
@@ -9,6 +9,7 @@ enum ProtoPirateSettingIndex {
|
||||
ProtoPirateSettingIndexTXPower,
|
||||
#endif
|
||||
ProtoPirateSettingIndexAutoSave,
|
||||
ProtoPirateSettingIndexCheckSaved,
|
||||
ProtoPirateSettingIndexLock,
|
||||
};
|
||||
|
||||
@@ -29,6 +30,12 @@ const char* const auto_save_text[AUTO_SAVE_COUNT] = {
|
||||
"ON",
|
||||
};
|
||||
|
||||
#define CHECK_SAVED_COUNT 2
|
||||
const char* const check_saved_text[CHECK_SAVED_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#define TX_POWER_COUNT 9
|
||||
const char* const tx_power_text[TX_POWER_COUNT] = {
|
||||
@@ -173,6 +180,14 @@ static void protopirate_scene_receiver_config_set_auto_save(VariableItem* item)
|
||||
variable_item_set_current_value_text(item, auto_save_text[index]);
|
||||
}
|
||||
|
||||
static void protopirate_scene_receiver_config_set_check_saved(VariableItem* item) {
|
||||
ProtoPirateApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
app->check_saved = (index == 1);
|
||||
variable_item_set_current_value_text(item, check_saved_text[index]);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
static void protopirate_scene_receiver_config_set_tx_power(VariableItem* item) {
|
||||
ProtoPirateApp* app = variable_item_get_context(item);
|
||||
@@ -268,6 +283,15 @@ void protopirate_scene_receiver_config_on_enter(void* context) {
|
||||
variable_item_set_current_value_index(item, app->auto_save ? 1 : 0);
|
||||
variable_item_set_current_value_text(item, auto_save_text[app->auto_save ? 1 : 0]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
app->variable_item_list,
|
||||
"Check Saved:",
|
||||
CHECK_SAVED_COUNT,
|
||||
protopirate_scene_receiver_config_set_check_saved,
|
||||
app);
|
||||
variable_item_set_current_value_index(item, app->check_saved ? 1 : 0);
|
||||
variable_item_set_current_value_text(item, check_saved_text[app->check_saved ? 1 : 0]);
|
||||
|
||||
variable_item_list_add(app->variable_item_list, "Lock Keyboard", 1, NULL, NULL);
|
||||
variable_item_list_set_enter_callback(
|
||||
app->variable_item_list, protopirate_scene_receiver_config_var_list_enter_callback, app);
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#include "../protopirate_app_i.h"
|
||||
#include "../helpers/protopirate_storage.h"
|
||||
#include "../helpers/protopirate_psa_bf_host.h"
|
||||
#include "../protocols/protocol_items.h"
|
||||
#include "proto_pirate_icons.h"
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "ProtoPirateReceiverInfo"
|
||||
|
||||
@@ -11,6 +13,25 @@ static void protopirate_scene_receiver_info_widget_callback(
|
||||
InputType type,
|
||||
void* context);
|
||||
|
||||
static bool
|
||||
protopirate_receiver_info_selected_protocol_is(ProtoPirateApp* app, const char* protocol_name) {
|
||||
if(!app || !app->txrx || !app->txrx->history || !protocol_name) return false;
|
||||
|
||||
FlipperFormat* ff =
|
||||
protopirate_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen);
|
||||
if(!ff) return false;
|
||||
|
||||
bool match = false;
|
||||
FuriString* protocol = furi_string_alloc();
|
||||
if(protocol) {
|
||||
flipper_format_rewind(ff);
|
||||
match = flipper_format_read_string(ff, FF_PROTOCOL, protocol) &&
|
||||
furi_string_cmp_str(protocol, protocol_name) == 0;
|
||||
furi_string_free(protocol);
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
static void protopirate_scene_receiver_info_text_input_callback(void* context) {
|
||||
ProtoPirateApp* app = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
@@ -19,6 +40,7 @@ static void protopirate_scene_receiver_info_text_input_callback(void* context) {
|
||||
|
||||
static void protopirate_receiver_info_build_normal_widget(ProtoPirateApp* app) {
|
||||
widget_reset(app->widget);
|
||||
app->emulate_disabled_for_loaded = true;
|
||||
|
||||
FuriString* text = furi_string_alloc();
|
||||
protopirate_history_get_text_item_menu(app->txrx->history, text, app->txrx->idx_menu_chosen);
|
||||
@@ -36,8 +58,11 @@ static void protopirate_receiver_info_build_normal_widget(ProtoPirateApp* app) {
|
||||
FuriString* protocol = furi_string_alloc();
|
||||
flipper_format_rewind(ff);
|
||||
if(flipper_format_read_string(ff, FF_PROTOCOL, protocol)) {
|
||||
if(furi_string_cmp_str(protocol, "PSA") == 0) is_psa = true;
|
||||
app->emulate_disabled_for_loaded = (furi_string_cmp_str(protocol, "Scher-Khan") == 0);
|
||||
const char* protocol_name = furi_string_get_cstr(protocol);
|
||||
if(strcmp(protopirate_protocol_catalog_canonical_name(protocol_name), "PSA") == 0)
|
||||
is_psa = true;
|
||||
app->emulate_disabled_for_loaded =
|
||||
!protopirate_protocol_catalog_can_tx(protocol_name);
|
||||
}
|
||||
furi_string_free(protocol);
|
||||
}
|
||||
@@ -129,7 +154,9 @@ static void protopirate_receiver_info_build_normal_widget(ProtoPirateApp* app) {
|
||||
widget_add_button_element(
|
||||
app->widget,
|
||||
GuiButtonTypeRight,
|
||||
"Save",
|
||||
protopirate_history_has_matched_saved(app->txrx->history, app->txrx->idx_menu_chosen) ?
|
||||
"Update" :
|
||||
"Save",
|
||||
protopirate_scene_receiver_info_widget_callback,
|
||||
app);
|
||||
|
||||
@@ -147,10 +174,15 @@ static void protopirate_scene_receiver_info_widget_callback(
|
||||
ProtoPirateApp* app = context;
|
||||
if(type == InputTypeShort || type == InputTypeLong) {
|
||||
if(result == GuiButtonTypeRight) {
|
||||
bool has_match = protopirate_history_has_matched_saved(
|
||||
app->txrx->history, app->txrx->idx_menu_chosen);
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, ProtoPirateCustomEventReceiverInfoSave);
|
||||
app->view_dispatcher,
|
||||
has_match ? ProtoPirateCustomEventReceiverInfoUpdate :
|
||||
ProtoPirateCustomEventReceiverInfoSave);
|
||||
} else if(result == GuiButtonTypeLeft) {
|
||||
if(protopirate_psa_bf_plugin_ensure_loaded(app) && app->psa_bf_plugin &&
|
||||
if(protopirate_receiver_info_selected_protocol_is(app, "PSA") &&
|
||||
protopirate_psa_bf_plugin_ensure_loaded(app) && app->psa_bf_plugin &&
|
||||
app->psa_bf_plugin->widget_left_should_bruteforce(
|
||||
app, ProtoPiratePsaBfContextReceiverInfo)) {
|
||||
view_dispatcher_send_custom_event(
|
||||
@@ -181,7 +213,7 @@ void protopirate_scene_receiver_info_on_enter(void* context) {
|
||||
|
||||
app->emulate_disabled_for_loaded = false;
|
||||
|
||||
if(protopirate_psa_bf_plugin_ensure_loaded(app) && app->psa_bf_plugin) {
|
||||
if(app->psa_bf_plugin) {
|
||||
if(app->psa_bf_plugin->is_running(app)) {
|
||||
app->psa_bf_plugin->on_scene_enter(app, ProtoPiratePsaBfContextReceiverInfo);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ProtoPirateViewWidget);
|
||||
@@ -197,7 +229,12 @@ bool protopirate_scene_receiver_info_on_event(void* context, SceneManagerEvent e
|
||||
ProtoPirateApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(protopirate_psa_bf_plugin_ensure_loaded(app) && app->psa_bf_plugin) {
|
||||
if((event.type == SceneManagerEventTypeCustom) &&
|
||||
(event.event == ProtoPirateCustomEventReceiverInfoBruteforceStart)) {
|
||||
protopirate_psa_bf_plugin_ensure_loaded(app);
|
||||
}
|
||||
|
||||
if(app->psa_bf_plugin) {
|
||||
if(app->psa_bf_plugin->is_running(app) ||
|
||||
event.event == ProtoPirateCustomEventPsaBruteforceComplete ||
|
||||
event.event == ProtoPirateCustomEventReceiverInfoBruteforceStart ||
|
||||
@@ -213,18 +250,36 @@ bool protopirate_scene_receiver_info_on_event(void* context, SceneManagerEvent e
|
||||
}
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == ProtoPirateCustomEventReceiverInfoUpdate) {
|
||||
uint16_t idx = app->txrx->idx_menu_chosen;
|
||||
const char* saved_path =
|
||||
protopirate_history_get_matched_saved_path(app->txrx->history, idx);
|
||||
if(saved_path) {
|
||||
FlipperFormat* rx_ff = protopirate_history_get_raw_data(app->txrx->history, idx);
|
||||
if(rx_ff) {
|
||||
if(protopirate_storage_save_capture_to_path(rx_ff, saved_path)) {
|
||||
notification_message(app->notifications, &sequence_success);
|
||||
FURI_LOG_I(TAG, "Updated saved capture from received signal: %s", saved_path);
|
||||
} else {
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
FURI_LOG_E(TAG, "Failed to update saved capture: %s", saved_path);
|
||||
}
|
||||
protopirate_history_release_scratch(app->txrx->history);
|
||||
} else {
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
FURI_LOG_E(TAG, "No received capture available for update");
|
||||
}
|
||||
}
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ProtoPirateViewWidget);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
if(event.event == ProtoPirateCustomEventReceiverInfoSave) {
|
||||
FlipperFormat* ff =
|
||||
protopirate_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen);
|
||||
if(ff) {
|
||||
FuriString* protocol = furi_string_alloc();
|
||||
flipper_format_rewind(ff);
|
||||
if(!flipper_format_read_string(ff, FF_PROTOCOL, protocol)) {
|
||||
furi_string_set_str(protocol, "Unknown");
|
||||
}
|
||||
|
||||
furi_string_replace_all(protocol, "/", "_");
|
||||
furi_string_replace_all(protocol, " ", "_");
|
||||
protopirate_storage_get_capture_display_protocol(ff, protocol);
|
||||
|
||||
FuriString* auto_path = furi_string_alloc();
|
||||
if(protopirate_storage_get_next_filename(
|
||||
@@ -326,9 +381,9 @@ bool protopirate_scene_receiver_info_on_event(void* context, SceneManagerEvent e
|
||||
void protopirate_scene_receiver_info_on_exit(void* context) {
|
||||
furi_check(context);
|
||||
ProtoPirateApp* app = context;
|
||||
protopirate_psa_bf_context_release(app);
|
||||
widget_reset(app->widget);
|
||||
if(app->txrx && app->txrx->history) {
|
||||
protopirate_history_release_scratch(app->txrx->history);
|
||||
}
|
||||
protopirate_psa_bf_plugin_unload_if_idle(app);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,67 @@
|
||||
// scenes/protopirate_scene_saved.c
|
||||
#include "../protopirate_app_i.h"
|
||||
#include "../helpers/protopirate_storage.h"
|
||||
|
||||
#include "proto_pirate_icons.h"
|
||||
|
||||
#define TAG "ProtoPirateSceneSaved"
|
||||
|
||||
void protopirate_scene_saved_on_enter(void* context) {
|
||||
furi_check(context);
|
||||
ProtoPirateApp* app = context;
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(storage) {
|
||||
if(!storage_dir_exists(storage, PROTOPIRATE_APP_FOLDER)) {
|
||||
storage_simply_mkdir(storage, PROTOPIRATE_APP_FOLDER);
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
if(!app->file_path) {
|
||||
FURI_LOG_E(TAG, "file_path is NULL");
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!app->dialogs) {
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
if(!app->dialogs) {
|
||||
FURI_LOG_E(TAG, "Failed to open dialogs");
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, ".psf", &I_subghz_10px);
|
||||
browser_options.base_path = PROTOPIRATE_APP_FOLDER;
|
||||
browser_options.skip_assets = true;
|
||||
browser_options.hide_dot_files = true;
|
||||
|
||||
furi_string_set(app->file_path, PROTOPIRATE_APP_FOLDER);
|
||||
|
||||
FuriString* selection = furi_string_alloc();
|
||||
if(app->loaded_file_path && !furi_string_empty(app->loaded_file_path)) {
|
||||
furi_string_set(selection, app->loaded_file_path);
|
||||
} else {
|
||||
furi_string_set(selection, PROTOPIRATE_APP_FOLDER);
|
||||
}
|
||||
|
||||
bool file_selected =
|
||||
dialog_file_browser_show(app->dialogs, selection, app->file_path, &browser_options);
|
||||
|
||||
if(file_selected) {
|
||||
if(app->loaded_file_path) {
|
||||
furi_string_free(app->loaded_file_path);
|
||||
}
|
||||
app->loaded_file_path = furi_string_alloc_set(selection);
|
||||
furi_string_free(selection);
|
||||
scene_manager_next_scene(app->scene_manager, ProtoPirateSceneSavedInfo);
|
||||
} else {
|
||||
furi_string_free(selection);
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
}
|
||||
}
|
||||
|
||||
bool protopirate_scene_saved_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// scenes/protopirate_scene_saved_info.c
|
||||
#include "../protopirate_app_i.h"
|
||||
#include "../helpers/protopirate_storage.h"
|
||||
#include "../protocols/protocol_items.h"
|
||||
#include "../protocols/protocols_common.h"
|
||||
#include "proto_pirate_icons.h"
|
||||
|
||||
@@ -107,14 +108,14 @@ void protopirate_scene_saved_info_on_enter(void* context) {
|
||||
|
||||
// Read fields
|
||||
uint32_t temp_data = 0;
|
||||
app->emulate_disabled_for_loaded = false;
|
||||
app->emulate_disabled_for_loaded = true;
|
||||
|
||||
flipper_format_rewind(ff);
|
||||
if(flipper_format_read_string(ff, FF_PROTOCOL, temp_str)) {
|
||||
furi_string_cat_printf(info_str, "Protocol: %s\n", furi_string_get_cstr(temp_str));
|
||||
}
|
||||
if(furi_string_cmp_str(temp_str, "Scher-Khan") == 0) {
|
||||
app->emulate_disabled_for_loaded = true;
|
||||
const char* protocol_name = furi_string_get_cstr(temp_str);
|
||||
furi_string_cat_printf(info_str, "Protocol: %s\n", protocol_name);
|
||||
app->emulate_disabled_for_loaded =
|
||||
!protopirate_protocol_catalog_can_tx(protocol_name);
|
||||
}
|
||||
|
||||
flipper_format_rewind(ff);
|
||||
|
||||
@@ -19,113 +19,10 @@ typedef enum {
|
||||
SubmenuIndexProtoPirateAbout,
|
||||
} SubmenuIndex;
|
||||
|
||||
// Forward declaration
|
||||
static void protopirate_scene_start_open_saved_captures(ProtoPirateApp* app);
|
||||
|
||||
static void protopirate_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
furi_check(context);
|
||||
ProtoPirateApp* app = context;
|
||||
|
||||
// Handle "Saved Captures" directly here, not via custom event
|
||||
if(index == SubmenuIndexProtoPirateSaved) {
|
||||
protopirate_scene_start_open_saved_captures(app);
|
||||
} else {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
}
|
||||
}
|
||||
|
||||
static void protopirate_scene_start_open_saved_captures(ProtoPirateApp* app) {
|
||||
FURI_LOG_I(TAG, "[1] Opening saved captures browser");
|
||||
FURI_LOG_I(TAG, "[1a] PROTOPIRATE_APP_FOLDER = %s", PROTOPIRATE_APP_FOLDER);
|
||||
|
||||
// Check and create folder
|
||||
FURI_LOG_D(TAG, "[2] Opening storage");
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
if(!storage) {
|
||||
FURI_LOG_E(TAG, "[2a] Failed to open storage!");
|
||||
return;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "[3] Checking folder exists");
|
||||
|
||||
if(!storage_dir_exists(storage, PROTOPIRATE_APP_FOLDER)) {
|
||||
FURI_LOG_I(TAG, "[4] Creating folder");
|
||||
storage_simply_mkdir(storage, PROTOPIRATE_APP_FOLDER);
|
||||
}
|
||||
|
||||
#ifndef REMOVE_LOGS
|
||||
bool folder_ok = storage_dir_exists(storage, PROTOPIRATE_APP_FOLDER);
|
||||
FURI_LOG_D(TAG, "[5] Folder exists: %s", folder_ok ? "yes" : "no");
|
||||
#endif
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
FURI_LOG_D(TAG, "[6] Storage closed");
|
||||
|
||||
// Check file_path
|
||||
FURI_LOG_D(TAG, "[7] Checking app->file_path");
|
||||
if(!app->file_path) {
|
||||
FURI_LOG_E(TAG, "[7a] app->file_path is NULL!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set starting path
|
||||
FURI_LOG_D(TAG, "[8] Setting file_path");
|
||||
furi_string_set(app->file_path, PROTOPIRATE_APP_FOLDER);
|
||||
FURI_LOG_D(TAG, "[9] file_path set to: %s", furi_string_get_cstr(app->file_path));
|
||||
|
||||
// Configure file browser
|
||||
FURI_LOG_D(TAG, "[10] Creating browser_options");
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
|
||||
FURI_LOG_D(TAG, "[11] Calling dialog_file_browser_set_basic_options");
|
||||
dialog_file_browser_set_basic_options(&browser_options, ".psf", &I_subghz_10px);
|
||||
|
||||
FURI_LOG_D(TAG, "[12] Setting browser_options fields");
|
||||
browser_options.base_path = PROTOPIRATE_APP_FOLDER;
|
||||
browser_options.skip_assets = true;
|
||||
browser_options.hide_dot_files = true;
|
||||
|
||||
FURI_LOG_D(TAG, "[13] Checking app->dialogs");
|
||||
FURI_LOG_D(TAG, "[13a] app->dialogs = %p", (void*)app->dialogs);
|
||||
|
||||
if(!app->dialogs) {
|
||||
FURI_LOG_E(TAG, "[13b] dialogs is NULL! Trying to open...");
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
if(!app->dialogs) {
|
||||
FURI_LOG_E(TAG, "[13c] Still NULL after open attempt!");
|
||||
return;
|
||||
}
|
||||
FURI_LOG_I(TAG, "[13d] dialogs opened successfully");
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "[14] === CALLING dialog_file_browser_show ===");
|
||||
FURI_LOG_D(TAG, "[14a] dialogs=%p, file_path=%p", (void*)app->dialogs, (void*)app->file_path);
|
||||
|
||||
bool file_selected =
|
||||
dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options);
|
||||
|
||||
FURI_LOG_I(TAG, "[15] === RETURNED from dialog_file_browser_show ===");
|
||||
FURI_LOG_D(TAG, "[15a] file_selected = %d", file_selected);
|
||||
|
||||
if(file_selected) {
|
||||
FURI_LOG_I(TAG, "[16] File selected: %s", furi_string_get_cstr(app->file_path));
|
||||
|
||||
if(app->loaded_file_path) {
|
||||
FURI_LOG_D(TAG, "[17] Freeing old loaded_file_path");
|
||||
furi_string_free(app->loaded_file_path);
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "[18] Allocating new loaded_file_path");
|
||||
app->loaded_file_path = furi_string_alloc_set(app->file_path);
|
||||
|
||||
FURI_LOG_D(TAG, "[19] Navigating to SavedInfo scene");
|
||||
scene_manager_next_scene(app->scene_manager, ProtoPirateSceneSavedInfo);
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "[16] File browser cancelled or empty");
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "[20] open_saved_captures complete");
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void protopirate_scene_start_on_enter(void* context) {
|
||||
@@ -196,6 +93,9 @@ bool protopirate_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(event.event == SubmenuIndexProtoPirateReceiver) {
|
||||
scene_manager_next_scene(app->scene_manager, ProtoPirateSceneReceiver);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexProtoPirateSaved) {
|
||||
scene_manager_next_scene(app->scene_manager, ProtoPirateSceneSaved);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexProtoPirateReceiverConfig) {
|
||||
scene_manager_next_scene(app->scene_manager, ProtoPirateSceneReceiverConfig);
|
||||
consumed = true;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,42 @@
|
||||
// scenes/protopirate_scene_timing_tuner.c
|
||||
#include "../protopirate_app_i.h"
|
||||
#ifdef ENABLE_TIMING_TUNER_SCENE
|
||||
#include "../protocols/protocol_items.h"
|
||||
|
||||
#ifndef PROTOPIRATE_TIMING_TUNER_PLUGIN_BUILD
|
||||
|
||||
void protopirate_scene_timing_tuner_on_enter(void* context) {
|
||||
protopirate_tool_scene_on_enter(context, ProtoPirateToolScenePluginKindTimingTuner);
|
||||
}
|
||||
|
||||
bool protopirate_scene_timing_tuner_on_event(void* context, SceneManagerEvent event) {
|
||||
return protopirate_tool_scene_on_event(context, event);
|
||||
}
|
||||
|
||||
void protopirate_scene_timing_tuner_on_exit(void* context) {
|
||||
protopirate_tool_scene_on_exit(context);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "../protopirate_history.h"
|
||||
#include "../protocols/protocol_timings.h"
|
||||
#include <gui/elements.h>
|
||||
#include <math.h>
|
||||
|
||||
#define TAG "ProtoPirateTimingTuner"
|
||||
|
||||
static const ProtoPirateToolSceneHostApi* g_tool_scene_host_api = NULL;
|
||||
|
||||
#define protopirate_ensure_view_about(app) g_tool_scene_host_api->ensure_view_about(app)
|
||||
#define protopirate_radio_init(app) g_tool_scene_host_api->radio_init(app)
|
||||
#define protopirate_rx_stack_resume_after_tx(app) \
|
||||
g_tool_scene_host_api->rx_stack_resume_after_tx(app)
|
||||
#define protopirate_begin(app, preset_data) g_tool_scene_host_api->begin(app, preset_data)
|
||||
#define protopirate_rx(app, frequency) g_tool_scene_host_api->rx(app, frequency)
|
||||
#define protopirate_rx_end(app) g_tool_scene_host_api->rx_end(app)
|
||||
#define protopirate_history_release_scratch(history) \
|
||||
g_tool_scene_host_api->history_release_scratch(history)
|
||||
|
||||
#define MAX_TIMING_SAMPLES 512
|
||||
#define VISIBLE_LINES 6
|
||||
#define LINE_HEIGHT 9
|
||||
@@ -49,6 +79,13 @@ typedef struct {
|
||||
|
||||
static TimingTunerContext* g_timing_ctx = NULL;
|
||||
|
||||
static void timing_tuner_context_free(void) {
|
||||
if(g_timing_ctx) {
|
||||
free(g_timing_ctx);
|
||||
g_timing_ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void calculate_timing_stats(TimingTunerContext* ctx) {
|
||||
size_t num_samples = ctx->buffer_wrapped ? MAX_TIMING_SAMPLES : ctx->write_idx;
|
||||
|
||||
@@ -614,22 +651,26 @@ void protopirate_scene_timing_tuner_on_enter(void* context) {
|
||||
|
||||
if(!protopirate_ensure_view_about(app)) {
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
app->tool_scene_nav_pending = TOOL_SCENE_NAV_POP;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!app->radio_initialized && !protopirate_radio_init(app)) {
|
||||
FURI_LOG_E(TAG, "Failed to initialize radio for timing tuner");
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
app->tool_scene_nav_pending = TOOL_SCENE_NAV_POP;
|
||||
return;
|
||||
}
|
||||
|
||||
if(app->txrx && app->txrx->history) {
|
||||
protopirate_history_release_scratch(app->txrx->history);
|
||||
}
|
||||
|
||||
protopirate_rx_stack_resume_after_tx(app);
|
||||
if(!app->txrx->receiver) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate receiver for timing tuner");
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
app->tool_scene_nav_pending = TOOL_SCENE_NAV_POP;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -637,7 +678,7 @@ void protopirate_scene_timing_tuner_on_enter(void* context) {
|
||||
if(!g_timing_ctx) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate timing tuner context");
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
app->tool_scene_nav_pending = TOOL_SCENE_NAV_POP;
|
||||
return;
|
||||
}
|
||||
memset(g_timing_ctx, 0, sizeof(TimingTunerContext));
|
||||
@@ -661,6 +702,11 @@ void protopirate_scene_timing_tuner_on_enter(void* context) {
|
||||
app->txrx->worker = subghz_worker_alloc();
|
||||
if(!app->txrx->worker) {
|
||||
FURI_LOG_E(TAG, "Failed to allocate worker!");
|
||||
view_set_draw_callback(app->view_about, NULL);
|
||||
view_set_input_callback(app->view_about, NULL);
|
||||
timing_tuner_context_free();
|
||||
notification_message(app->notifications, &sequence_error);
|
||||
app->tool_scene_nav_pending = TOOL_SCENE_NAV_POP;
|
||||
return;
|
||||
}
|
||||
// Set up worker callbacks
|
||||
@@ -689,14 +735,15 @@ bool protopirate_scene_timing_tuner_on_event(void* context, SceneManagerEvent ev
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == 0) {
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
app->tool_scene_nav_pending = TOOL_SCENE_NAV_POP;
|
||||
consumed = true;
|
||||
} else if(event.event == 1) {
|
||||
if(g_timing_ctx && g_timing_ctx->is_receiving) {
|
||||
protopirate_rx_end(app);
|
||||
g_timing_ctx->is_receiving = false;
|
||||
}
|
||||
scene_manager_next_scene(app->scene_manager, ProtoPirateSceneReceiverConfig);
|
||||
app->tool_scene_nav_pending = TOOL_SCENE_NAV_NEXT;
|
||||
app->tool_scene_nav_target = ProtoPirateSceneReceiverConfig;
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
@@ -723,10 +770,16 @@ void protopirate_scene_timing_tuner_on_exit(void* context) {
|
||||
protopirate_rx_end(app);
|
||||
}
|
||||
|
||||
subghz_worker_set_pair_callback(
|
||||
app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
|
||||
if(app->txrx && app->txrx->receiver) {
|
||||
subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, NULL);
|
||||
}
|
||||
|
||||
if(app->txrx->worker) {
|
||||
if(app->txrx && app->txrx->worker) {
|
||||
subghz_worker_set_pair_callback(
|
||||
app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
|
||||
}
|
||||
|
||||
if(app->txrx && app->txrx->worker) {
|
||||
FURI_LOG_D(TAG, "Freeing worker %p", app->txrx->worker);
|
||||
subghz_worker_free(app->txrx->worker);
|
||||
app->txrx->worker = NULL;
|
||||
@@ -734,12 +787,38 @@ void protopirate_scene_timing_tuner_on_exit(void* context) {
|
||||
FURI_LOG_D(TAG, "Worker was NULL, skipping free");
|
||||
}
|
||||
|
||||
view_set_draw_callback(app->view_about, NULL);
|
||||
view_set_input_callback(app->view_about, NULL);
|
||||
|
||||
if(g_timing_ctx) {
|
||||
free(g_timing_ctx);
|
||||
g_timing_ctx = NULL;
|
||||
if(app->view_about) {
|
||||
view_set_draw_callback(app->view_about, NULL);
|
||||
view_set_input_callback(app->view_about, NULL);
|
||||
view_set_context(app->view_about, NULL);
|
||||
}
|
||||
|
||||
timing_tuner_context_free();
|
||||
}
|
||||
|
||||
static void timing_tuner_plugin_set_host_api(const ProtoPirateToolSceneHostApi* host_api) {
|
||||
g_tool_scene_host_api = host_api;
|
||||
}
|
||||
|
||||
static const ProtoPirateToolScenePlugin protopirate_timing_tuner_plugin = {
|
||||
.plugin_name = "ProtoPirate Timing Tuner",
|
||||
.kind = ProtoPirateToolScenePluginKindTimingTuner,
|
||||
.set_host_api = timing_tuner_plugin_set_host_api,
|
||||
.on_enter = protopirate_scene_timing_tuner_on_enter,
|
||||
.on_event = protopirate_scene_timing_tuner_on_event,
|
||||
.on_exit = protopirate_scene_timing_tuner_on_exit,
|
||||
.release = NULL,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor protopirate_timing_tuner_plugin_descriptor = {
|
||||
.appid = PROTOPIRATE_TOOL_SCENE_PLUGIN_APP_ID,
|
||||
.ep_api_version = PROTOPIRATE_TOOL_SCENE_PLUGIN_API_VERSION,
|
||||
.entry_point = &protopirate_timing_tuner_plugin,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* protopirate_timing_tuner_plugin_ep(void) {
|
||||
return &protopirate_timing_tuner_plugin_descriptor;
|
||||
}
|
||||
|
||||
#endif // PROTOPIRATE_TIMING_TUNER_PLUGIN_BUILD
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <furi.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "proto_pirate_icons.h"
|
||||
|
||||
@@ -34,6 +35,7 @@ typedef struct {
|
||||
ProtoPirateLock lock;
|
||||
uint8_t lock_count;
|
||||
uint8_t animation_frame;
|
||||
uint8_t sub_decode_progress;
|
||||
bool dolphin_view;
|
||||
bool sub_decode_mode;
|
||||
} ProtoPirateReceiverModel;
|
||||
@@ -110,7 +112,24 @@ void protopirate_view_receiver_set_sub_decode_mode(
|
||||
with_view_model(
|
||||
receiver->view,
|
||||
ProtoPirateReceiverModel * model,
|
||||
{ model->sub_decode_mode = sub_decode_mode; },
|
||||
{
|
||||
model->sub_decode_mode = sub_decode_mode;
|
||||
if(!sub_decode_mode) {
|
||||
model->sub_decode_progress = 0;
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void protopirate_view_receiver_set_sub_decode_progress(
|
||||
ProtoPirateReceiver* receiver,
|
||||
uint8_t progress) {
|
||||
furi_check(receiver);
|
||||
if(progress > 100) progress = 100;
|
||||
with_view_model(
|
||||
receiver->view,
|
||||
ProtoPirateReceiverModel * model,
|
||||
{ model->sub_decode_progress = progress; },
|
||||
true);
|
||||
}
|
||||
|
||||
@@ -202,6 +221,23 @@ static void protopirate_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, b
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
|
||||
}
|
||||
|
||||
static void
|
||||
protopirate_view_receiver_draw_progress_badge(Canvas* canvas, uint8_t progress) {
|
||||
char progress_text[8];
|
||||
snprintf(progress_text, sizeof(progress_text), "%u%%", progress > 100 ? 100 : progress);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
uint8_t width = canvas_string_width(canvas, progress_text) + 8;
|
||||
if(width < 30) width = 30;
|
||||
if(width > 42) width = 42;
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_rbox(canvas, 0, 52, width, 12, 2);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_str_aligned(canvas, width / 2, 62, AlignCenter, AlignBottom, progress_text);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* model) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
@@ -220,13 +256,20 @@ void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* mo
|
||||
|
||||
// Draw RSSI
|
||||
protopirate_view_rssi_draw(canvas, model);
|
||||
} else {
|
||||
protopirate_view_receiver_draw_progress_badge(canvas, model->sub_decode_progress);
|
||||
}
|
||||
|
||||
//Draw To Unlock, Locked etc...
|
||||
if(model->lock_count) {
|
||||
canvas_draw_str(canvas, 44, 63, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 79, 63, furi_string_get_cstr(model->preset_str));
|
||||
canvas_draw_str(canvas, 96, 63, furi_string_get_cstr(model->history_stat_str));
|
||||
if(model->sub_decode_mode) {
|
||||
canvas_draw_str(canvas, 44, 63, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 96, 63, furi_string_get_cstr(model->history_stat_str));
|
||||
} else {
|
||||
canvas_draw_str(canvas, 44, 63, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 79, 63, furi_string_get_cstr(model->preset_str));
|
||||
canvas_draw_str(canvas, 96, 63, furi_string_get_cstr(model->history_stat_str));
|
||||
}
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
|
||||
elements_multiline_text(canvas, 65, 26, "To unlock\npress:");
|
||||
@@ -240,9 +283,14 @@ void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* mo
|
||||
canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);
|
||||
canvas_draw_str(canvas, 74, 62, "Locked");
|
||||
} else {
|
||||
canvas_draw_str(canvas, 44, 63, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 79, 63, furi_string_get_cstr(model->preset_str));
|
||||
canvas_draw_str(canvas, 96, 63, furi_string_get_cstr(model->history_stat_str));
|
||||
if(model->sub_decode_mode) {
|
||||
canvas_draw_str(canvas, 44, 63, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 96, 63, furi_string_get_cstr(model->history_stat_str));
|
||||
} else {
|
||||
canvas_draw_str(canvas, 44, 63, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 79, 63, furi_string_get_cstr(model->preset_str));
|
||||
canvas_draw_str(canvas, 96, 63, furi_string_get_cstr(model->history_stat_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +328,7 @@ void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* mo
|
||||
}
|
||||
} else {
|
||||
//Are we in Radar View or FLipper View Mode?
|
||||
if(!model->dolphin_view) {
|
||||
if(!model->sub_decode_mode && !model->dolphin_view) {
|
||||
const uint8_t center_x = 64;
|
||||
const uint8_t center_y = 22;
|
||||
for(uint8_t wave = 0; wave < 3; wave++) {
|
||||
@@ -345,16 +393,20 @@ void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* mo
|
||||
//canvas_draw_str(canvas, 44, 10, model->external_radio ? "Ext" : "Int"); //FOR EXACT FLIPPER CLONE
|
||||
}
|
||||
|
||||
// Draw EXT/INT indicator in upper right corner
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if(model->external_radio) {
|
||||
canvas_draw_str_aligned(canvas, 127, 0, AlignRight, AlignTop, "Ext");
|
||||
if(model->sub_decode_mode) {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 127, 0, AlignRight, AlignTop, furi_string_get_cstr(model->preset_str));
|
||||
} else {
|
||||
canvas_draw_str_aligned(canvas, 127, 0, AlignRight, AlignTop, "Int");
|
||||
if(model->external_radio) {
|
||||
canvas_draw_str_aligned(canvas, 127, 0, AlignRight, AlignTop, "Ext");
|
||||
} else {
|
||||
canvas_draw_str_aligned(canvas, 127, 0, AlignRight, AlignTop, "Int");
|
||||
}
|
||||
}
|
||||
|
||||
//Draw the Auto-save Indicator
|
||||
if(model->auto_save) {
|
||||
if(!model->sub_decode_mode && model->auto_save) {
|
||||
const char* auto_save_text = "Save";
|
||||
canvas_draw_str(
|
||||
canvas, 110 - canvas_string_width(canvas, auto_save_text), 7, auto_save_text);
|
||||
@@ -369,8 +421,15 @@ bool protopirate_view_receiver_input(InputEvent* event, void* context) {
|
||||
bool consumed = false;
|
||||
|
||||
ProtoPirateLock lock;
|
||||
bool sub_decode_mode = false;
|
||||
with_view_model(
|
||||
receiver->view, ProtoPirateReceiverModel * model, { lock = model->lock; }, false);
|
||||
receiver->view,
|
||||
ProtoPirateReceiverModel * model,
|
||||
{
|
||||
lock = model->lock;
|
||||
sub_decode_mode = model->sub_decode_mode;
|
||||
},
|
||||
false);
|
||||
|
||||
if(lock == ProtoPirateLockOn) {
|
||||
bool do_unlock_cb = false;
|
||||
@@ -433,7 +492,7 @@ bool protopirate_view_receiver_input(InputEvent* event, void* context) {
|
||||
consumed = true;
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
if(receiver->callback) {
|
||||
if(!sub_decode_mode && receiver->callback) {
|
||||
receiver->callback(ProtoPirateCustomEventViewReceiverConfig, receiver->context);
|
||||
}
|
||||
consumed = true;
|
||||
@@ -465,7 +524,7 @@ bool protopirate_view_receiver_input(InputEvent* event, void* context) {
|
||||
|
||||
if(item_count > 0) {
|
||||
do_ok_cb = true;
|
||||
} else if(event->type == InputTypeLong) {
|
||||
} else if(!sub_decode_mode && event->type == InputTypeLong) {
|
||||
do_toggle = true;
|
||||
}
|
||||
},
|
||||
@@ -538,6 +597,7 @@ ProtoPirateReceiver* protopirate_view_receiver_alloc(bool auto_save) {
|
||||
model->lock_count = 0;
|
||||
model->auto_save = auto_save;
|
||||
model->animation_frame = 0;
|
||||
model->sub_decode_progress = 0;
|
||||
model->dolphin_view = true;
|
||||
model->sub_decode_mode = false;
|
||||
},
|
||||
|
||||
@@ -33,6 +33,9 @@ void protopirate_view_receiver_set_autosave(ProtoPirateReceiver* receiver, bool
|
||||
void protopirate_view_receiver_set_sub_decode_mode(
|
||||
ProtoPirateReceiver* receiver,
|
||||
bool sub_decode_mode);
|
||||
void protopirate_view_receiver_set_sub_decode_progress(
|
||||
ProtoPirateReceiver* receiver,
|
||||
uint8_t progress);
|
||||
void protopirate_view_receiver_reset_menu(ProtoPirateReceiver* receiver);
|
||||
|
||||
void protopirate_view_receiver_sync_menu_from_history(
|
||||
|
||||
Reference in New Issue
Block a user