• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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