/
HdlcDecoder.cpp
175 lines (156 loc) · 4.84 KB
/
HdlcDecoder.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// Copyright 2016-2019 Rob Riggs <rob@mobilinkd.com>
// All rights reserved.
#include "HdlcDecoder.hpp"
#include "GPIO.hpp"
#include "Log.h"
namespace mobilinkd { namespace tnc { namespace hdlc {
NewDecoder::optional_result_type NewDecoder::operator()(bool input, bool pll_lock)
{
optional_result_type result = nullptr;
auto status = process(input, pll_lock);
if (status)
{
INFO("HDLC decode status = 0x%02x, bits = %d", int(status), int(report_bits));
if (can_pass(status) and packet->size() > 2)
{
result = packet;
packet = nullptr;
} else {
packet->clear();
}
}
return result;
}
uint8_t NewDecoder::process(bool input, bool pll_lock)
{
uint8_t result_code = 0;
if (state == State::IDLE and not pll_lock) return result_code;
while (packet == nullptr) {
packet = ioFramePool().acquire();
if (!packet) osThreadYield();
}
if (pll_lock or dcd != DCD::ON) {
if (ones == 5) {
if (input) {
// flag byte
flag = 1;
} else {
// bit stuffing...
flag = 0;
ones = 0;
return result_code;
}
}
had_dcd |= pll_lock;
buffer >>= 1;
buffer |= (input * 128);
bits += 1; // Free-running until Sync byte.
if (input) {
++ones;
} else {
ones = 0;
}
if (flag) {
switch (buffer) {
case 0x7E:
if (packet->size() > 2) {
// We have started decoding a packet.
packet->parse_fcs();
report_bits = bits;
if (dcd == DCD::PARTIAL and not had_dcd) {
// 120 (136) bits per AX.25 section 3.9.
// Note we discard the flags.
result_code = STATUS_NO_CARRIER;
} else if (packet->ok()) {
// Not compliant with AX.25 section 3.9.
// We ignore byte alignment when FCS is OK.
result_code = STATUS_OK;
} else if (bits == 8) {
// Otherwise, if there is a CRC error but we are on
// an even byte boundary, flag a CRC error. This is
// used by the "pass all" rule.
// Must be byte-aligned per AX.25 section 3.9.
result_code = STATUS_CRC_ERROR;
} else {
// Extraneous bits mean we have a framing error.
// We should not pass this frame up the stack.
result_code = STATUS_FRAME_ERROR;
}
} else {
packet->clear();
}
state = State::SYNC;
flag = 0;
bits = 0;
had_dcd = false;
break;
case 0xFE:
if (packet->size()) {
result_code = STATUS_FRAME_ABORT;
}
state = State::IDLE;
flag = 0;
bits = 0;
break;
default:
/* pass */
break;
}
return result_code;
}
switch (state)
{
case State::IDLE:
break;
case State::SYNC:
if (bits == 8) { // 8th bit.
// Start of frame data.
state = State::RECEIVE;
packet->push_back(buffer);
bits = 0;
}
break;
case State::RECEIVE:
if (bits == 8) { // 8th bit.
packet->push_back(buffer);
bits = 0;
}
}
} else {
// PLL unlocked.
// Note the rules here are the same as above.
report_bits = bits;
had_dcd = false;
if (packet->size() > 2)
{
packet->parse_fcs();
if (packet->ok())
{
// Not compliant with AX.25 section 3.9.
// We ignore byte alignment when FCS is OK.
result_code = STATUS_OK;
}
else if (bits == 8)
{
// Must be byte-aligned per AX.25 section 3.9.
result_code = STATUS_CRC_ERROR;
}
else
{
result_code = STATUS_NO_CARRIER;
}
}
else
{
packet->clear();
}
if (state != State::IDLE) {
buffer = 0;
flag = 0;
bits = 0;
state = State::IDLE;
}
}
return result_code;
}
}}} // mobilinkd::tnc::hdlc