1 // Copyright 2024 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 // __ ___ ___ _ _ ___ _ _ ___ 17 // \ \ / /_\ | _ \ \| |_ _| \| |/ __| 18 // \ \/\/ / _ \| / .` || || .` | (_ | 19 // \_/\_/_/ \_\_|_\_|\_|___|_|\_|\___| 20 // _____ _____ ___ ___ ___ __ __ ___ _ _ _____ _ _ 21 // | __\ \/ / _ \ __| _ \_ _| \/ | __| \| |_ _/_\ | | 22 // | _| > <| _/ _|| /| || |\/| | _|| .` | | |/ _ \| |__ 23 // |___/_/\_\_| |___|_|_\___|_| |_|___|_|\_| |_/_/ \_\____| 24 // 25 // This module is in an early, experimental state. The APIs are in flux and may 26 // change without notice. Please do not rely on it in production code, but feel 27 // free to explore and share feedback with the Pigweed team! 28 29 #include "pw_async2/dispatcher.h" 30 #include "pw_async2/poll.h" 31 #include "pw_channel/channel.h" 32 #include "pw_containers/vector.h" 33 #include "pw_hdlc/decoder.h" 34 #include "pw_multibuf/allocator.h" 35 #include "pw_multibuf/multibuf.h" 36 #include "pw_status/status.h" 37 38 namespace pw::hdlc { 39 40 /// A router that multiplexes multiple datagram-oriented ``Channel`` s 41 /// over a single byte-oriented ``Channel`` using HDLC framing. 42 class Router final { 43 public: 44 class Registration; 45 46 /// Constructs a ``Router`` 47 /// 48 /// @param[in] io_channel The channel on which to send and receive encoded 49 /// HDLC packets. 50 /// 51 /// @param[in] decode_buffer The memory to use for storing partially-decoded 52 /// HDLC frames. This buffer should be at least 53 /// ``Decoder::RequiredBufferSizeForFrameSize(frame_size)`` bytes in order 54 /// to ensure that HDLC frames of size ``frame_size`` can be successfully 55 /// decoded. Router(pw::channel::ByteReaderWriter & io_channel,ByteSpan decode_buffer)56 Router(pw::channel::ByteReaderWriter& io_channel, ByteSpan decode_buffer) 57 : io_channel_(io_channel), decoder_(decode_buffer) {} 58 59 // Router is not copyable or movable. 60 Router(const Router&) = delete; 61 Router& operator=(const Router&) = delete; 62 Router(Router&&) = delete; 63 Router& operator=(Router&&) = delete; 64 65 /// Registers a ``Channel`` tied to the provided addresses. 66 /// 67 /// All incoming HDLC messages received on ``io_channel`` with HDLC address 68 /// ``receive_address`` will be decoded and routed to the provided 69 /// ``channel``. 70 /// 71 /// Data read from ``channel`` will be HDLC-encoded and sent to 72 /// ``io_channel``. 73 /// 74 /// Note that a non-writeable channel will exert backpressure on the entire 75 /// router, so channels should strive to consume or discard incoming data as 76 /// quickly as possible in order to prevent starvation of other channels. 77 /// 78 /// @param[in] receive_address Incoming HDLC messages received on the 79 /// external ``io_channel`` with an address matching ``receive_address`` 80 /// will be decoded and written to ``channel``. 81 /// 82 /// @param[in] send_address Data read from ``channel`` will be written 83 /// to ``io_channel`` with the HDLC address ``send_address``. 84 /// 85 /// @returns @rst 86 /// 87 /// .. pw-status-codes:: 88 /// 89 /// OK: ``Channel`` was successfully registered. 90 /// 91 /// ALREADY_EXISTS: A registration already exists for either 92 /// ``channel``, ``receive_address``, or ``send_address``. Channels may 93 /// not be registered with multiple addresses, nor may addresses be 94 /// used with multiple channels. 95 /// 96 /// @endrst 97 Status AddChannel(pw::channel::DatagramReaderWriter& channel, 98 uint64_t receive_address, 99 uint64_t send_address); 100 101 /// Removes a previously registered ``Channel`` tied to the provided 102 /// addresses. 103 /// 104 /// @returns @rst 105 /// 106 /// .. pw-status-codes:: 107 /// 108 /// OK: The channel was successfully deregistered. 109 /// 110 /// NOT_FOUND: A registration of the channel for the provided 111 /// addresses was not found. 112 /// 113 /// @endrst 114 Status RemoveChannel(pw::channel::DatagramReaderWriter& channel, 115 uint64_t receive_address, 116 uint64_t send_address); 117 118 /// Progress the router as far as possible, waking the provided ``cx`` 119 /// when more progress can be made. 120 /// 121 /// This will only return ``Ready`` if ``io_channel`` has been observed as 122 /// closed, after which all messages have been flushed to the remaining 123 /// channels and the channels have been closed. 124 pw::async2::Poll<> Pend(pw::async2::Context& cx); 125 126 /// Closes all underlying channels, attempting to flush any data. 127 pw::async2::Poll<> PendClose(pw::async2::Context& cx); 128 129 private: 130 // TODO: https://pwbug.dev/329902904 - Use allocator-based collections and 131 // remove this arbitrary limit. 132 constexpr static size_t kSomeNumberOfChannels = 16; 133 134 /// A channel associated with an incoming and outgoing address. 135 struct ChannelData { ChannelDataChannelData136 ChannelData(pw::channel::DatagramReaderWriter& channel_arg, 137 uint64_t receive_address_arg, 138 uint64_t send_address_arg) 139 : channel(&channel_arg), 140 receive_address(receive_address_arg), 141 send_address(send_address_arg) {} 142 143 ChannelData(const ChannelData&) = delete; 144 ChannelData& operator=(const ChannelData&) = delete; 145 ChannelData(ChannelData&&) = default; 146 ChannelData& operator=(ChannelData&&) = default; 147 148 /// A channel which reads and writes datagrams. 149 pw::channel::DatagramReaderWriter* channel; 150 151 /// Data received over HDLC with this address will be sent to ``channel``. 152 uint64_t receive_address; 153 154 /// Data read from ``channel`` will be sent out over HDLC with this 155 /// address. 156 uint64_t send_address; 157 }; 158 159 /// Returns a pointer to the ``ChannelData`` corresponding to the provided 160 /// ``receive_address`` or nullptr if no such entry is found. 161 ChannelData* FindChannelForReceiveAddress(uint64_t receive_address); 162 163 /// Decodes and writes buffers from ``io_channel_`` and writes them into 164 /// the corresponding channel. 165 void DecodeAndWriteIncoming(pw::async2::Context& cx); 166 167 /// Attempts to send the decoded ``frame`` contents to the corresponding 168 /// channel. 169 pw::async2::Poll<> PollDeliverIncomingFrame(pw::async2::Context& cx, 170 const Frame& frame); 171 172 /// Searches channels for a ``buffer_to_encode_and_send_`` if there is none. 173 void TryFillBufferToEncodeAndSend(pw::async2::Context& cx); 174 175 /// Reads from ``channel_datas_``, HDLC encodes the packets, and sends them 176 /// out over ``io_channel_``. 177 void WriteOutgoingMessages(pw::async2::Context& cx); 178 179 /// Removes any entries in ``channel_datas_`` that have closed. 180 /// 181 /// Consolidating this into one operation allows for a minimal amount of 182 /// shifting of the various channel elements. 183 void RemoveClosedChannels(); 184 185 ////////////////////////////////////////////////////////// 186 /// Channels used for both incoming and outgoing data. /// 187 ////////////////////////////////////////////////////////// 188 189 /// The underlying channel over which HDLC-encoded messages are sent and 190 /// received. This is frequently a low-level driver e.g. UART. 191 pw::channel::ByteReaderWriter& io_channel_; 192 193 /// The channels which send and receive unencoded data. 194 pw::Vector<ChannelData, kSomeNumberOfChannels> channel_datas_; 195 196 /////////////////////////////////////////////////////////// 197 /// State associated with the incoming data being read. /// 198 /////////////////////////////////////////////////////////// 199 200 /// Incoming data that has not yet been processed by ``decoder_``. 201 pw::multibuf::MultiBuf incoming_data_; 202 203 /// An HDLC decoder. 204 pw::hdlc::Decoder decoder_; 205 206 /// The most recent frame returned by ``decoder_``. 207 std::optional<pw::hdlc::Frame> decoded_frame_; 208 209 /////////////////////////////////////////////////////////// 210 /// State associated with the outgoing data being sent. /// 211 /////////////////////////////////////////////////////////// 212 213 struct OutgoingBuffer { 214 pw::multibuf::MultiBuf buffer; 215 size_t hdlc_encoded_size; 216 uint64_t target_address; 217 }; 218 /// The last buffer read from one of ``channel_datas_`` but not yet encoded 219 /// and sent to ``io_channel_`. 220 std::optional<OutgoingBuffer> buffer_to_encode_and_send_; 221 222 /// The next index of ``channel_datas_`` to read an outgoing packet from. 223 /// 224 /// This is used to provide fairness between the channel outputs. 225 size_t next_first_read_index_ = 0; 226 }; 227 228 } // namespace pw::hdlc 229