mirror of
https://github.com/bettse/seader.git
synced 2026-04-28 12:45:30 +00:00
Tighten runtime ownership and metadata resets
This commit is contained in:
+2
-1
@@ -21,6 +21,7 @@ App(
|
||||
"*.c",
|
||||
"aeabi_uldivmod.sx",
|
||||
"!hf_interface_fal/*.c",
|
||||
"!wiegand_interface_fal/*.c",
|
||||
],
|
||||
fap_icon="icons/logo.png",
|
||||
fap_category="NFC",
|
||||
@@ -60,7 +61,7 @@ App(
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="plugin_wiegand_ep",
|
||||
requires=["seader"],
|
||||
sources=["hf_interface_fal/wiegand.c"],
|
||||
sources=["wiegand_interface_fal/wiegand.c"],
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
|
||||
+37
-15
@@ -79,7 +79,7 @@ Rules:
|
||||
| `HF` | `Loaded` | plugin manager, plugin EP, plugin ctx may be non-`NULL`; pollers may be `NULL` |
|
||||
| `HF` | `Active` | plugin manager, plugin EP, plugin ctx must be non-`NULL`; active pollers may be non-`NULL` |
|
||||
| `HF` | `TearingDown` | teardown owns all pointer mutation; no scene code may touch HF runtime |
|
||||
| `UHF` | `Unloaded` | all HF runtime pointers must be `NULL` |
|
||||
| `UHF` | `Unloaded` | all HF runtime pointers must be `NULL`; UHF maintenance/probe flow owns mode runtime |
|
||||
|
||||
Invalid combinations are bugs:
|
||||
|
||||
@@ -92,14 +92,23 @@ Invalid combinations are bugs:
|
||||
|
||||
### HF startup
|
||||
|
||||
1. Verify `mode_runtime == None`
|
||||
2. Verify `hf_session_state == Unloaded`
|
||||
3. Load `plugin_hf.fal`
|
||||
4. Resolve plugin entry point
|
||||
5. Allocate plugin context
|
||||
6. Set `hf_session_state = Loaded`
|
||||
7. Set `mode_runtime = HF`
|
||||
8. Start read and transition to `Active`
|
||||
Legal startup paths:
|
||||
|
||||
1. Cold acquire:
|
||||
- verify `mode_runtime == None`
|
||||
- verify `hf_session_state == Unloaded`
|
||||
- load `plugin_hf.fal`
|
||||
- resolve plugin entry point
|
||||
- allocate plugin context
|
||||
- set `hf_session_state = Loaded`
|
||||
- set `mode_runtime = HF`
|
||||
- start read and transition to `Active`
|
||||
2. Fast-path re-acquire:
|
||||
- allowed only when the existing HF runtime is already coherent
|
||||
- preserve the existing `Loaded` or `Active` state
|
||||
- do not unload/reload the plugin
|
||||
|
||||
Any partial pointer/state combination must first normalize to `Unloaded`.
|
||||
|
||||
### HF teardown
|
||||
|
||||
@@ -113,7 +122,8 @@ Invalid combinations are bugs:
|
||||
8. Set `hf_session_state = Unloaded`
|
||||
9. Set `mode_runtime = None`
|
||||
|
||||
This order must be implemented in one worker-owned path and nowhere else.
|
||||
The blocking fallback teardown path must use the same state machine and ordering.
|
||||
This order must be implemented in one worker-owned release primitive and nowhere else.
|
||||
|
||||
## Forbidden actions
|
||||
|
||||
@@ -125,17 +135,28 @@ This order must be implemented in one worker-owned path and nowhere else.
|
||||
- Starting UHF while HF session state is not `Unloaded`
|
||||
- Starting HF while `mode_runtime == UHF`
|
||||
|
||||
## Plugin boundary
|
||||
## UHF runtime
|
||||
|
||||
`hf_interface_fal/` is part of this repository. It is not a submodule.
|
||||
`SeaderModeRuntimeUHF` is active only while the SAM maintenance/SNMP probe flow is active.
|
||||
|
||||
Rules:
|
||||
|
||||
- HF plugin source must remain in-tree and follow this contract.
|
||||
- UHF runtime must be entered when the probe starts.
|
||||
- UHF runtime must be cleared when the probe finishes.
|
||||
- While UHF runtime is active, HF acquire must be rejected.
|
||||
- UHF runtime must not coexist with any live HF runtime pointer.
|
||||
|
||||
## Plugin boundary
|
||||
|
||||
`hf_interface_fal/` and `wiegand_interface_fal/` are part of this repository. They are not submodules.
|
||||
|
||||
Rules:
|
||||
|
||||
- HF and Wiegand plugin sources must remain in-tree and follow this contract.
|
||||
- The host/plugin boundary is narrow:
|
||||
- host owns orchestration, SAM transport, UI routing, and lifetime
|
||||
- plugin owns HF protocol execution only
|
||||
- The plugin must not directly own scene transitions or global app teardown.
|
||||
- each plugin owns only its protocol-specific execution
|
||||
- Plugins must not directly own scene transitions or global app teardown.
|
||||
|
||||
## Change checklist
|
||||
|
||||
@@ -147,4 +168,5 @@ Before merging a change that touches HF/UHF/session code, confirm:
|
||||
- no scene code mutates live HF runtime
|
||||
- no teardown path mutates app-lifetime callback wiring
|
||||
- all state-table invariants still hold
|
||||
- `OWNERSHIP_MODEL.md` changed in the same patch as any lifetime/order/state-machine change
|
||||
- this document still matches the implementation
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Seader embedded HF plugin sources
|
||||
|
||||
This directory is part of the main Seader repository.
|
||||
|
||||
It contains the embedded HF `.fal` plugin sources used by Seader:
|
||||
- `hf.c`
|
||||
- `hf_interface.h`
|
||||
|
||||
The HF plugin source path in `application.fam` must point at `hf_interface_fal/hf.c`.
|
||||
@@ -1,9 +0,0 @@
|
||||
# Seader embedded plugin sources
|
||||
|
||||
This directory is part of the main Seader repository.
|
||||
|
||||
It contains the embedded `.fal` plugin sources used by Seader:
|
||||
- `wiegand.c`
|
||||
- `hf.c`
|
||||
|
||||
The source paths in `application.fam` must point at `hf_interface_fal/*.c`.
|
||||
@@ -71,6 +71,17 @@ static SeaderWorker* seader_get_active_worker(Seader* seader) {
|
||||
return seader ? seader->worker : NULL;
|
||||
}
|
||||
|
||||
static void seader_reset_cached_sam_metadata(Seader* seader) {
|
||||
if(!seader) {
|
||||
return;
|
||||
}
|
||||
|
||||
seader->sam_version[0] = 0U;
|
||||
seader->sam_version[1] = 0U;
|
||||
seader->uhf_status_label[0] = '\0';
|
||||
seader_uhf_snmp_probe_init(&seader->snmp_probe);
|
||||
}
|
||||
|
||||
static bool seader_snmp_probe_send_next_request(Seader* seader) {
|
||||
SeaderUartBridge* seader_uart = seader_get_uart(seader);
|
||||
uint8_t* scratch = seader_uart ? (seader_uart->tx_buf + MAX_FRAME_HEADERS) : NULL;
|
||||
@@ -99,6 +110,9 @@ static void seader_snmp_probe_finish(Seader* seader) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(seader->mode_runtime == SeaderModeRuntimeUHF) {
|
||||
seader->mode_runtime = SeaderModeRuntimeNone;
|
||||
}
|
||||
seader_sam_set_state(seader, SeaderSamStateIdle, SeaderSamIntentNone, SamCommand_PR_NOTHING);
|
||||
}
|
||||
|
||||
@@ -107,6 +121,7 @@ static void seader_start_snmp_probe(Seader* seader) {
|
||||
return;
|
||||
}
|
||||
|
||||
seader->mode_runtime = SeaderModeRuntimeUHF;
|
||||
seader_uhf_snmp_probe_init(&seader->snmp_probe);
|
||||
seader_update_uhf_status_label(seader);
|
||||
seader_sam_set_state(
|
||||
@@ -648,6 +663,7 @@ void seader_worker_send_serial_number(Seader* seader) {
|
||||
void seader_worker_send_version(Seader* seader) {
|
||||
SamCommand_t samCommand = {0};
|
||||
samCommand.present = SamCommand_PR_version;
|
||||
seader_reset_cached_sam_metadata(seader);
|
||||
seader->sam_present = true;
|
||||
seader_update_sam_key_label(seader, NULL, 0U);
|
||||
seader_sam_set_state(
|
||||
|
||||
@@ -21,9 +21,6 @@ void seader_scene_read_prepare(Seader* seader) {
|
||||
seader->samCommand = SamCommand_PR_NOTHING;
|
||||
}
|
||||
memset(seader->read_error, 0, sizeof(seader->read_error));
|
||||
if(seader->worker) {
|
||||
seader_worker_reset_poller_session(seader->worker);
|
||||
}
|
||||
}
|
||||
|
||||
void seader_scene_read_cleanup(Seader* seader) {
|
||||
|
||||
@@ -799,13 +799,13 @@ static void seader_hf_teardown_blocking(Seader* seader) {
|
||||
return;
|
||||
}
|
||||
|
||||
seader->hf_session_state = SeaderHfSessionStateTearingDown;
|
||||
if(!seader_worker_acquire(seader) || !seader->worker || !seader->uart) {
|
||||
FURI_LOG_W(TAG, "HF blocking teardown fallback");
|
||||
seader_hf_plugin_release(seader);
|
||||
return;
|
||||
}
|
||||
|
||||
seader->hf_session_state = SeaderHfSessionStateTearingDown;
|
||||
seader_worker_stop(seader->worker);
|
||||
FURI_LOG_I(TAG, "HF teardown blocking");
|
||||
seader_worker_start(
|
||||
@@ -820,12 +820,11 @@ static void seader_hf_teardown_blocking(Seader* seader) {
|
||||
void seader_hf_plugin_release(Seader* seader) {
|
||||
furi_assert(seader);
|
||||
|
||||
seader->hf_session_state = SeaderHfSessionStateTearingDown;
|
||||
|
||||
if(seader->plugin_hf && seader->hf_plugin_ctx) {
|
||||
seader->plugin_hf->stop(seader->hf_plugin_ctx);
|
||||
seader->plugin_hf->free(seader->hf_plugin_ctx);
|
||||
}
|
||||
seader->hf_plugin_ctx = NULL;
|
||||
seader->plugin_hf = NULL;
|
||||
|
||||
if(seader->poller) {
|
||||
FURI_LOG_I(TAG, "Stopping host NFC poller");
|
||||
@@ -841,12 +840,22 @@ void seader_hf_plugin_release(Seader* seader) {
|
||||
seader->picopass_poller = NULL;
|
||||
}
|
||||
|
||||
if(seader->plugin_hf && seader->hf_plugin_ctx) {
|
||||
seader->plugin_hf->free(seader->hf_plugin_ctx);
|
||||
}
|
||||
seader->hf_plugin_ctx = NULL;
|
||||
seader->plugin_hf = NULL;
|
||||
|
||||
if(seader->hf_plugin_manager) {
|
||||
FURI_LOG_I(TAG, "Unloading HF plugin");
|
||||
plugin_manager_free(seader->hf_plugin_manager);
|
||||
seader->hf_plugin_manager = NULL;
|
||||
}
|
||||
|
||||
if(seader->worker) {
|
||||
seader_worker_reset_poller_session(seader->worker);
|
||||
}
|
||||
|
||||
if(seader->mode_runtime == SeaderModeRuntimeHF) {
|
||||
seader->mode_runtime = SeaderModeRuntimeNone;
|
||||
}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@
|
||||
#include <Payload.h>
|
||||
#include <FrameProtocol.h>
|
||||
|
||||
#include "hf_interface_fal/interface.h"
|
||||
#include "wiegand_interface_fal/interface.h"
|
||||
#include "hf_interface_fal/hf_interface.h"
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include <flipper_application/plugins/plugin_manager.h>
|
||||
|
||||
+1
-4
@@ -24,9 +24,6 @@ static void seader_worker_release_hf_session(Seader* seader) {
|
||||
}
|
||||
|
||||
seader_hf_plugin_release(seader);
|
||||
if(seader->worker) {
|
||||
seader_worker_reset_poller_session(seader->worker);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -222,7 +219,7 @@ void seader_worker_start(
|
||||
seader_worker_stop(seader_worker);
|
||||
}
|
||||
|
||||
seader_worker->stage = SeaderPollerEventTypeCardDetect;
|
||||
seader_worker_reset_poller_session(seader_worker);
|
||||
seader_worker->callback = callback;
|
||||
seader_worker->context = context;
|
||||
seader_worker->uart = uart;
|
||||
|
||||
+26
-7
@@ -10,16 +10,35 @@ static size_t seader_uhf_append_family(
|
||||
bool* wrote_any,
|
||||
const char* name,
|
||||
bool key_present) {
|
||||
if(*wrote_any) {
|
||||
pos += (size_t)snprintf(out + pos, out_size - pos, "/");
|
||||
} else {
|
||||
pos += (size_t)snprintf(out + pos, out_size - pos, "UHF: ");
|
||||
*wrote_any = true;
|
||||
int written = 0;
|
||||
|
||||
if(pos >= out_size) {
|
||||
return out_size - 1U;
|
||||
}
|
||||
|
||||
if(*wrote_any) {
|
||||
written = snprintf(out + pos, out_size - pos, "/");
|
||||
} else {
|
||||
written = snprintf(out + pos, out_size - pos, "UHF: ");
|
||||
*wrote_any = true;
|
||||
}
|
||||
pos += (size_t)written;
|
||||
if(pos >= out_size) {
|
||||
return out_size - 1U;
|
||||
}
|
||||
|
||||
written = snprintf(out + pos, out_size - pos, "%s", name);
|
||||
pos += (size_t)written;
|
||||
if(pos >= out_size) {
|
||||
return out_size - 1U;
|
||||
}
|
||||
|
||||
pos += (size_t)snprintf(out + pos, out_size - pos, "%s", name);
|
||||
if(!key_present) {
|
||||
pos += (size_t)snprintf(out + pos, out_size - pos, " [no key]");
|
||||
written = snprintf(out + pos, out_size - pos, " [no key]");
|
||||
pos += (size_t)written;
|
||||
if(pos >= out_size) {
|
||||
return out_size - 1U;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Seader embedded Wiegand plugin sources
|
||||
|
||||
This directory is part of the main Seader repository.
|
||||
|
||||
It contains the embedded Wiegand `.fal` plugin sources used by Seader:
|
||||
- `wiegand.c`
|
||||
- `interface.h`
|
||||
|
||||
The Wiegand plugin source path in `application.fam` must point at `wiegand_interface_fal/wiegand.c`.
|
||||
Reference in New Issue
Block a user