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