1 // Copyright 2023 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 <array> 17 18 #include "pw_bytes/span.h" 19 #include "pw_hdlc/decoder.h" 20 #include "pw_hdlc/default_addresses.h" 21 #include "pw_hdlc/encoder.h" 22 #include "pw_result/result.h" 23 #include "pw_rpc_transport/rpc_transport.h" 24 #include "pw_status/status.h" 25 #include "pw_status/try.h" 26 #include "pw_stream/memory_stream.h" 27 #include "rpc_transport.h" 28 29 namespace pw::rpc { 30 31 inline constexpr size_t kHdlcProtocolOverheadBytes = 14; 32 33 template <size_t kMaxPacketSize> 34 class HdlcRpcPacketEncoder 35 : public RpcPacketEncoder<HdlcRpcPacketEncoder<kMaxPacketSize>> { 36 public: 37 // Encodes `packet` as HDLC UI frame and splits the resulting frame into 38 // chunks of `RpcFrame`s where every `RpcFrame` is no longer than 39 // `max_frame_size`. Calls `callback` for each of the resulting `RpcFrame`s. 40 // 41 // Returns: 42 // * FAILED_PRECONDITION if `packet` is too long or `max_frame_size` is zero. 43 // * The underlying HDLC encoding error if it fails to generate a UI frame. 44 // * The underlying callback invocation error from the first failed callback. 45 // 46 Status Encode(ConstByteSpan packet, 47 size_t max_frame_size, 48 OnRpcFrameEncodedCallback&& callback, 49 unsigned rpc_address = hdlc::kDefaultRpcAddress) { 50 if (packet.size() > kMaxPacketSize) { 51 return Status::FailedPrecondition(); 52 } 53 if (max_frame_size == 0) { 54 return Status::FailedPrecondition(); 55 } 56 stream::MemoryWriter writer(buffer_); 57 PW_TRY(hdlc::WriteUIFrame(rpc_address, packet, writer)); 58 59 auto remaining = writer.WrittenData(); 60 while (!remaining.empty()) { 61 auto next_fragment_size = std::min(max_frame_size, remaining.size()); 62 auto fragment = remaining.first(next_fragment_size); 63 // No header needed for HDLC: frame payload is already HDLC-encoded and 64 // includes frame delimiters. 65 RpcFrame frame{.header = {}, .payload = fragment}; 66 PW_TRY(callback(frame)); 67 remaining = remaining.subspan(next_fragment_size); 68 } 69 70 return OkStatus(); 71 } 72 73 private: 74 // Buffer for HDLC-encoded data. Must be 2x of the max packet size to 75 // accommodate HDLC escape bytes for the worst case where each payload byte 76 // must be escaped, plus 14 bytes for the HDLC protocol overhead. 77 static constexpr size_t kEncodeBufferSize = 78 2 * kMaxPacketSize + kHdlcProtocolOverheadBytes; 79 std::array<std::byte, kEncodeBufferSize> buffer_; 80 }; 81 82 template <size_t kMaxPacketSize> 83 class HdlcRpcPacketDecoder 84 : public RpcPacketDecoder<HdlcRpcPacketDecoder<kMaxPacketSize>> { 85 public: HdlcRpcPacketDecoder()86 HdlcRpcPacketDecoder() : decoder_(decode_buffer_) {} 87 88 // Finds and decodes HDLC frames in `buffer` and calls `callback` for each 89 // well-formed frame. Malformed frames are ignored and dropped quietly. Decode(ConstByteSpan buffer,OnRpcPacketDecodedCallback && callback)90 Status Decode(ConstByteSpan buffer, OnRpcPacketDecodedCallback&& callback) { 91 decoder_.Process( 92 buffer, 93 [callback = std::move(callback)](Result<hdlc::Frame> hdlc_frame) { 94 if (hdlc_frame.ok()) { 95 callback(hdlc_frame->data()); 96 } 97 }); 98 return OkStatus(); 99 } 100 101 private: 102 // decode_buffer_ is used to store a decoded HDLC packet, including the 103 // payload (of up to kMaxPacketSize), address (varint that is always 0 in our 104 // case), control flag and checksum. The total size of the non-payload 105 // components is kMinContentSizeBytes. 106 std::array<std::byte, kMaxPacketSize + hdlc::Frame::kMinContentSizeBytes> 107 decode_buffer_{}; 108 hdlc::Decoder decoder_; 109 }; 110 111 } // namespace pw::rpc 112