// Copyright 2020 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include "pw_hdlc/decoder.h" #include "pw_assert/assert.h" #include "pw_bytes/endian.h" #include "pw_hdlc/internal/protocol.h" #include "pw_log/log.h" #include "pw_varint/varint.h" using std::byte; namespace pw::hdlc { Result Frame::Parse(ConstByteSpan frame) { uint64_t address; size_t address_size = varint::Decode(frame, &address, kAddressFormat); int data_size = frame.size() - address_size - kControlSize - kFcsSize; if (address_size == 0 || data_size < 0) { return Status::DataLoss(); } return Frame( address, frame[address_size], frame.subspan(address_size + 1, data_size)); } Result Decoder::Process(const byte new_byte) { switch (state_) { case State::kInterFrame: { if (new_byte == kFlag) { state_ = State::kFrame; // Report an error if non-flag bytes were read between frames. if (current_frame_size_ != 0u) { Reset(); return Status::DataLoss(); } } else { // Count bytes to track how many are discarded. current_frame_size_ += 1; } return Status::Unavailable(); // Report error when starting a new frame. } case State::kFrame: { if (new_byte == kFlag) { const Status status = CheckFrame(); const size_t completed_frame_size = current_frame_size_; Reset(); if (status.ok()) { return Frame::Parse(buffer_.first(completed_frame_size)); } return status; } if (new_byte == kEscape) { state_ = State::kFrameEscape; } else { AppendByte(new_byte); } return Status::Unavailable(); } case State::kFrameEscape: { // The flag character cannot be escaped; return an error. if (new_byte == kFlag) { state_ = State::kFrame; Reset(); return Status::DataLoss(); } if (new_byte == kEscape) { // Two escape characters in a row is illegal -- invalidate this frame. // The frame is reported abandoned when the next flag byte appears. state_ = State::kInterFrame; // Count the escape byte so that the inter-frame state detects an error. current_frame_size_ += 1; } else { state_ = State::kFrame; AppendByte(Escape(new_byte)); } return Status::Unavailable(); } } PW_CRASH("Bad decoder state"); } void Decoder::AppendByte(byte new_byte) { if (current_frame_size_ < max_size()) { buffer_[current_frame_size_] = new_byte; } if (current_frame_size_ >= last_read_bytes_.size()) { // A byte will be ejected. Add it to the running checksum. fcs_.Update(last_read_bytes_[last_read_bytes_index_]); } last_read_bytes_[last_read_bytes_index_] = new_byte; last_read_bytes_index_ = (last_read_bytes_index_ + 1) % last_read_bytes_.size(); // Always increase size: if it is larger than the buffer, overflow occurred. current_frame_size_ += 1; } Status Decoder::CheckFrame() const { // Empty frames are not an error; repeated flag characters are okay. if (current_frame_size_ == 0u) { return Status::Unavailable(); } if (current_frame_size_ < Frame::kMinSizeBytes) { PW_LOG_ERROR("Received %lu-byte frame; frame must be at least 6 bytes", static_cast(current_frame_size_)); return Status::DataLoss(); } if (!VerifyFrameCheckSequence()) { PW_LOG_ERROR("Frame check sequence verification failed"); return Status::DataLoss(); } if (current_frame_size_ > max_size()) { // Frame does not fit into the provided buffer; indicate this to the caller. // This may not be considered an error if the caller is doing a partial // decode. return Status::ResourceExhausted(); } return OkStatus(); } bool Decoder::VerifyFrameCheckSequence() const { // De-ring the last four bytes read, which at this point contain the FCS. std::array fcs_buffer; size_t index = last_read_bytes_index_; for (size_t i = 0; i < fcs_buffer.size(); ++i) { fcs_buffer[i] = last_read_bytes_[index]; index = (index + 1) % last_read_bytes_.size(); } uint32_t actual_fcs = bytes::ReadInOrder(std::endian::little, fcs_buffer); return actual_fcs == fcs_.value(); } } // namespace pw::hdlc