1 // Copyright 2020 The Pigweed Authors 2 // 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 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 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 13 // the License. 14 #pragma once 15 16 #include <algorithm> 17 #include <array> 18 #include <cstddef> 19 #include <cstring> 20 #include <functional> // std::invoke 21 22 #include "pw_assert/assert.h" 23 #include "pw_bytes/span.h" 24 #include "pw_checksum/crc32.h" 25 #include "pw_result/result.h" 26 #include "pw_status/status.h" 27 28 namespace pw::hdlc { 29 30 // Represents the contents of an HDLC frame -- the unescaped data between two 31 // flag bytes. Instances of Frame are only created when a full, valid frame has 32 // been read. 33 // 34 // For now, the Frame class assumes a single-byte control field and a 32-bit 35 // frame check sequence (FCS). 36 class Frame { 37 private: 38 static constexpr size_t kMinimumAddressSize = 1; 39 static constexpr size_t kControlSize = 1; 40 static constexpr size_t kFcsSize = sizeof(uint32_t); 41 42 public: 43 // The minimum size of a frame, excluding control bytes (flag or escape). 44 static constexpr size_t kMinSizeBytes = 45 kMinimumAddressSize + kControlSize + kFcsSize; 46 47 static Result<Frame> Parse(ConstByteSpan frame); 48 address()49 constexpr uint64_t address() const { return address_; } 50 control()51 constexpr std::byte control() const { return control_; } 52 data()53 constexpr ConstByteSpan data() const { return data_; } 54 55 private: 56 // Creates a Frame with the specified data. The data MUST be valid frame data 57 // with a verified frame check sequence. Frame(uint64_t address,std::byte control,ConstByteSpan data)58 constexpr Frame(uint64_t address, std::byte control, ConstByteSpan data) 59 : data_(data), address_(address), control_(control) {} 60 61 ConstByteSpan data_; 62 uint64_t address_; 63 std::byte control_; 64 }; 65 66 // The Decoder class facilitates decoding of data frames using the HDLC 67 // protocol, by returning packets as they are decoded and storing incomplete 68 // data frames in a buffer. 69 // 70 // The Decoder class does not own the buffer it writes to. It can be used to 71 // write bytes to any buffer. The DecoderBuffer template class, defined below, 72 // allocates a buffer. 73 class Decoder { 74 public: Decoder(ByteSpan buffer)75 constexpr Decoder(ByteSpan buffer) 76 : buffer_(buffer), 77 last_read_bytes_({}), 78 last_read_bytes_index_(0), 79 current_frame_size_(0), 80 state_(State::kInterFrame) {} 81 82 Decoder(const Decoder&) = delete; 83 Decoder& operator=(const Decoder&) = delete; 84 85 // Parses a single byte of an HDLC stream. Returns a Result with the complete 86 // frame if the byte completes a frame. The status is the following: 87 // 88 // OK - A frame was successfully decoded. The Result contains the Frame, 89 // which is invalidated by the next Process call. 90 // UNAVAILABLE - No frame is available. 91 // RESOURCE_EXHAUSTED - A frame completed, but it was too large to fit in 92 // the decoder's buffer. 93 // DATA_LOSS - A frame completed, but it was invalid. The frame was 94 // incomplete or the frame check sequence verification failed. 95 // 96 Result<Frame> Process(std::byte new_byte); 97 98 // Processes a span of data and calls the provided callback with each frame or 99 // error. 100 template <typename F, typename... Args> Process(ConstByteSpan data,F && callback,Args &&...args)101 void Process(ConstByteSpan data, F&& callback, Args&&... args) { 102 for (std::byte b : data) { 103 auto result = Process(b); 104 if (result.status() != Status::Unavailable()) { 105 std::invoke( 106 std::forward<F>(callback), std::forward<Args>(args)..., result); 107 } 108 } 109 } 110 111 // Returns the maximum size of the Decoder's frame buffer. max_size()112 size_t max_size() const { return buffer_.size(); } 113 114 // Clears and resets the decoder. Clear()115 void Clear() { 116 state_ = State::kInterFrame; 117 Reset(); 118 }; 119 120 private: 121 // State enum class is used to make the Decoder a finite state machine. 122 enum class State { 123 kInterFrame, 124 kFrame, 125 kFrameEscape, 126 }; 127 Reset()128 void Reset() { 129 current_frame_size_ = 0; 130 last_read_bytes_index_ = 0; 131 fcs_.clear(); 132 } 133 134 void AppendByte(std::byte new_byte); 135 136 Status CheckFrame() const; 137 138 bool VerifyFrameCheckSequence() const; 139 140 const ByteSpan buffer_; 141 142 // Ring buffer of the last four bytes read into the current frame, to allow 143 // calculating the frame's CRC incrementally. As data is evicted from this 144 // buffer, it is added to the running CRC. Once a frame is complete, the 145 // buffer contains the frame's FCS. 146 std::array<std::byte, sizeof(uint32_t)> last_read_bytes_; 147 size_t last_read_bytes_index_; 148 149 // Incremental checksum of the current frame. 150 checksum::Crc32 fcs_; 151 152 size_t current_frame_size_; 153 154 State state_; 155 }; 156 157 // DecoderBuffers declare a buffer along with a Decoder. 158 template <size_t kSizeBytes> 159 class DecoderBuffer : public Decoder { 160 public: DecoderBuffer()161 DecoderBuffer() : Decoder(frame_buffer_) {} 162 163 // Returns the maximum length of the bytes that can be inserted in the bytes 164 // buffer. max_size()165 static constexpr size_t max_size() { return kSizeBytes; } 166 167 private: 168 static_assert(kSizeBytes >= Frame::kMinSizeBytes); 169 170 std::array<std::byte, kSizeBytes> frame_buffer_; 171 }; 172 173 } // namespace pw::hdlc 174