2020-07-08 14:39:14 +00:00
|
|
|
// 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.
|
2020-07-23 22:40:28 +00:00
|
|
|
|
2021-01-08 21:08:45 +00:00
|
|
|
#include "pw_hdlc/encoder.h"
|
2020-07-23 22:40:28 +00:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <array>
|
|
|
|
#include <cstddef>
|
|
|
|
#include <cstring>
|
|
|
|
#include <span>
|
|
|
|
|
2020-09-03 20:32:00 +00:00
|
|
|
#include "pw_bytes/endian.h"
|
|
|
|
#include "pw_checksum/crc32.h"
|
2021-01-08 21:08:45 +00:00
|
|
|
#include "pw_hdlc_private/protocol.h"
|
2020-07-23 22:40:28 +00:00
|
|
|
|
|
|
|
using std::byte;
|
|
|
|
|
2021-01-08 21:08:45 +00:00
|
|
|
namespace pw::hdlc {
|
2020-07-23 22:40:28 +00:00
|
|
|
namespace {
|
|
|
|
|
2020-09-03 20:32:00 +00:00
|
|
|
// Indicates this an information packet with sequence numbers set to 0.
|
|
|
|
constexpr byte kUnusedControl = byte{0};
|
|
|
|
|
|
|
|
Status EscapeAndWrite(const byte b, stream::Writer& writer) {
|
2020-09-14 15:49:17 +00:00
|
|
|
if (b == kFlag) {
|
2020-09-03 20:32:00 +00:00
|
|
|
return writer.Write(kEscapedFlag);
|
|
|
|
}
|
2020-09-14 15:49:17 +00:00
|
|
|
if (b == kEscape) {
|
2020-09-03 20:32:00 +00:00
|
|
|
return writer.Write(kEscapedEscape);
|
2020-07-23 22:40:28 +00:00
|
|
|
}
|
|
|
|
return writer.Write(b);
|
|
|
|
}
|
|
|
|
|
2020-09-03 20:32:00 +00:00
|
|
|
// Encodes and writes HDLC frames.
|
|
|
|
class Encoder {
|
|
|
|
public:
|
|
|
|
constexpr Encoder(stream::Writer& output) : writer_(output) {}
|
2020-07-23 22:40:28 +00:00
|
|
|
|
2020-09-03 20:32:00 +00:00
|
|
|
// Writes the header for an I-frame. After successfully calling
|
|
|
|
// StartInformationFrame, WriteData may be called any number of times.
|
2020-12-10 06:43:55 +00:00
|
|
|
[[maybe_unused]] Status StartInformationFrame(uint8_t address);
|
|
|
|
|
|
|
|
// Writes the header for an U-frame. After successfully calling
|
|
|
|
// StartUnnumberedFrame, WriteData may be called any number of times.
|
|
|
|
Status StartUnnumberedFrame(uint8_t address);
|
2020-09-03 20:32:00 +00:00
|
|
|
|
|
|
|
// Writes data for an ongoing frame. Must only be called after a successful
|
|
|
|
// StartInformationFrame call, and prior to a FinishFrame() call.
|
|
|
|
Status WriteData(ConstByteSpan data);
|
|
|
|
|
|
|
|
// Finishes a frame. Writes the frame check sequence and a terminating flag.
|
|
|
|
Status FinishFrame();
|
|
|
|
|
|
|
|
private:
|
|
|
|
stream::Writer& writer_;
|
|
|
|
checksum::Crc32 fcs_;
|
|
|
|
};
|
2020-07-23 22:40:28 +00:00
|
|
|
|
2020-09-03 20:32:00 +00:00
|
|
|
Status Encoder::StartInformationFrame(uint8_t address) {
|
|
|
|
fcs_.clear();
|
2020-09-14 15:49:17 +00:00
|
|
|
if (Status status = writer_.Write(kFlag); !status.ok()) {
|
2020-07-23 22:40:28 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2020-12-10 06:43:55 +00:00
|
|
|
const byte address_and_control[] = {
|
|
|
|
std::byte{address}, kUnusedControl, kUnusedControl};
|
|
|
|
return WriteData(address_and_control);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status Encoder::StartUnnumberedFrame(uint8_t address) {
|
|
|
|
fcs_.clear();
|
|
|
|
if (Status status = writer_.Write(kFlag); !status.ok()) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
const byte address_and_control[] = {
|
|
|
|
std::byte{address}, UFrameControl::UnnumberedInformation().data()};
|
2020-09-03 20:32:00 +00:00
|
|
|
return WriteData(address_and_control);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status Encoder::WriteData(ConstByteSpan data) {
|
|
|
|
auto begin = data.begin();
|
2020-07-23 22:40:28 +00:00
|
|
|
while (true) {
|
2020-09-03 20:32:00 +00:00
|
|
|
auto end = std::find_if(begin, data.end(), NeedsEscaping);
|
2020-07-23 22:40:28 +00:00
|
|
|
|
2020-09-03 20:32:00 +00:00
|
|
|
if (Status status = writer_.Write(std::span(begin, end)); !status.ok()) {
|
2020-07-23 22:40:28 +00:00
|
|
|
return status;
|
|
|
|
}
|
2020-09-03 20:32:00 +00:00
|
|
|
if (end == data.end()) {
|
|
|
|
fcs_.Update(data);
|
2021-01-07 21:26:57 +00:00
|
|
|
return OkStatus();
|
2020-07-23 22:40:28 +00:00
|
|
|
}
|
2020-09-03 20:32:00 +00:00
|
|
|
if (Status status = EscapeAndWrite(*end, writer_); !status.ok()) {
|
2020-07-23 22:40:28 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
begin = end + 1;
|
|
|
|
}
|
2020-09-03 20:32:00 +00:00
|
|
|
}
|
2020-07-23 22:40:28 +00:00
|
|
|
|
2020-09-03 20:32:00 +00:00
|
|
|
Status Encoder::FinishFrame() {
|
2020-09-02 20:11:03 +00:00
|
|
|
if (Status status =
|
2020-09-03 20:32:00 +00:00
|
|
|
WriteData(bytes::CopyInOrder(std::endian::little, fcs_.value()));
|
2020-09-02 20:11:03 +00:00
|
|
|
!status.ok()) {
|
2020-07-23 22:40:28 +00:00
|
|
|
return status;
|
|
|
|
}
|
2020-09-14 15:49:17 +00:00
|
|
|
return writer_.Write(kFlag);
|
2020-09-03 20:32:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2020-12-10 06:43:55 +00:00
|
|
|
Status WriteUIFrame(uint8_t address,
|
|
|
|
ConstByteSpan payload,
|
|
|
|
stream::Writer& writer) {
|
2020-09-03 20:32:00 +00:00
|
|
|
Encoder encoder(writer);
|
|
|
|
|
2020-12-10 06:43:55 +00:00
|
|
|
if (Status status = encoder.StartUnnumberedFrame(address); !status.ok()) {
|
2020-09-03 20:32:00 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
if (Status status = encoder.WriteData(payload); !status.ok()) {
|
2020-07-23 22:40:28 +00:00
|
|
|
return status;
|
|
|
|
}
|
2020-09-03 20:32:00 +00:00
|
|
|
return encoder.FinishFrame();
|
2020-07-23 22:40:28 +00:00
|
|
|
}
|
|
|
|
|
2021-01-08 21:08:45 +00:00
|
|
|
} // namespace pw::hdlc
|