1 // Copyright 2021 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
15 #include <array>
16 #include <cstddef>
17 #include <cstdint>
18 #include <cstdio>
19 #include <mutex>
20
21 #include "pw_assert/check.h"
22 #include "pw_hdlc/decoder.h"
23 #include "pw_hdlc/default_addresses.h"
24 #include "pw_hdlc/encoded_size.h"
25 #include "pw_hdlc/rpc_channel.h"
26 #include "pw_log/log.h"
27 #include "pw_rpc/channel.h"
28 #include "pw_sync/mutex.h"
29 #include "pw_system/config.h"
30 #include "pw_system/io.h"
31 #include "pw_system/rpc_server.h"
32 #include "pw_trace/trace.h"
33
34 #if PW_SYSTEM_DEFAULT_CHANNEL_ID != PW_SYSTEM_LOGGING_CHANNEL_ID && \
35 PW_SYSTEM_DEFAULT_RPC_HDLC_ADDRESS == PW_SYSTEM_LOGGING_RPC_HDLC_ADDRESS
36 #error \
37 "Default and logging addresses must be different to support multiple channels."
38 #endif
39
40 namespace pw::system {
41 namespace {
42
43 constexpr size_t kMaxTransmissionUnit = PW_SYSTEM_MAX_TRANSMISSION_UNIT;
44
45 static_assert(kMaxTransmissionUnit ==
46 hdlc::MaxEncodedFrameSize(rpc::cfg::kEncodingBufferSizeBytes));
47
48 #if PW_SYSTEM_DEFAULT_CHANNEL_ID == PW_SYSTEM_LOGGING_CHANNEL_ID
49 hdlc::FixedMtuChannelOutput<kMaxTransmissionUnit> hdlc_channel_output(
50 GetWriter(), PW_SYSTEM_DEFAULT_RPC_HDLC_ADDRESS, "HDLC channel");
51 rpc::Channel channels[] = {
52 rpc::Channel::Create<kDefaultRpcChannelId>(&hdlc_channel_output)};
53 #else
54 class SynchronizedChannelOutput : public rpc::ChannelOutput {
55 public:
SynchronizedChannelOutput(stream::Writer & writer,uint64_t address,const char * channel_name)56 SynchronizedChannelOutput(stream::Writer& writer,
57 uint64_t address,
58 const char* channel_name)
59 : rpc::ChannelOutput(channel_name),
60 inner_(writer, address, channel_name) {}
61
Send(span<const std::byte> buffer)62 Status Send(span<const std::byte> buffer) override {
63 std::lock_guard guard(mtx_);
64 auto s = inner_.Send(buffer);
65 return s;
66 }
67
MaximumTransmissionUnit()68 size_t MaximumTransmissionUnit() override {
69 std::lock_guard guard(mtx_);
70 auto s = inner_.MaximumTransmissionUnit();
71 return s;
72 }
73
74 private:
75 sync::Mutex mtx_;
76 hdlc::FixedMtuChannelOutput<kMaxTransmissionUnit> inner_ PW_GUARDED_BY(mtx_);
77 };
78
79 SynchronizedChannelOutput hdlc_channel_output[] = {
80 SynchronizedChannelOutput(GetWriter(),
81 PW_SYSTEM_DEFAULT_RPC_HDLC_ADDRESS,
82 "HDLC default channel"),
83 SynchronizedChannelOutput(GetWriter(),
84 PW_SYSTEM_LOGGING_RPC_HDLC_ADDRESS,
85 "HDLC logging channel"),
86 };
87 rpc::Channel channels[] = {
88 rpc::Channel::Create<kDefaultRpcChannelId>(&hdlc_channel_output[0]),
89 rpc::Channel::Create<kLoggingRpcChannelId>(&hdlc_channel_output[1]),
90 };
91 #endif
92 rpc::Server server(channels);
93
94 constexpr size_t kDecoderBufferSize =
95 hdlc::Decoder::RequiredBufferSizeForFrameSize(kMaxTransmissionUnit);
96 // Declare a buffer for decoding incoming HDLC frames.
97 std::array<std::byte, kDecoderBufferSize> input_buffer;
98 hdlc::Decoder decoder(input_buffer);
99
100 std::array<std::byte, 1> data;
101
102 } // namespace
103
GetRpcServer()104 rpc::Server& GetRpcServer() { return server; }
105
106 class RpcDispatchThread final : public thread::ThreadCore {
107 public:
108 RpcDispatchThread() = default;
109 RpcDispatchThread(const RpcDispatchThread&) = delete;
110 RpcDispatchThread(RpcDispatchThread&&) = delete;
111 RpcDispatchThread& operator=(const RpcDispatchThread&) = delete;
112 RpcDispatchThread& operator=(RpcDispatchThread&&) = delete;
113
Run()114 void Run() override {
115 PW_LOG_INFO("Running RPC server");
116 while (true) {
117 auto ret_val = GetReader().Read(data);
118 if (!ret_val.ok()) {
119 continue;
120 }
121 for (std::byte byte : ret_val.value()) {
122 if (auto result = decoder.Process(byte); result.ok()) {
123 hdlc::Frame& frame = result.value();
124 PW_TRACE_SCOPE("RPC process frame");
125 if (frame.address() == PW_SYSTEM_DEFAULT_RPC_HDLC_ADDRESS ||
126 frame.address() == PW_SYSTEM_LOGGING_RPC_HDLC_ADDRESS) {
127 if (!server.ProcessPacket(frame.data()).ok()) {
128 PW_LOG_ERROR("Failed to process packet");
129 }
130 }
131 }
132 }
133 }
134 }
135 };
136
GetRpcDispatchThread()137 thread::ThreadCore& GetRpcDispatchThread() {
138 static RpcDispatchThread rpc_dispatch_thread;
139 return rpc_dispatch_thread;
140 }
141
142 } // namespace pw::system
143