xref: /aosp_15_r20/external/pigweed/pw_hdlc/public/pw_hdlc/router.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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