ether2ser 0.1.0
Ethernet <-> synchronous V.24 bridge firmware for RP2040 + W5500
Loading...
Searching...
No Matches
hdlc_decoder.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_decoder.c
5 * Purpose: HDLC decoder (deframing, unescaping, and CRC check).
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_decoder.h"
14
15// Standard library headers
16#include <limits.h>
17#include <stdbool.h>
18#include <stddef.h>
19#include <stdint.h>
20
21// Project Headers
22#include "hdlc_common.h"
23#include "system/common.h"
24
25// Generated headers
26
27#define HDLC_BIT_STUFF_ONES_LIMIT 5U
28
29typedef struct
30{
31 uint8_t ones_run;
42
44
45bool hdlc_decode_byte(const HDLC_FRAME_T* frame, uint8_t* payload, const size_t out_capacity,
46 size_t* payload_length)
47{
48 if (frame == NULL || payload == NULL || payload_length == NULL || out_capacity == 0 ||
49 frame->payload[0] != HDLC_FLAG_BYTE || frame->payload[frame->length - 1] != HDLC_FLAG_BYTE)
50 {
52 LOG_DEBUG("Invalid frame\r\n");
53 goto abort;
54 }
55
56 bool found_escape = false;
57 size_t outbyte_ctr = 0;
58 for (size_t frame_cntr = 1; frame_cntr < frame->length - 1; frame_cntr++)
59 {
60 if (frame->payload[frame_cntr] == HDLC_ESCAPE_BYTE)
61 {
62 found_escape = true;
63 continue;
64 }
65 if (outbyte_ctr >= out_capacity)
66 {
68 LOG_DEBUG("Payload too long\r\n");
69 goto abort;
70 }
71 payload[outbyte_ctr++] = found_escape ? frame->payload[frame_cntr] ^ HDLC_ESCAPE_XOR
72 : frame->payload[frame_cntr];
73 found_escape = false;
74 }
75
76 if (outbyte_ctr < 2)
77 {
79 goto abort;
80 }
81
82 uint16_t crc16 = payload[outbyte_ctr - 2] << 8 | payload[outbyte_ctr - 1];
83 *payload_length = (outbyte_ctr - 2);
84
85 // Check crc
86 uint16_t recovered_crc = hdlc_crc16(payload, *payload_length);
87 if (crc16 != recovered_crc)
88 {
90 goto abort;
91 }
92
93 return true;
94
95abort:
96 if (payload_length)
97 {
98 *payload_length = 0;
99 }
100 return false;
101}
102
103#define HDLC_DEC_BYTE_IDX(raw_bit_index) (1 + ((raw_bit_index) / CHAR_BIT))
104#define HDLC_DEC_BIT_IDX(raw_bit_index) ((raw_bit_index) % CHAR_BIT)
105#define HDLC_DEC_BIT_POS(decoder) \
106 ((decoder)->lsb_first ? (HDLC_DEC_BIT_IDX((decoder)->raw_bit_index)) \
107 : ((7 - HDLC_DEC_BIT_IDX((decoder)->raw_bit_index))))
108#define HDLC_DEC_GET_OUT_BIT(decoder) \
109 (frame->payload[HDLC_DEC_BYTE_IDX((decoder)->raw_bit_index)] >> HDLC_DEC_BIT_POS((decoder)) & 1)
110
112 uint8_t* out_bit)
113{
114 if (!decoder || !frame || !out_bit)
115 {
116 return HDLC_BIT_ERR;
117 }
118 size_t raw_bits_total = (frame->length > 2) ? (frame->length - 2) * CHAR_BIT : 0;
119
120 // TODO: This has to be tested on target
121 while (decoder->raw_bit_index < raw_bits_total)
122 {
123 uint8_t raw_bit = HDLC_DEC_GET_OUT_BIT(decoder);
124 ++decoder->raw_bit_index;
125 if (decoder->skip_next_zero)
126 {
127 if (raw_bit != 0)
128 {
129 return HDLC_BIT_ERR;
130 }
131 decoder->skip_next_zero = false;
132 decoder->ones_run = 0;
133 continue;
134 }
135 *out_bit = raw_bit;
136 if (raw_bit)
137 {
138 if ((++decoder->ones_run) == HDLC_BIT_STUFF_ONES_LIMIT)
139 {
140 decoder->skip_next_zero = true;
141 }
142 }
143 else
144 {
145 decoder->ones_run = 0;
146 }
147 return HDLC_BIT_OK;
148 }
149 return HDLC_BIT_EOF;
150}
151
153 uint8_t* byte)
154{
155 *byte = 0;
156 for (uint8_t i = 0; i < CHAR_BIT; i++)
157 {
158 size_t bit_position = 0;
159 uint8_t bit;
160 hdlc_decoder_bit_type_t bit_result = hdlc_get_bit(decoder, frame, &bit);
161 if (bit_result != HDLC_BIT_OK)
162 {
163 return bit_result;
164 }
165 bit_position = decoder->lsb_first ? i : (CHAR_BIT - 1U - i);
166 *byte |= bit << bit_position;
167 }
168 return HDLC_BIT_OK;
169}
170
171bool hdlc_decode(const HDLC_FRAME_T* frame, uint8_t* payload, const size_t out_capacity,
172 size_t* payload_length, bool lsb_first)
173{
174 if (frame == NULL || payload == NULL || payload_length == NULL || out_capacity == 0 ||
175 frame->length < 2 || frame->payload[0] != HDLC_FLAG_BYTE ||
176 frame->payload[frame->length - 1] != HDLC_FLAG_BYTE)
177 {
179 LOG_DEBUG("Invalid frame\r\n");
180 goto abort;
181 }
182
183 size_t outbyte_ctr = 0;
184 hdlc_decoder_t decoder = {
185 .raw_bit_index = 0, .ones_run = 0, .skip_next_zero = false, .lsb_first = lsb_first};
186 for (;;)
187 {
188 uint8_t out_byte;
189 hdlc_decoder_bit_type_t bit_result = hdlc_get_byte(&decoder, frame, &out_byte);
190 if (bit_result == HDLC_BIT_EOF)
191 {
192 break;
193 }
194 if (bit_result == HDLC_BIT_ERR)
195 {
197 LOG_DEBUG("Invalid frame\r\n");
198 goto abort;
199 }
200 if (outbyte_ctr >= out_capacity)
201 {
203 LOG_DEBUG("Payload too long\r\n");
204 goto abort;
205 }
206 payload[outbyte_ctr++] = out_byte;
207 }
208
209 if (outbyte_ctr < 2)
210 {
212 goto abort;
213 }
214
215 uint16_t crc16 = payload[outbyte_ctr - 2] << CHAR_BIT | payload[outbyte_ctr - 1];
216 *payload_length = (outbyte_ctr - 2);
217
218 // Check crc
219 uint16_t recovered_crc = hdlc_crc16(payload, *payload_length);
220 if (crc16 != recovered_crc)
221 {
223 LOG_DEBUG("CRC mismatch\r\n");
224 goto abort;
225 }
226
227 return true;
228
229abort:
230 if (payload_length)
231 {
232 *payload_length = 0;
233 }
234 return false;
235}
236
238{
239 if (out_stats == NULL)
240 {
241 return;
242 }
243 *out_stats = hdlc_decode_stats;
244}
245
#define LOG_DEBUG(...)
Definition common.h:165
uint16_t hdlc_crc16(const uint8_t *payload, size_t num_bytes)
Compute HDLC CRC16 (FCS) over a payload.
Definition hdlc_common.c:45
#define HDLC_ESCAPE_BYTE
Definition hdlc_common.h:30
#define HDLC_FLAG_BYTE
Definition hdlc_common.h:29
#define HDLC_ESCAPE_XOR
Definition hdlc_common.h:31
static hdlc_decode_stats_t hdlc_decode_stats
void hdlc_decode_stats_reset(void)
Reset cumulative HDLC decode statistics.
static hdlc_decoder_bit_type_t hdlc_get_bit(hdlc_decoder_t *decoder, const HDLC_FRAME_T *frame, uint8_t *out_bit)
bool hdlc_decode(const HDLC_FRAME_T *frame, uint8_t *payload, const size_t out_capacity, size_t *payload_length, bool lsb_first)
Decode an HDLC bit-stuffed frame.
hdlc_decoder_bit_type_t
@ HDLC_BIT_OK
@ HDLC_BIT_EOF
@ HDLC_BIT_ERR
void hdlc_decode_stats_snapshot(hdlc_decode_stats_t *out_stats)
Copy cumulative HDLC decode statistics.
bool hdlc_decode_byte(const HDLC_FRAME_T *frame, uint8_t *payload, const size_t out_capacity, size_t *payload_length)
Decode a byte-escaped HDLC frame compatibility path.
#define HDLC_DEC_GET_OUT_BIT(decoder)
static hdlc_decoder_bit_type_t hdlc_get_byte(hdlc_decoder_t *decoder, const HDLC_FRAME_T *frame, uint8_t *byte)
#define HDLC_BIT_STUFF_ONES_LIMIT
Generic HDLC frame buffer descriptor.
Definition hdlc_common.h:38
size_t length
Definition hdlc_common.h:42
uint8_t * payload
Definition hdlc_common.h:40
Cumulative HDLC decode failure reason counters.
uint64_t payload_too_long
size_t raw_bit_index