1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_hdlc/decoder.h"
16
17 #include "pw_assert/check.h"
18 #include "pw_bytes/endian.h"
19 #include "pw_hdlc/internal/protocol.h"
20 #include "pw_log/log.h"
21 #include "pw_varint/varint.h"
22
23 using std::byte;
24
25 namespace pw::hdlc {
26
Parse(ConstByteSpan frame)27 Result<Frame> Frame::Parse(ConstByteSpan frame) {
28 uint64_t address;
29 size_t address_size = varint::Decode(frame, &address, kAddressFormat);
30 int data_size = frame.size() - address_size - kControlSize - kFcsSize;
31
32 if (address_size == 0 || data_size < 0) {
33 return Status::DataLoss();
34 }
35
36 return Frame(
37 address, frame[address_size], frame.subspan(address_size + 1, data_size));
38 }
39
Process(const byte new_byte)40 Result<Frame> Decoder::Process(const byte new_byte) {
41 switch (state_) {
42 case State::kInterFrame: {
43 if (new_byte == kFlag) {
44 state_ = State::kFrame;
45
46 // Report an error if non-flag bytes were read between frames.
47 if (current_frame_size_ != 0u) {
48 Reset();
49 return Status::DataLoss();
50 }
51 } else {
52 // Count bytes to track how many are discarded.
53 current_frame_size_ += 1;
54 }
55 return Status::Unavailable(); // Report error when starting a new frame.
56 }
57 case State::kFrame: {
58 if (new_byte == kFlag) {
59 const Status status = CheckFrame();
60
61 const size_t completed_frame_size = current_frame_size_;
62 Reset();
63
64 if (status.ok()) {
65 return Frame::Parse(buffer_.first(completed_frame_size));
66 }
67 return status;
68 }
69
70 if (new_byte == kEscape) {
71 state_ = State::kFrameEscape;
72 } else {
73 AppendByte(new_byte);
74 }
75 return Status::Unavailable();
76 }
77 case State::kFrameEscape: {
78 // The flag character cannot be escaped; return an error.
79 if (new_byte == kFlag) {
80 state_ = State::kFrame;
81 Reset();
82 return Status::DataLoss();
83 }
84
85 if (new_byte == kEscape) {
86 // Two escape characters in a row is illegal -- invalidate this frame.
87 // The frame is reported abandoned when the next flag byte appears.
88 state_ = State::kInterFrame;
89
90 // Count the escape byte so that the inter-frame state detects an error.
91 current_frame_size_ += 1;
92 } else {
93 state_ = State::kFrame;
94 AppendByte(Escape(new_byte));
95 }
96 return Status::Unavailable();
97 }
98 }
99 PW_CRASH("Bad decoder state");
100 }
101
AppendByte(byte new_byte)102 void Decoder::AppendByte(byte new_byte) {
103 if (current_frame_size_ < max_size()) {
104 buffer_[current_frame_size_] = new_byte;
105 }
106
107 if (current_frame_size_ >= last_read_bytes_.size()) {
108 // A byte will be ejected. Add it to the running checksum.
109 fcs_.Update(last_read_bytes_[last_read_bytes_index_]);
110 }
111
112 last_read_bytes_[last_read_bytes_index_] = new_byte;
113 last_read_bytes_index_ =
114 (last_read_bytes_index_ + 1) % last_read_bytes_.size();
115
116 // Always increase size: if it is larger than the buffer, overflow occurred.
117 current_frame_size_ += 1;
118 }
119
CheckFrame() const120 Status Decoder::CheckFrame() const {
121 // Empty frames are not an error; repeated flag characters are okay.
122 if (current_frame_size_ == 0u) {
123 return Status::Unavailable();
124 }
125
126 if (current_frame_size_ < Frame::kMinContentSizeBytes) {
127 PW_LOG_ERROR("Received %lu-byte frame; frame must be at least 6 bytes",
128 static_cast<unsigned long>(current_frame_size_));
129 return Status::DataLoss();
130 }
131
132 if (!VerifyFrameCheckSequence()) {
133 PW_LOG_ERROR("Frame check sequence verification failed");
134 return Status::DataLoss();
135 }
136
137 if (current_frame_size_ > max_size()) {
138 // Frame does not fit into the provided buffer; indicate this to the caller.
139 // This may not be considered an error if the caller is doing a partial
140 // decode.
141 return Status::ResourceExhausted();
142 }
143
144 return OkStatus();
145 }
146
VerifyFrameCheckSequence() const147 bool Decoder::VerifyFrameCheckSequence() const {
148 // De-ring the last four bytes read, which at this point contain the FCS.
149 std::array<std::byte, sizeof(uint32_t)> fcs_buffer;
150 size_t index = last_read_bytes_index_;
151
152 for (size_t i = 0; i < fcs_buffer.size(); ++i) {
153 fcs_buffer[i] = last_read_bytes_[index];
154 index = (index + 1) % last_read_bytes_.size();
155 }
156
157 uint32_t actual_fcs =
158 bytes::ReadInOrder<uint32_t>(endian::little, fcs_buffer);
159 return actual_fcs == fcs_.value();
160 }
161
162 } // namespace pw::hdlc
163