1 // Copyright 2020 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
15 #include "pw_hdlc_lite/encoder.h"
23 #include "pw_bytes/endian.h"
24 #include "pw_checksum/crc32.h"
25 #include "pw_hdlc_lite_private/protocol.h"
29 namespace pw::hdlc_lite {
32 // Indicates this an information packet with sequence numbers set to 0.
33 constexpr byte kUnusedControl = byte{0};
35 Status EscapeAndWrite(const byte b, stream::Writer& writer) {
37 return writer.Write(kEscapedFlag);
40 return writer.Write(kEscapedEscape);
42 return writer.Write(b);
45 // Encodes and writes HDLC frames.
48 constexpr Encoder(stream::Writer& output) : writer_(output) {}
50 // Writes the header for an I-frame. After successfully calling
51 // StartInformationFrame, WriteData may be called any number of times.
52 Status StartInformationFrame(uint8_t address);
54 // Writes data for an ongoing frame. Must only be called after a successful
55 // StartInformationFrame call, and prior to a FinishFrame() call.
56 Status WriteData(ConstByteSpan data);
58 // Finishes a frame. Writes the frame check sequence and a terminating flag.
62 stream::Writer& writer_;
66 Status Encoder::StartInformationFrame(uint8_t address) {
68 if (Status status = writer_.Write(kFlag); !status.ok()) {
72 const byte address_and_control[] = {std::byte{address}, kUnusedControl};
73 return WriteData(address_and_control);
76 Status Encoder::WriteData(ConstByteSpan data) {
77 auto begin = data.begin();
79 auto end = std::find_if(begin, data.end(), NeedsEscaping);
81 if (Status status = writer_.Write(std::span(begin, end)); !status.ok()) {
84 if (end == data.end()) {
88 if (Status status = EscapeAndWrite(*end, writer_); !status.ok()) {
95 Status Encoder::FinishFrame() {
97 WriteData(bytes::CopyInOrder(std::endian::little, fcs_.value()));
101 return writer_.Write(kFlag);
106 Status WriteInformationFrame(uint8_t address,
107 ConstByteSpan payload,
108 stream::Writer& writer) {
109 Encoder encoder(writer);
111 if (Status status = encoder.StartInformationFrame(address); !status.ok()) {
114 if (Status status = encoder.WriteData(payload); !status.ok()) {
117 return encoder.FinishFrame();
120 } // namespace pw::hdlc_lite