HERMES Modem
Hermes ARQ/Broadcast modem
Loading...
Searching...
No Matches
arq_fsm.h
Go to the documentation of this file.
1/* HERMES Modem — ARQ FSM: state/event types and session structure
2 *
3 * Copyright (C) 2025 Rhizomatica
4 * Author: Rafael Diniz <rafael@riseup.net>
5 *
6 * SPDX-License-Identifier: GPL-3.0-or-later
7 */
8
9#ifndef ARQ_FSM_H_
10#define ARQ_FSM_H_
11
12#include <stdbool.h>
13#include <stddef.h>
14#include <stdint.h>
15
16#include "arq.h" /* CALLSIGN_MAX_SIZE, arq_action_t/type, arq_info */
17#include "arq_timing.h" /* arq_timing_ctx_t */
18
19/* ======================================================================
20 * Level 1 — Connection FSM states
21 * ====================================================================== */
22
23typedef enum
24{
25 ARQ_CONN_DISCONNECTED = 0, /* no session; idle */
26 ARQ_CONN_LISTENING = 1, /* waiting for incoming CALL frame */
27 ARQ_CONN_CALLING = 2, /* outgoing CALL sent; awaiting ACCEPT */
28 ARQ_CONN_ACCEPTING = 3, /* ACCEPT sent; awaiting first data/ACK */
29 ARQ_CONN_CONNECTED = 4, /* data-flow sub-FSM is active */
30 ARQ_CONN_DISCONNECTING = 5, /* DISCONNECT frame being exchanged */
33
34/* ======================================================================
35 * Level 2 — Data-flow sub-FSM states (active only in ARQ_CONN_CONNECTED)
36 * ====================================================================== */
37
38typedef enum
39{
40 ARQ_DFLOW_IDLE_ISS = 0, /* ISS: no pending frame; waiting for data */
41 ARQ_DFLOW_DATA_TX = 1, /* ISS: frame queued/transmitting */
42 ARQ_DFLOW_WAIT_ACK = 2, /* ISS: PTT-OFF; waiting for peer ACK */
43 ARQ_DFLOW_IDLE_IRS = 3, /* IRS: waiting for peer data frame */
44 ARQ_DFLOW_DATA_RX = 4, /* IRS: data frame decoded; ACK pending */
45 ARQ_DFLOW_ACK_TX = 5, /* IRS: ACK frame being transmitted */
46 ARQ_DFLOW_TURN_REQ_TX = 6, /* IRS→ISS: TURN_REQ being transmitted */
47 ARQ_DFLOW_TURN_REQ_WAIT = 7, /* IRS→ISS: waiting for TURN_ACK */
48 ARQ_DFLOW_TURN_ACK_TX = 8, /* ISS→IRS: TURN_ACK being transmitted */
49 ARQ_DFLOW_MODE_REQ_TX = 9, /* mode upgrade: MODE_REQ being transmitted */
50 ARQ_DFLOW_MODE_REQ_WAIT = 10, /* mode upgrade: waiting for MODE_ACK */
51 ARQ_DFLOW_MODE_ACK_TX = 11, /* mode upgrade: MODE_ACK being transmitted */
52 ARQ_DFLOW_KEEPALIVE_TX = 12, /* KEEPALIVE being transmitted */
53 ARQ_DFLOW_KEEPALIVE_WAIT = 13, /* waiting for KEEPALIVE_ACK */
56
57/* ======================================================================
58 * Caller/callee role (set at connect time, stays for session lifetime)
59 * ====================================================================== */
60
61typedef enum
62{
64 ARQ_ROLE_CALLER = 1, /* originated the call (starts as ISS) */
65 ARQ_ROLE_CALLEE = 2 /* received the call (starts as IRS) */
67
68/* ======================================================================
69 * Events
70 * ====================================================================== */
71
72typedef enum
73{
74 /* Application events (from TCP interface via channel bus) */
75 ARQ_EV_APP_LISTEN = 0, /* LISTEN ON received */
76 ARQ_EV_APP_STOP_LISTEN = 1, /* LISTEN OFF received */
77 ARQ_EV_APP_CONNECT = 2, /* CONNECT <dst> received */
78 ARQ_EV_APP_DISCONNECT = 3, /* DISCONNECT received */
79 ARQ_EV_APP_DATA_READY = 4, /* TX data available in buffer */
80
81 /* Radio RX events (from modem worker) */
82 ARQ_EV_RX_CALL = 5, /* CALL frame decoded */
83 ARQ_EV_RX_ACCEPT = 6, /* ACCEPT frame decoded */
84 ARQ_EV_RX_ACK = 7, /* ACK frame decoded */
85 ARQ_EV_RX_DATA = 8, /* DATA frame decoded */
86 ARQ_EV_RX_DISCONNECT = 9, /* DISCONNECT frame decoded */
87 ARQ_EV_RX_TURN_REQ = 10, /* TURN_REQ frame decoded */
88 ARQ_EV_RX_TURN_ACK = 11, /* TURN_ACK frame decoded */
89 ARQ_EV_RX_MODE_REQ = 12, /* MODE_REQ frame decoded */
90 ARQ_EV_RX_MODE_ACK = 13, /* MODE_ACK frame decoded */
91 ARQ_EV_RX_KEEPALIVE = 14, /* KEEPALIVE frame decoded */
92 ARQ_EV_RX_KEEPALIVE_ACK = 15, /* KEEPALIVE_ACK frame decoded */
93
94 /* Timer events */
95 ARQ_EV_TIMER_RETRY = 16, /* retry deadline expired */
96 ARQ_EV_TIMER_TIMEOUT = 17, /* session/call timeout expired */
97 ARQ_EV_TIMER_ACK = 18, /* ACK wait deadline expired */
98 ARQ_EV_TIMER_PEER_BACKLOG = 19, /* peer-backlog hold expired */
99 ARQ_EV_TIMER_KEEPALIVE = 20, /* keepalive interval expired */
100
101 /* Modem events */
102 ARQ_EV_TX_STARTED = 21, /* PTT ON (frame on air) */
103 ARQ_EV_TX_COMPLETE = 22, /* PTT OFF (TX finished) */
104
107
114typedef struct
115{
117
118 /* Frame-derived fields (set on RX events) */
119 uint8_t session_id;
120 uint8_t seq;
121 uint8_t ack_seq;
122 uint8_t rx_flags; /* ARQ_FLAG_TURN_REQ / HAS_DATA / HAS_SNR bits */
123 int8_t snr_encoded; /* as received from frame header */
124 uint16_t ack_delay_raw; /* as received (10ms units, 0=unknown) */
125
126 /* Mode negotiation */
127 int mode; /* requested/applied FreeDV mode */
128 size_t data_bytes; /* payload byte count (DATA frames) */
129
130 /* Received data payload — carried through event queue so the FSM can
131 * validate sequence numbers before delivering to the application. */
132 uint8_t payload[512];
134
135 /* Local receive SNR at the time the frame was decoded (dB, 0 = unknown).
136 * Carried in-band so the FSM can update local_snr_x10 without relying on
137 * the cross-thread arq_update_link_metrics() write, which races with the
138 * event queue push in modem.c. */
139 float rx_snr;
140
141 /* Call setup */
142 char remote_call[CALLSIGN_MAX_SIZE];
144
145/* ======================================================================
146 * Session structure — replaces arq_ctx_t
147 *
148 * All state is in this struct; no hidden global flags.
149 * Monotonic timestamps are uint64_t milliseconds from hermes_log startup.
150 * ====================================================================== */
151
152typedef struct
153{
154 /* --- State machine fields --- */
155 arq_conn_state_t conn_state; /* Level 1 connection state */
156 arq_dflow_state_t dflow_state; /* Level 2 data-flow state */
157 arq_role_t role; /* CALLER or CALLEE */
158
159 /* --- Identifiers --- */
160 uint8_t session_id; /* random byte chosen by caller */
161 char remote_call[CALLSIGN_MAX_SIZE];
162
163 /* --- Sequence numbers --- */
164 uint8_t tx_seq; /* next seq we will send */
165 uint8_t rx_expected; /* next seq we expect from peer */
166
167 /* --- Mode / speed --- */
168 int payload_mode; /* MY data TX mode (ISS); per-direction,
169 * independent of peer's TX mode */
170 int control_mode; /* always FREEDV_MODE_DATAC13 */
171 int initial_payload_mode; /* startup payload mode (= broadcast RX
172 * mode); restored on disconnect so the
173 * payload decoder matches broadcast */
174 int speed_level; /* reliability ladder: 0=DATAC4,
175 * 1=DATAC3, 2=DATAC1 */
176 int tx_success_count; /* consecutive clean ACKs (no retry)
177 * towards ladder step-up */
178 int mode_upgrade_count; /* SNR hysteresis counter for upgrade */
179 int pending_tx_mode; /* mode requested in MODE_REQ (retry) */
180 int peer_tx_mode; /* mode peer last TX'd in = my payload
181 * RX decoder mode when IRS; updated
182 * from ev->mode on every DATA frame */
183
184 /* --- Retry/timeout bookkeeping --- */
185 int tx_retries_left; /* retries remaining for current frame */
186 uint64_t state_enter_ms; /* when current conn_state was entered */
187 uint64_t startup_deadline_ms; /* end of DATAC13-only startup period */
188
189 /* --- Peer state observed from frames --- */
190 bool peer_has_data; /* peer's HAS_DATA flag in last frame */
191 bool acktx_had_has_data; /* HAS_DATA was set in the last ACK sent */
192 int peer_snr_x10; /* peer-reported SNR * 10 (integer) */
193 int local_snr_x10; /* local SNR EMA * 10 */
194 uint64_t peer_busy_until_ms; /* remote TX busy guard expiry */
195
196 /* --- Data bookkeeping --- */
197 int tx_backlog_bytes; /* bytes pending in TX buffer */
198
199 /* --- Teardown flags --- */
200 bool disconnect_to_no_client; /* after disconnect: clear arq_info */
201 bool pending_disconnect_notify;/* defer notify_disconnected until TX done */
202 bool pending_disconnect; /* APP_DISCONNECT deferred until TX buf empty */
203
204 /* --- Initial connect guard --- */
205 bool pending_connect_confirm; /* caller must ACK ACCEPT when no initial
206 * DATA is queued, otherwise callee stays
207 * in ACCEPTING waiting for first traffic */
208 bool need_initial_guard; /* ISS must apply channel guard before
209 * first DATA after connect (prevents
210 * transmitting before IRS resets its
211 * decoders from TX→RX) */
212
213 /* --- Delivery-feedback safety net --- */
214 int consecutive_retries; /* consecutive non-clean TX outcomes */
215 uint64_t mode_hold_until_ms; /* after forced downgrade: don't upgrade
216 * until this uptime (prevents oscillation
217 * when stale SNR says "upgrade" but the
218 * channel can't support it) */
219
220 /* --- Retransmit buffer --- */
221 uint8_t tx_retransmit_buf[1024]; /* last-sent data frame bytes; must be
222 * >= max frame: 8 hdr + 502 DATAC1
223 * payload = 510 bytes (was 256, too
224 * small → DATAC1 retries consumed fresh
225 * ring bytes, corrupting byte stream) */
226 int tx_retransmit_len; /* 0 = no saved frame */
227 uint8_t tx_retransmit_seq; /* tx_seq the saved frame belongs to */
228 int tx_inflight_bytes; /* payload bytes in unACKed frame */
229
230 /* --- Keepalive tracking --- */
232 uint64_t last_rx_ms; /* last successful frame decode time */
233
234 /* --- Timer mechanism --- */
235 uint64_t deadline_ms; /* absolute monotonic deadline */
236 arq_event_id_t deadline_event; /* event to fire when deadline fires */
238
239/* ======================================================================
240 * FSM action callbacks
241 *
242 * Registered once at startup via arq_fsm_set_callbacks().
243 * All callbacks are called from the ARQ event-loop thread.
244 * ====================================================================== */
245
246typedef struct
247{
249 void (*send_tx_frame)(int packet_type, int mode,
250 size_t frame_size, const uint8_t *frame);
251
253 void (*notify_connected)(const char *remote_call);
254
256 void (*notify_pending)(const char *remote_call);
257
259 void (*notify_cancelpending)(void);
260
263 void (*notify_disconnected)(bool to_no_client);
264
266 void (*deliver_rx_data)(const uint8_t *data, size_t len);
267
269 int (*tx_backlog)(void);
270
272 int (*tx_read)(uint8_t *buf, size_t len);
273
275 void (*send_buffer_status)(int backlog_bytes);
277
282
288
289/* ======================================================================
290 * FSM public API (implemented in arq_fsm.c)
291 * ====================================================================== */
292
297void arq_fsm_init(arq_session_t *sess);
298
308void arq_fsm_dispatch(arq_session_t *sess, const arq_event_t *event);
309
319int arq_fsm_timeout_ms(const arq_session_t *sess, uint64_t now);
320
325
330
334const char *arq_event_name(arq_event_id_t ev);
335
336#endif /* ARQ_FSM_H_ */
#define CALLSIGN_MAX_SIZE
Definition arq.h:24
arq_event_id_t
Definition arq_fsm.h:73
@ ARQ_EV_TX_STARTED
Definition arq_fsm.h:102
@ ARQ_EV_RX_MODE_ACK
Definition arq_fsm.h:90
@ ARQ_EV_TIMER_ACK
Definition arq_fsm.h:97
@ ARQ_EV_TX_COMPLETE
Definition arq_fsm.h:103
@ ARQ_EV_RX_TURN_REQ
Definition arq_fsm.h:87
@ ARQ_EV_TIMER_RETRY
Definition arq_fsm.h:95
@ ARQ_EV_APP_DATA_READY
Definition arq_fsm.h:79
@ ARQ_EV_RX_ACCEPT
Definition arq_fsm.h:83
@ ARQ_EV_RX_KEEPALIVE_ACK
Definition arq_fsm.h:92
@ ARQ_EV_APP_DISCONNECT
Definition arq_fsm.h:78
@ ARQ_EV_RX_MODE_REQ
Definition arq_fsm.h:89
@ ARQ_EV_TIMER_PEER_BACKLOG
Definition arq_fsm.h:98
@ ARQ_EV_RX_CALL
Definition arq_fsm.h:82
@ ARQ_EV_RX_DISCONNECT
Definition arq_fsm.h:86
@ ARQ_EV_TIMER_KEEPALIVE
Definition arq_fsm.h:99
@ ARQ_EV_APP_STOP_LISTEN
Definition arq_fsm.h:76
@ ARQ_EV_TIMER_TIMEOUT
Definition arq_fsm.h:96
@ ARQ_EV_RX_KEEPALIVE
Definition arq_fsm.h:91
@ ARQ_EV_APP_CONNECT
Definition arq_fsm.h:77
@ ARQ_EV_RX_DATA
Definition arq_fsm.h:85
@ ARQ_EV_RX_TURN_ACK
Definition arq_fsm.h:88
@ ARQ_EV__COUNT
Definition arq_fsm.h:105
@ ARQ_EV_APP_LISTEN
Definition arq_fsm.h:75
@ ARQ_EV_RX_ACK
Definition arq_fsm.h:84
void arq_fsm_init(arq_session_t *sess)
Initialise a session structure to DISCONNECTED state.
Definition arq_fsm.c:118
arq_dflow_state_t
Definition arq_fsm.h:39
@ ARQ_DFLOW_TURN_REQ_WAIT
Definition arq_fsm.h:47
@ ARQ_DFLOW_MODE_REQ_WAIT
Definition arq_fsm.h:50
@ ARQ_DFLOW_TURN_REQ_TX
Definition arq_fsm.h:46
@ ARQ_DFLOW_MODE_ACK_TX
Definition arq_fsm.h:51
@ ARQ_DFLOW_DATA_TX
Definition arq_fsm.h:41
@ ARQ_DFLOW__COUNT
Definition arq_fsm.h:54
@ ARQ_DFLOW_KEEPALIVE_TX
Definition arq_fsm.h:52
@ ARQ_DFLOW_ACK_TX
Definition arq_fsm.h:45
@ ARQ_DFLOW_TURN_ACK_TX
Definition arq_fsm.h:48
@ ARQ_DFLOW_KEEPALIVE_WAIT
Definition arq_fsm.h:53
@ ARQ_DFLOW_IDLE_IRS
Definition arq_fsm.h:43
@ ARQ_DFLOW_DATA_RX
Definition arq_fsm.h:44
@ ARQ_DFLOW_WAIT_ACK
Definition arq_fsm.h:42
@ ARQ_DFLOW_MODE_REQ_TX
Definition arq_fsm.h:49
@ ARQ_DFLOW_IDLE_ISS
Definition arq_fsm.h:40
const char * arq_conn_state_name(arq_conn_state_t s)
Human-readable name for a connection state (for log output).
Definition arq_fsm.c:30
void arq_fsm_set_callbacks(const arq_fsm_callbacks_t *cbs)
Register FSM action callbacks (call once before first dispatch).
Definition arq_fsm.c:104
const char * arq_dflow_state_name(arq_dflow_state_t s)
Human-readable name for a data-flow state (for log output).
Definition arq_fsm.c:44
arq_conn_state_t
Definition arq_fsm.h:24
@ ARQ_CONN_DISCONNECTED
Definition arq_fsm.h:25
@ ARQ_CONN_LISTENING
Definition arq_fsm.h:26
@ ARQ_CONN_CONNECTED
Definition arq_fsm.h:29
@ ARQ_CONN_ACCEPTING
Definition arq_fsm.h:28
@ ARQ_CONN_CALLING
Definition arq_fsm.h:27
@ ARQ_CONN__COUNT
Definition arq_fsm.h:31
@ ARQ_CONN_DISCONNECTING
Definition arq_fsm.h:30
int arq_fsm_timeout_ms(const arq_session_t *sess, uint64_t now)
Return milliseconds until the next deadline, or INT_MAX if idle.
Definition arq_fsm.c:134
arq_role_t
Definition arq_fsm.h:62
@ ARQ_ROLE_CALLEE
Definition arq_fsm.h:65
@ ARQ_ROLE_CALLER
Definition arq_fsm.h:64
@ ARQ_ROLE_NONE
Definition arq_fsm.h:63
void arq_fsm_dispatch(arq_session_t *sess, const arq_event_t *event)
Dispatch an event through both FSM levels.
Definition arq_fsm.c:1730
const char * arq_event_name(arq_event_id_t ev)
Human-readable name for an event (for log output).
Definition arq_fsm.c:66
void arq_fsm_set_timing(arq_timing_ctx_t *timing)
Register timing context for recording (call once at init).
Definition arq_fsm.c:109
ARQ event with all possible payload fields.
Definition arq_fsm.h:115
size_t payload_len
Definition arq_fsm.h:133
size_t data_bytes
Definition arq_fsm.h:128
uint8_t session_id
Definition arq_fsm.h:119
uint8_t ack_seq
Definition arq_fsm.h:121
int mode
Definition arq_fsm.h:127
uint16_t ack_delay_raw
Definition arq_fsm.h:124
uint8_t rx_flags
Definition arq_fsm.h:122
float rx_snr
Definition arq_fsm.h:139
int8_t snr_encoded
Definition arq_fsm.h:123
arq_event_id_t id
Definition arq_fsm.h:116
uint8_t seq
Definition arq_fsm.h:120
Definition arq_fsm.h:247
Definition arq_fsm.h:153
bool peer_has_data
Definition arq_fsm.h:190
uint64_t mode_hold_until_ms
Definition arq_fsm.h:215
int tx_retransmit_len
Definition arq_fsm.h:226
bool acktx_had_has_data
Definition arq_fsm.h:191
int keepalive_miss_count
Definition arq_fsm.h:231
uint8_t session_id
Definition arq_fsm.h:160
bool pending_disconnect_notify
Definition arq_fsm.h:201
uint64_t last_rx_ms
Definition arq_fsm.h:232
uint8_t tx_retransmit_seq
Definition arq_fsm.h:227
bool disconnect_to_no_client
Definition arq_fsm.h:200
int mode_upgrade_count
Definition arq_fsm.h:178
uint64_t peer_busy_until_ms
Definition arq_fsm.h:194
int tx_retries_left
Definition arq_fsm.h:185
int local_snr_x10
Definition arq_fsm.h:193
bool need_initial_guard
Definition arq_fsm.h:208
bool pending_connect_confirm
Definition arq_fsm.h:205
int consecutive_retries
Definition arq_fsm.h:214
int tx_backlog_bytes
Definition arq_fsm.h:197
int speed_level
Definition arq_fsm.h:174
uint64_t state_enter_ms
Definition arq_fsm.h:186
arq_dflow_state_t dflow_state
Definition arq_fsm.h:156
arq_role_t role
Definition arq_fsm.h:157
uint8_t tx_seq
Definition arq_fsm.h:164
int peer_tx_mode
Definition arq_fsm.h:180
int tx_success_count
Definition arq_fsm.h:176
int payload_mode
Definition arq_fsm.h:168
int initial_payload_mode
Definition arq_fsm.h:171
arq_event_id_t deadline_event
Definition arq_fsm.h:236
int pending_tx_mode
Definition arq_fsm.h:179
arq_conn_state_t conn_state
Definition arq_fsm.h:155
int control_mode
Definition arq_fsm.h:170
bool pending_disconnect
Definition arq_fsm.h:202
uint64_t deadline_ms
Definition arq_fsm.h:235
int tx_inflight_bytes
Definition arq_fsm.h:228
uint8_t rx_expected
Definition arq_fsm.h:165
int peer_snr_x10
Definition arq_fsm.h:192
uint64_t startup_deadline_ms
Definition arq_fsm.h:187
Definition arq_timing.h:24