You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
4.8 KiB
163 lines
4.8 KiB
// 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> 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<Frame> 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<unsigned long>(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<std::byte, sizeof(uint32_t)> 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<uint32_t>(std::endian::little, fcs_buffer);
|
|
return actual_fcs == fcs_.value();
|
|
}
|
|
|
|
} // namespace pw::hdlc
|