ether2ser 0.1.0
Ethernet <-> synchronous V.24 bridge firmware for RP2040 + W5500
Loading...
Searching...
No Matches
hdlc_sync.c
Go to the documentation of this file.
1/*
2 * ether2ser - Ethernet <-> synchronous V.24 (RS-232/V.28) bridge
3 *
4 * File: src/protocol/hdlc_sync.c
5 * Purpose: HDLC sync accumulator implementation.
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 *
9 * Copyright (c) 2026 Florian <f.leuze@outlook.de>
10 */
11
12// Related headers
13#include "hdlc_sync.h"
14
15// Standard library headers
16#include <stdbool.h>
17#include <stdint.h>
18#include <string.h>
19
20// Project Headers
21#include "hdlc_common.h"
22#include "system/error.h"
23
24// Generated headers
25
26#define HDLC_BITS_PER_BYTE 8U
27#define HDLC_SYNC_MIN_FRAME_SIZE_BYTES 4U
28#define HDLC_SYNC_MAX_REASONABLE_FRAME_SIZE_BYTES 2048U
29#define HDLC_SYNC_SHORT_BUFFER_SEARCH_LIMIT 64U
30
31/*
32 * Module model:
33 * - Input is a continuous raw-byte stream from PIO where HDLC flag alignment is unknown.
34 * - We detect an opening flag (possibly bit-shifted), then keep consuming aligned bytes
35 * until we hit a closing flag.
36 * - We return FRAME_READY with the still-encoded frame (flags + stuffed bytes + FCS).
37 * - Caller decides accept/reject after decode/CRC and then calls consume_candidate().
38 *
39 * State machine (poll side):
40 *
41 * HUNTING
42 * | found opening flag + bit phase
43 * v
44 * SYNCING -- consume first aligned byte --> SYNCED
45 * ^ |
46 * |<----------- closing flag -------------- |
47 *
48 * From SYNCING/SYNCED:
49 * - if more raw data needed: return E2S_OK and resume next poll() from `processed`
50 * - if candidate ready: return E2S_ERR_HDLC_ACC_FRAME_READY and wait for consume_candidate()
51 *
52 * Ownership split:
53 * - poll() discovers candidates and exposes them.
54 * - consume_candidate() advances raw buffer based on decode accept/reject decision.
55 */
56
57typedef enum
58{
59 // Consumed at least one aligned byte and can continue in the same poll call.
61 // Need more raw data before we can continue.
63 // Found full candidate bounded by closing flag.
65 // Fatal for this poll call (e.g. destination frame buffer too small).
68
69// Drop oldest raw bytes while keeping relative order of the remainder.
70static void hdlc_sync_drop_prefix(HDLC_SYNC_ACCUMULATOR_T* accumulator, size_t drop_count)
71{
72 if (!accumulator || drop_count == 0)
73 {
74 return;
75 }
76
77 if (drop_count >= accumulator->position)
78 {
79 // Everything is consumed; cursor must be reset as well.
80 accumulator->position = 0;
81 accumulator->processed = 0;
82 return;
83 }
84
85 size_t remaining = accumulator->position - drop_count;
86 memmove(accumulator->buffer, accumulator->buffer + drop_count, remaining);
87 accumulator->position = remaining;
88}
89
91{
92 if (!accumulator)
93 {
94 return;
95 }
96
97 // Reset to "no active candidate" baseline.
98 accumulator->processed = 0;
99 accumulator->candidate_start = 0;
100 accumulator->candidate_end = 0;
101 accumulator->candidate_valid = false;
102 accumulator->state = HDLC_SYNC_STATE_HUNTING;
103 accumulator->bit_offset = 0;
104 accumulator->align_shift_right = false;
105}
106
107// False-lock recovery when candidate grows implausibly large.
109 HDLC_FRAME_T* out_frame, size_t* scan_index)
110{
111 if (!accumulator || !out_frame || !scan_index)
112 {
113 return;
114 }
115
116 // Candidate grew far beyond expected frame sizes; treat it as false lock and
117 // retry from the next raw byte so alternate alignments can be tested.
118 size_t drop = accumulator->candidate_start + 1;
119 if (drop > accumulator->position)
120 {
121 drop = accumulator->position;
122 }
123
124 hdlc_sync_drop_prefix(accumulator, drop);
126 out_frame->length = 0;
127 *scan_index = 0;
128}
129
130/*
131 * Reconstruct one aligned byte from raw stream at the selected bit phase.
132 *
133 * shift_right = true -> byte is composed as raw[i] >> off | raw[i+1] << (8-off)
134 * shift_right = false -> byte is composed as raw[i] << off | raw[i+1] >> (8-off)
135 */
136static bool hdlc_sync_get_aligned_byte(const HDLC_SYNC_ACCUMULATOR_T* accumulator, size_t raw_index,
137 uint8_t bit_offset, bool shift_right, uint8_t* out_byte)
138{
139 if (!accumulator || !out_byte || raw_index >= accumulator->position ||
140 bit_offset >= HDLC_BITS_PER_BYTE)
141 {
142 return false;
143 }
144
145 if (bit_offset == 0)
146 {
147 *out_byte = accumulator->buffer[raw_index];
148 return true;
149 }
150
151 if ((raw_index + 1) >= accumulator->position)
152 {
153 return false;
154 }
155
156 if (shift_right)
157 {
158 *out_byte = (uint8_t)((uint8_t)(accumulator->buffer[raw_index] >> bit_offset) |
159 (uint8_t)(accumulator->buffer[raw_index + 1] <<
160 (HDLC_BITS_PER_BYTE - bit_offset)));
161 }
162 else
163 {
164 *out_byte = (uint8_t)((uint8_t)(accumulator->buffer[raw_index] << bit_offset) |
165 (uint8_t)(accumulator->buffer[raw_index + 1] >>
166 (HDLC_BITS_PER_BYTE - bit_offset)));
167 }
168 return true;
169}
170
171/*
172 * Fast opening-flag hunt:
173 * - byte-aligned first
174 * - then right-shift phases (most common in our traces)
175 * - optional left-shift fallback if caller enables it
176 */
178 size_t scan_index, bool allow_left_shift,
179 size_t* out_start_index, uint8_t* out_bit_pos,
180 bool* out_shift_right)
181{
182 if (!accumulator || !out_start_index || !out_bit_pos || !out_shift_right ||
183 accumulator->position < 2)
184 {
185 return false;
186 }
187
188 // Start one raw byte earlier so a boundary-spanning flag is not missed.
189 size_t start = (scan_index > 0) ? (scan_index - 1) : 0;
190 if (start >= (accumulator->position - 1))
191 {
192 return false;
193 }
194
195 uint8_t aligned = 0;
196 for (; start < (accumulator->position - 1); ++start)
197 {
198 // Byte-aligned first.
199 if (hdlc_sync_get_aligned_byte(accumulator, start, 0, false, &aligned) &&
200 aligned == accumulator->sync_byte)
201 {
202 *out_start_index = start;
203 *out_bit_pos = 0;
204 *out_shift_right = false;
205 return true;
206 }
207
208 // Prefer right-shift alignment for non-zero offsets.
209 for (uint8_t bit_pos = 1; bit_pos < HDLC_BITS_PER_BYTE; ++bit_pos)
210 {
211 if (hdlc_sync_get_aligned_byte(accumulator, start, bit_pos, true, &aligned) &&
212 aligned == accumulator->sync_byte)
213 {
214 *out_start_index = start;
215 *out_bit_pos = bit_pos;
216 *out_shift_right = true;
217 return true;
218 }
219 }
220
221 if (allow_left_shift)
222 {
223 for (uint8_t bit_pos = 1; bit_pos < HDLC_BITS_PER_BYTE; ++bit_pos)
224 {
225 if (hdlc_sync_get_aligned_byte(accumulator, start, bit_pos, false, &aligned) &&
226 aligned == accumulator->sync_byte)
227 {
228 *out_start_index = start;
229 *out_bit_pos = bit_pos;
230 *out_shift_right = false;
231 return true;
232 }
233 }
234 }
235 }
236
237 return false;
238}
239
240/*
241 * Exhaustive search used only for short buffers:
242 * for each possible alignment, find complete flag...flag candidate and keep the longest.
243 *
244 * Why longest:
245 * short/noisy buffers can contain multiple false openings; longest span tends to represent
246 * the real frame candidate when close/overlapping flag signatures appear.
247 */
249 size_t scan_index, size_t* out_start_index,
250 uint8_t* out_bit_pos, bool* out_shift_right)
251{
252 if (!accumulator || !out_start_index || !out_bit_pos || !out_shift_right ||
255 {
256 // For long buffers we intentionally skip this expensive exhaustive path.
257 return false;
258 }
259
260 // Start one byte earlier to keep boundary-spanning openings visible.
261 size_t start = (scan_index > 0) ? (scan_index - 1) : 0;
262 if (start >= (accumulator->position - 1))
263 {
264 return false;
265 }
266
267 bool found_best = false;
268 size_t best_start = 0;
269 uint8_t best_bit_pos = 0;
270 bool best_shift = false;
271 size_t best_frame_size = 0;
272 uint8_t start_byte = 0;
273 uint8_t probe_byte = 0;
274
275 for (; start < (accumulator->position - 1); ++start)
276 {
277 // Try all bit phases for this raw start index.
278 for (uint8_t bit_pos = 0; bit_pos < HDLC_BITS_PER_BYTE; ++bit_pos)
279 {
280 // mode=0 -> left-shift composition, mode=1 -> right-shift composition.
281 for (uint8_t mode = 0; mode < 2; ++mode)
282 {
283 bool shift_right = (mode != 0);
284 if (bit_pos == 0 && shift_right)
285 {
286 // offset=0 has no direction; avoid duplicate test.
287 continue;
288 }
289
290 if (!hdlc_sync_get_aligned_byte(accumulator, start, bit_pos, shift_right,
291 &start_byte) ||
292 start_byte != accumulator->sync_byte)
293 {
294 // This alignment does not start with a flag; skip quickly.
295 continue;
296 }
297
298 // We have a plausible opening flag: probe forward for matching closing flag.
299 size_t frame_size = 1;
300 for (size_t probe = start + 1; probe < accumulator->position; ++probe)
301 {
302 if (!hdlc_sync_get_aligned_byte(accumulator, probe, bit_pos, shift_right,
303 &probe_byte))
304 {
305 // Need one more raw byte for this alignment before continuing probe.
306 break;
307 }
308 frame_size++;
309 if (probe_byte == accumulator->sync_byte &&
310 frame_size >= HDLC_SYNC_MIN_FRAME_SIZE_BYTES)
311 {
312 // Require at least: opening flag + 2-byte FCS + closing flag.
313 if (!found_best || frame_size > best_frame_size)
314 {
315 // Keep longest complete candidate found in this short window.
316 found_best = true;
317 best_start = start;
318 best_bit_pos = bit_pos;
319 best_shift = shift_right;
320 best_frame_size = frame_size;
321 }
322 // This opening is already closed; no need to probe further.
323 break;
324 }
325 }
326 }
327 }
328 }
329
330 if (!found_best)
331 {
332 return false;
333 }
334
335 *out_start_index = best_start;
336 *out_bit_pos = best_bit_pos;
337 *out_shift_right = best_shift;
338 // Caller enters SYNCING with this chosen opening+alignment.
339 return true;
340}
341
342// HUNTING -> SYNCING transition (select opening flag + alignment).
344 HDLC_FRAME_T* out_frame,
345 size_t* scan_index)
346{
347 size_t start_index = 0;
348 uint8_t found_bit_pos = 0;
349 bool found_shift_dir = false;
350
352 accumulator, *scan_index, &start_index, &found_bit_pos, &found_shift_dir);
353 if (!found)
354 {
355 // For longer buffers, fast opening search is cheaper than exhaustive pairing.
356 found = hdlc_sync_find_opening_candidate(accumulator, *scan_index, false, &start_index,
357 &found_bit_pos, &found_shift_dir);
358 }
359
360 if (!found)
361 {
362 // Nothing found in current buffer window.
363 *scan_index = accumulator->position;
365 }
366
367 accumulator->state = HDLC_SYNC_STATE_SYNCING;
368 accumulator->bit_offset = found_bit_pos;
369 accumulator->align_shift_right = found_shift_dir;
370 accumulator->candidate_start = start_index;
371 accumulator->candidate_valid = false;
372 out_frame->length = 1;
373 out_frame->payload[0] = accumulator->sync_byte;
374 *scan_index = start_index + 1;
376}
377
378// Consume the first aligned byte after opening flag.
380 HDLC_FRAME_T* out_frame,
381 size_t* scan_index,
382 e2s_error_t* out_error)
383{
385 {
386 // Guardrail against false lock on idle/noise runs.
387 hdlc_sync_reject_oversized_candidate(accumulator, out_frame, scan_index);
389 }
390
391 uint8_t aligned = 0;
392 if (!hdlc_sync_get_aligned_byte(accumulator, *scan_index, accumulator->bit_offset,
393 accumulator->align_shift_right, &aligned))
394 {
395 if (accumulator->bit_offset != 0)
396 {
397 // Non-zero offset needs raw[i+1] lookahead; count those wait events.
398 accumulator->lookahead_wait_syncing++;
399 }
401 }
402
403 if (out_frame->length >= out_frame->capacity)
404 {
405 // Destination frame buffer is owned by caller; fail hard to avoid overwrite.
406 out_frame->length = 0;
407 accumulator->state = HDLC_SYNC_STATE_HUNTING;
408 accumulator->bit_offset = 0;
411 }
412
413 out_frame->payload[out_frame->length++] = aligned;
414 accumulator->state = HDLC_SYNC_STATE_SYNCED;
415 (*scan_index)++;
417}
418
419// Consume aligned bytes until closing flag; then report candidate ready.
421 HDLC_FRAME_T* out_frame,
422 size_t* scan_index,
423 e2s_error_t* out_error)
424{
426 {
427 // Guardrail against false lock on idle/noise runs.
428 hdlc_sync_reject_oversized_candidate(accumulator, out_frame, scan_index);
430 }
431
432 uint8_t aligned = 0;
433 if (!hdlc_sync_get_aligned_byte(accumulator, *scan_index, accumulator->bit_offset,
434 accumulator->align_shift_right, &aligned))
435 {
436 if (accumulator->bit_offset != 0)
437 {
438 // Non-zero offset needs raw[i+1] lookahead; count those wait events.
439 accumulator->lookahead_wait_synced++;
440 }
442 }
443
444 if (out_frame->length >= out_frame->capacity)
445 {
446 // Destination frame buffer is owned by caller; fail hard to avoid overwrite.
447 out_frame->length = 0;
448 accumulator->state = HDLC_SYNC_STATE_HUNTING;
449 accumulator->bit_offset = 0;
452 }
453
454 out_frame->payload[out_frame->length++] = aligned;
455 if (aligned != accumulator->sync_byte)
456 {
457 // Still inside payload/FCS.
458 (*scan_index)++;
460 }
461
462 // Closing flag reached: candidate is complete in raw buffer.
463 accumulator->frame_ready_count++;
464 accumulator->state = HDLC_SYNC_STATE_HUNTING;
465 accumulator->candidate_end = *scan_index + 1;
466 accumulator->candidate_valid = true;
467
468 // For diagnostics/tests we report the equivalent left-shift bit offset.
469 if (accumulator->align_shift_right && accumulator->bit_offset > 0)
470 {
471 accumulator->bit_offset = (uint8_t)(HDLC_BITS_PER_BYTE - accumulator->bit_offset);
472 }
473
474 (*scan_index)++;
476}
477
478/*
479 * Finalize poll() when no frame was produced:
480 * - in HUNTING: drop scanned prefix but keep 1-byte overlap so split flags survive
481 * - in SYNCING/SYNCED: keep buffered candidate and processed cursor for continuation
482 */
483static void hdlc_sync_finalize_poll_no_frame(HDLC_SYNC_ACCUMULATOR_T* accumulator, size_t scan_index)
484{
485 if (!accumulator)
486 {
487 return;
488 }
489
490 accumulator->processed = scan_index;
491 if (accumulator->state != HDLC_SYNC_STATE_HUNTING)
492 {
493 return;
494 }
495
496 // Keep one-byte overlap so a flag that starts at the previous last byte can
497 // still be matched once the next byte arrives.
498 size_t drop = (accumulator->processed > 0) ? (accumulator->processed - 1) : 0;
499 if (drop > accumulator->position)
500 {
501 drop = accumulator->position;
502 }
503 hdlc_sync_drop_prefix(accumulator, drop);
504 accumulator->processed = 0;
505}
506
507void hdlc_sync_acc_init(HDLC_SYNC_ACCUMULATOR_T* accumulator, uint8_t sync_byte)
508{
509 // Full explicit init is intentional: structure may be stack-allocated.
510 accumulator->position = 0;
511 accumulator->processed = 0;
512 accumulator->candidate_start = 0;
513 accumulator->candidate_end = 0;
514 accumulator->candidate_valid = false;
515 accumulator->bit_offset = 0;
516 accumulator->align_shift_right = false;
517 accumulator->state = HDLC_SYNC_STATE_HUNTING;
518 accumulator->sync_byte = sync_byte;
519 accumulator->lookahead_wait_syncing = 0;
520 accumulator->lookahead_wait_synced = 0;
521 accumulator->frame_ready_count = 0;
522 accumulator->consume_count = 0;
523 accumulator->hardcap_drop_events = 0;
524 accumulator->hardcap_drop_bytes = 0;
525}
526
528{
529 if (!accumulator)
530 {
531 return false;
532 }
533 if (accumulator->position >= RX_HDLC_SYNC_MAX_BUFFER_SIZE)
534 {
535 // Backpressure is handled by caller; here we simply refuse new byte.
536 return false;
537 }
538 accumulator->buffer[accumulator->position] = byte;
539 accumulator->position++;
540 return true;
541}
542
543/*
544 * Poll contract:
545 * - returns E2S_OK when no complete candidate available yet
546 * - returns E2S_ERR_HDLC_ACC_FRAME_READY when out_frame holds one candidate
547 * - caller must then call hdlc_sync_acc_consume_candidate(accept)
548 */
550{
551 if (!accumulator || !out_frame || !out_frame->payload || out_frame->capacity == 0)
552 {
553 return E2S_OK;
554 }
555 if (accumulator->position == 0)
556 {
557 return E2S_OK;
558 }
559
560 // Only needed when aligned byte assembly needs lookahead.
561 if (accumulator->state != HDLC_SYNC_STATE_HUNTING && accumulator->bit_offset != 0 &&
562 accumulator->position < 2)
563 {
564 // Need one more raw byte for cross-byte reconstruction.
565 return E2S_OK;
566 }
567
568 // `scan_index` is the raw cursor local to this poll cycle.
569 size_t scan_index = accumulator->processed;
570 while (scan_index < accumulator->position)
571 {
573 e2s_error_t error_code = E2S_OK;
574
575 switch (accumulator->state)
576 {
578 // Select candidate opening + lock alignment.
579 step_result = hdlc_sync_step_hunting(accumulator, out_frame, &scan_index);
580 break;
582 // Consume first data byte after opening flag.
583 step_result =
584 hdlc_sync_step_syncing(accumulator, out_frame, &scan_index, &error_code);
585 break;
587 // Continue until closing flag.
588 step_result = hdlc_sync_step_synced(accumulator, out_frame, &scan_index, &error_code);
589 break;
590 default:
591 return E2S_OK;
592 }
593
594 if (step_result == HDLC_SYNC_POLL_STEP_PROGRESS)
595 {
596 continue;
597 }
598
599 if (step_result == HDLC_SYNC_POLL_STEP_FRAME_READY)
600 {
601 // Candidate lifecycle switches to caller; next poll starts fresh.
602 accumulator->processed = 0;
604 }
605
606 if (step_result == HDLC_SYNC_POLL_STEP_ERROR)
607 {
608 return error_code;
609 }
610
611 // STOP: need more raw bytes or no useful work left in this poll cycle.
612 break;
613 }
614
615 hdlc_sync_finalize_poll_no_frame(accumulator, scan_index);
616
617 // In SYNCING/SYNCED we preserve the buffered candidate region and keep `processed`
618 // as resume cursor. This allows trying alternate openings on candidate reject.
619 return E2S_OK;
620}
621
623{
624 if (!accumulator || !accumulator->candidate_valid)
625 {
626 return;
627 }
628
629 size_t drop = 0;
630 if (accept)
631 {
632 // Accepted frame: drop full [opening..closing] candidate region.
633 drop = accumulator->candidate_end;
634 }
635 else
636 {
637 // Reject strategy: advance only one raw byte from opening.
638 // This preserves overlapping candidates at other bit phases.
639 drop = accumulator->candidate_start + 1;
640 }
641 if (drop > accumulator->position)
642 {
643 drop = accumulator->position;
644 }
645 if (drop > 0)
646 {
647 accumulator->consume_count++;
648 hdlc_sync_drop_prefix(accumulator, drop);
649 }
651
652 // Hard cap: if buffer is near full, drop oldest bytes to keep bounded.
653 if (accumulator->position >= (RX_HDLC_SYNC_MAX_BUFFER_SIZE - 16))
654 {
655 size_t keep = 16;
656 drop = accumulator->position - keep;
657 accumulator->hardcap_drop_events++;
658 accumulator->hardcap_drop_bytes += (uint32_t)drop;
659 // Keep a small tail so boundary-spanning flags still have a chance.
660 hdlc_sync_drop_prefix(accumulator, drop);
661 accumulator->processed = 0;
662 }
663}
e2s_error_t
Common error codes returned by ether2ser modules.
Definition error.h:27
@ E2S_ERR_HDLC_ACC_FRAME_READY
Definition error.h:67
@ E2S_OK
Definition error.h:28
@ E2S_ERR_HDLC_DECODE_PAYLOAD_TOO_LONG
Definition error.h:62
void hdlc_sync_acc_consume_candidate(HDLC_SYNC_ACCUMULATOR_T *accumulator, bool accept)
Consume current candidate and advance accumulator window.
Definition hdlc_sync.c:622
static bool hdlc_sync_get_aligned_byte(const HDLC_SYNC_ACCUMULATOR_T *accumulator, size_t raw_index, uint8_t bit_offset, bool shift_right, uint8_t *out_byte)
Definition hdlc_sync.c:136
static bool hdlc_sync_find_complete_candidate_short(const HDLC_SYNC_ACCUMULATOR_T *accumulator, size_t scan_index, size_t *out_start_index, uint8_t *out_bit_pos, bool *out_shift_right)
Definition hdlc_sync.c:248
static HDLC_SYNC_POLL_STEP_RESULT_T hdlc_sync_step_syncing(HDLC_SYNC_ACCUMULATOR_T *accumulator, HDLC_FRAME_T *out_frame, size_t *scan_index, e2s_error_t *out_error)
Definition hdlc_sync.c:379
static HDLC_SYNC_POLL_STEP_RESULT_T hdlc_sync_step_synced(HDLC_SYNC_ACCUMULATOR_T *accumulator, HDLC_FRAME_T *out_frame, size_t *scan_index, e2s_error_t *out_error)
Definition hdlc_sync.c:420
static void hdlc_sync_reject_oversized_candidate(HDLC_SYNC_ACCUMULATOR_T *accumulator, HDLC_FRAME_T *out_frame, size_t *scan_index)
Definition hdlc_sync.c:108
#define HDLC_SYNC_SHORT_BUFFER_SEARCH_LIMIT
Definition hdlc_sync.c:29
static bool hdlc_sync_find_opening_candidate(const HDLC_SYNC_ACCUMULATOR_T *accumulator, size_t scan_index, bool allow_left_shift, size_t *out_start_index, uint8_t *out_bit_pos, bool *out_shift_right)
Definition hdlc_sync.c:177
void hdlc_sync_acc_init(HDLC_SYNC_ACCUMULATOR_T *accumulator, uint8_t sync_byte)
Initialize HDLC sync accumulator state.
Definition hdlc_sync.c:507
#define HDLC_BITS_PER_BYTE
Definition hdlc_sync.c:26
#define HDLC_SYNC_MAX_REASONABLE_FRAME_SIZE_BYTES
Definition hdlc_sync.c:28
HDLC_SYNC_POLL_STEP_RESULT_T
Definition hdlc_sync.c:58
@ HDLC_SYNC_POLL_STEP_STOP
Definition hdlc_sync.c:62
@ HDLC_SYNC_POLL_STEP_FRAME_READY
Definition hdlc_sync.c:64
@ HDLC_SYNC_POLL_STEP_ERROR
Definition hdlc_sync.c:66
@ HDLC_SYNC_POLL_STEP_PROGRESS
Definition hdlc_sync.c:60
static void hdlc_sync_finalize_poll_no_frame(HDLC_SYNC_ACCUMULATOR_T *accumulator, size_t scan_index)
Definition hdlc_sync.c:483
static void hdlc_sync_reset_hunting_state(HDLC_SYNC_ACCUMULATOR_T *accumulator)
Definition hdlc_sync.c:90
static HDLC_SYNC_POLL_STEP_RESULT_T hdlc_sync_step_hunting(HDLC_SYNC_ACCUMULATOR_T *accumulator, HDLC_FRAME_T *out_frame, size_t *scan_index)
Definition hdlc_sync.c:343
static void hdlc_sync_drop_prefix(HDLC_SYNC_ACCUMULATOR_T *accumulator, size_t drop_count)
Definition hdlc_sync.c:70
e2s_error_t hdlc_sync_acc_poll(HDLC_SYNC_ACCUMULATOR_T *accumulator, HDLC_FRAME_T *out_frame)
Poll accumulator for an aligned HDLC frame candidate.
Definition hdlc_sync.c:549
#define HDLC_SYNC_MIN_FRAME_SIZE_BYTES
Definition hdlc_sync.c:27
bool hdlc_sync_acc_process_byte(HDLC_SYNC_ACCUMULATOR_T *accumulator, uint8_t byte)
Append one received raw byte to the accumulator.
Definition hdlc_sync.c:527
#define RX_HDLC_SYNC_MAX_BUFFER_SIZE
Maximum raw RX bytes retained in the HDLC sync accumulator.
Definition hdlc_sync.h:30
@ HDLC_SYNC_STATE_HUNTING
Definition hdlc_sync.h:43
@ HDLC_SYNC_STATE_SYNCED
Definition hdlc_sync.h:47
@ HDLC_SYNC_STATE_SYNCING
Definition hdlc_sync.h:45
Generic HDLC frame buffer descriptor.
Definition hdlc_common.h:38
size_t capacity
Definition hdlc_common.h:44
size_t length
Definition hdlc_common.h:42
uint8_t * payload
Definition hdlc_common.h:40
Accumulator and state for HDLC bit-offset synchronization.
Definition hdlc_sync.h:54
uint32_t lookahead_wait_synced
Definition hdlc_sync.h:75
uint32_t hardcap_drop_bytes
Definition hdlc_sync.h:79
uint8_t buffer[RX_HDLC_SYNC_MAX_BUFFER_SIZE]
Definition hdlc_sync.h:56
uint32_t lookahead_wait_syncing
Definition hdlc_sync.h:74
HDLC_SYNC_STATE_T state
Definition hdlc_sync.h:71
uint32_t hardcap_drop_events
Definition hdlc_sync.h:78