xref: /aosp_15_r20/external/pigweed/pw_rpc_transport/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker.. _module-pw_rpc_transport:
2*61c4878aSAndroid Build Coastguard Worker
3*61c4878aSAndroid Build Coastguard Worker================
4*61c4878aSAndroid Build Coastguard Workerpw_rpc_transport
5*61c4878aSAndroid Build Coastguard Worker================
6*61c4878aSAndroid Build Coastguard WorkerThe ``pw_rpc_transport`` provides a transport layer for ``pw_rpc``.
7*61c4878aSAndroid Build Coastguard Worker
8*61c4878aSAndroid Build Coastguard Worker.. warning::
9*61c4878aSAndroid Build Coastguard Worker  This is an experimental module currently under development. APIs and
10*61c4878aSAndroid Build Coastguard Worker  functionality may change at any time.
11*61c4878aSAndroid Build Coastguard Worker
12*61c4878aSAndroid Build Coastguard Worker``pw_rpc`` provides a system for defining and invoking remote procedure calls
13*61c4878aSAndroid Build Coastguard Worker(RPCs) on a device. It does not include any transports for sending these RPC
14*61c4878aSAndroid Build Coastguard Workercalls. On a real device there could be multiple ways of inter-process and/or
15*61c4878aSAndroid Build Coastguard Workerinter-core communication: hardware mailboxes, shared memory, network sockets,
16*61c4878aSAndroid Build Coastguard WorkerUnix domain sockets. ``pw_rpc_transport`` provides means to implement various
17*61c4878aSAndroid Build Coastguard Workertransports and integrate them with ``pw_rpc`` services.
18*61c4878aSAndroid Build Coastguard Worker
19*61c4878aSAndroid Build Coastguard Worker``pw_rpc_transport`` relies on the assumption that a ``pw_rpc`` channel ID
20*61c4878aSAndroid Build Coastguard Workeruniquely identifies both sides of an RPC conversation. It allows developers to
21*61c4878aSAndroid Build Coastguard Workerdefine transports, egresses and ingresses for various channel IDs and choose
22*61c4878aSAndroid Build Coastguard Workerwhat framing will be used to send RPC packets over those transports.
23*61c4878aSAndroid Build Coastguard Worker
24*61c4878aSAndroid Build Coastguard WorkerRpcFrame
25*61c4878aSAndroid Build Coastguard Worker--------
26*61c4878aSAndroid Build Coastguard WorkerFramed RPC data ready to be sent via ``RpcFrameSender``. Consists of a header
27*61c4878aSAndroid Build Coastguard Workerand a payload. Some RPC transport encodings may not require a header and put
28*61c4878aSAndroid Build Coastguard Workerall of the framed data into the payload (in which case the header can be
29*61c4878aSAndroid Build Coastguard Workeran empty span).
30*61c4878aSAndroid Build Coastguard Worker
31*61c4878aSAndroid Build Coastguard WorkerA single RPC packet can be split into multiple ``RpcFrame``'s depending on the
32*61c4878aSAndroid Build Coastguard WorkerMTU of the transport.
33*61c4878aSAndroid Build Coastguard Worker
34*61c4878aSAndroid Build Coastguard WorkerAll frames for an RPC packet are expected to be sent and received in order
35*61c4878aSAndroid Build Coastguard Workerwithout being interleaved by other packets' frames.
36*61c4878aSAndroid Build Coastguard Worker
37*61c4878aSAndroid Build Coastguard WorkerRpcFrameSender
38*61c4878aSAndroid Build Coastguard Worker--------------
39*61c4878aSAndroid Build Coastguard WorkerSends RPC frames over some communication channel (e.g. a hardware mailbox,
40*61c4878aSAndroid Build Coastguard Workershared memory, or a socket). It exposes its MTU size and generally only knows
41*61c4878aSAndroid Build Coastguard Workerhow to send an ``RpcFrame`` of a size that doesn't exceed that MTU.
42*61c4878aSAndroid Build Coastguard Worker
43*61c4878aSAndroid Build Coastguard WorkerRpcPacketEncoder / RpcPacketDecoder
44*61c4878aSAndroid Build Coastguard Worker-----------------------------------
45*61c4878aSAndroid Build Coastguard Worker``RpcPacketEncoder`` is used to split and frame an RPC packet.
46*61c4878aSAndroid Build Coastguard Worker``RpcPacketDecoder`` then does the opposite e.g. stitches together received
47*61c4878aSAndroid Build Coastguard Workerframes and removes any framing added by the encoder.
48*61c4878aSAndroid Build Coastguard Worker
49*61c4878aSAndroid Build Coastguard WorkerRpcEgressHandler
50*61c4878aSAndroid Build Coastguard Worker----------------
51*61c4878aSAndroid Build Coastguard WorkerProvides means of sending an RPC packet to its destination. Typically it ties
52*61c4878aSAndroid Build Coastguard Workertogether an ``RpcPacketEncoder`` and ``RpcFrameSender``.
53*61c4878aSAndroid Build Coastguard Worker
54*61c4878aSAndroid Build Coastguard WorkerRpcIngressHandler
55*61c4878aSAndroid Build Coastguard Worker-----------------
56*61c4878aSAndroid Build Coastguard WorkerProvides means of receiving RPC packets over some transport. Typically it has
57*61c4878aSAndroid Build Coastguard Workerlogic for reading RPC frames from some transport (a network connection,
58*61c4878aSAndroid Build Coastguard Workershared memory, or a hardware mailbox), stitching and decoding them with
59*61c4878aSAndroid Build Coastguard Worker``RpcPacketDecoder`` and passing full RPC packets to their intended processor
60*61c4878aSAndroid Build Coastguard Workervia ``RpcPacketProcessor``.
61*61c4878aSAndroid Build Coastguard Worker
62*61c4878aSAndroid Build Coastguard WorkerRpcPacketProcessor
63*61c4878aSAndroid Build Coastguard Worker------------------
64*61c4878aSAndroid Build Coastguard WorkerUsed by ``RpcIngressHandler`` to send the received RPC packet to its intended
65*61c4878aSAndroid Build Coastguard Workerhandler (e.g. a pw_rpc ``Service``).
66*61c4878aSAndroid Build Coastguard Worker
67*61c4878aSAndroid Build Coastguard Worker--------------------
68*61c4878aSAndroid Build Coastguard WorkerCreating a transport
69*61c4878aSAndroid Build Coastguard Worker--------------------
70*61c4878aSAndroid Build Coastguard WorkerRPC transports implement ``pw::rpc::RpcFrameSender``. The transport exposes its
71*61c4878aSAndroid Build Coastguard Workermaximum transmission unit (MTU) and only knows how to send packets of up to the
72*61c4878aSAndroid Build Coastguard Workersize of that MTU.
73*61c4878aSAndroid Build Coastguard Worker
74*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
75*61c4878aSAndroid Build Coastguard Worker
76*61c4878aSAndroid Build Coastguard Worker   class MyRpcTransport : public RpcFrameSender {
77*61c4878aSAndroid Build Coastguard Worker   public:
78*61c4878aSAndroid Build Coastguard Worker     size_t mtu() const override { return 128; }
79*61c4878aSAndroid Build Coastguard Worker
80*61c4878aSAndroid Build Coastguard Worker     Status Send(RpcFrame frame) override {
81*61c4878aSAndroid Build Coastguard Worker       // Send the frame via mailbox, shared memory or some other mechanism...
82*61c4878aSAndroid Build Coastguard Worker     }
83*61c4878aSAndroid Build Coastguard Worker   };
84*61c4878aSAndroid Build Coastguard Worker
85*61c4878aSAndroid Build Coastguard Worker--------------------------
86*61c4878aSAndroid Build Coastguard WorkerIntegration with pw_stream
87*61c4878aSAndroid Build Coastguard Worker--------------------------
88*61c4878aSAndroid Build Coastguard WorkerAn RpcFrameSender implementaion that wraps a ``pw::stream::Writer`` is provided
89*61c4878aSAndroid Build Coastguard Workerby ``pw::rpc::StreamRpcFrameSender``. As the stream interface doesn't know
90*61c4878aSAndroid Build Coastguard Workerabout MTU's, it's up to the user to select one.
91*61c4878aSAndroid Build Coastguard Worker
92*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
93*61c4878aSAndroid Build Coastguard Worker
94*61c4878aSAndroid Build Coastguard Worker   stream::SysIoWriter writer;
95*61c4878aSAndroid Build Coastguard Worker   StreamRpcFrameSender<kMtu> sender(writer);
96*61c4878aSAndroid Build Coastguard Worker
97*61c4878aSAndroid Build Coastguard WorkerA thread to feed data to a ``pw::rpc::RpcIngressHandler`` from a
98*61c4878aSAndroid Build Coastguard Worker``pw::stream::Reader`` is provided by ``pw::rpc::StreamRpcDispatcher``.
99*61c4878aSAndroid Build Coastguard Worker
100*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
101*61c4878aSAndroid Build Coastguard Worker
102*61c4878aSAndroid Build Coastguard Worker   rpc::HdlcRpcIngress<kMaxRpcPacketSize> hdlc_ingress(...);
103*61c4878aSAndroid Build Coastguard Worker   stream::SysIoReader reader;
104*61c4878aSAndroid Build Coastguard Worker
105*61c4878aSAndroid Build Coastguard Worker   // Feed Hdlc ingress with bytes from sysio.
106*61c4878aSAndroid Build Coastguard Worker   rpc::StreamRpcDispatcher<kMaxSysioRead> sysio_dispatcher(reader,
107*61c4878aSAndroid Build Coastguard Worker                                                            hdlc_ingress);
108*61c4878aSAndroid Build Coastguard Worker
109*61c4878aSAndroid Build Coastguard Worker   thread::DetachedThread(SysioDispatcherThreadOptions(),
110*61c4878aSAndroid Build Coastguard Worker                          sysio_dispatcher);
111*61c4878aSAndroid Build Coastguard Worker
112*61c4878aSAndroid Build Coastguard Worker-------------------------------------------
113*61c4878aSAndroid Build Coastguard WorkerUsing transports: a sample three-node setup
114*61c4878aSAndroid Build Coastguard Worker-------------------------------------------
115*61c4878aSAndroid Build Coastguard Worker
116*61c4878aSAndroid Build Coastguard WorkerA transport must be properly registered in order for ``pw_rpc`` to correctly
117*61c4878aSAndroid Build Coastguard Workerroute its packets. Below is an example of using a ``SocketRpcTransport`` and
118*61c4878aSAndroid Build Coastguard Workera (hypothetical) ``SharedMemoryRpcTransport`` to set up RPC connectivity between
119*61c4878aSAndroid Build Coastguard Workerthree endpoints.
120*61c4878aSAndroid Build Coastguard Worker
121*61c4878aSAndroid Build Coastguard WorkerNode A runs ``pw_rpc`` clients who want to talk to nodes B and C using
122*61c4878aSAndroid Build Coastguard Worker``kChannelAB`` and ``kChannelAC`` respectively. However there is no direct
123*61c4878aSAndroid Build Coastguard Workerconnectivity from A to C: only B can talk to C over shared memory while A can
124*61c4878aSAndroid Build Coastguard Workertalk to B over a socket connection. Also, some services on A are self-hosted
125*61c4878aSAndroid Build Coastguard Workerand accessed from the same process on ``kChannelAA``:
126*61c4878aSAndroid Build Coastguard Worker
127*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
128*61c4878aSAndroid Build Coastguard Worker
129*61c4878aSAndroid Build Coastguard Worker   // Set up A->B transport over a network socket where B is a server
130*61c4878aSAndroid Build Coastguard Worker   // and A is a client.
131*61c4878aSAndroid Build Coastguard Worker   SocketRpcTransport<kSocketReadBufferSize> a_to_b_transport(
132*61c4878aSAndroid Build Coastguard Worker     SocketRpcTransport<kSocketReadBufferSize>::kAsClient, "localhost",
133*61c4878aSAndroid Build Coastguard Worker     kNodeBPortNumber);
134*61c4878aSAndroid Build Coastguard Worker
135*61c4878aSAndroid Build Coastguard Worker   // LocalRpcEgress handles RPC packets received from other nodes and destined
136*61c4878aSAndroid Build Coastguard Worker   // to this node.
137*61c4878aSAndroid Build Coastguard Worker   LocalRpcEgress<kLocalEgressQueueSize, kMaxPacketSize> local_egress;
138*61c4878aSAndroid Build Coastguard Worker   // HdlcRpcEgress applies HDLC framing to all packets outgoing over the A->B
139*61c4878aSAndroid Build Coastguard Worker   // transport.
140*61c4878aSAndroid Build Coastguard Worker   HdlcRpcEgress<kMaxPacketSize> a_to_b_egress("a->b", a_to_b_transport);
141*61c4878aSAndroid Build Coastguard Worker
142*61c4878aSAndroid Build Coastguard Worker   // List of channels for all packets originated locally at A.
143*61c4878aSAndroid Build Coastguard Worker   std::array tx_channels = {
144*61c4878aSAndroid Build Coastguard Worker     // Self-destined packets go directly to local egress.
145*61c4878aSAndroid Build Coastguard Worker     Channel::Create<kChannelAA>(&local_egress),
146*61c4878aSAndroid Build Coastguard Worker     // Packets to B and C go over A->B transport.
147*61c4878aSAndroid Build Coastguard Worker     Channel::Create<kChannelAB>(&a_to_b_egress),
148*61c4878aSAndroid Build Coastguard Worker     Channel::Create<kChannelAC>(&a_to_b_egress),
149*61c4878aSAndroid Build Coastguard Worker   };
150*61c4878aSAndroid Build Coastguard Worker
151*61c4878aSAndroid Build Coastguard Worker   // Here we list all egresses for the packets _incoming_ from B.
152*61c4878aSAndroid Build Coastguard Worker   std::array b_rx_channels = {
153*61c4878aSAndroid Build Coastguard Worker     // Packets on both AB and AC channels are destined locally; hence sending
154*61c4878aSAndroid Build Coastguard Worker     // to the local egress.
155*61c4878aSAndroid Build Coastguard Worker     ChannelEgress{kChannelAB, local_egress},
156*61c4878aSAndroid Build Coastguard Worker     ChannelEgress{kChannelAC, local_egress},
157*61c4878aSAndroid Build Coastguard Worker   };
158*61c4878aSAndroid Build Coastguard Worker
159*61c4878aSAndroid Build Coastguard Worker   // HdlcRpcIngress complements HdlcRpcEgress: all packets received on
160*61c4878aSAndroid Build Coastguard Worker   // `b_rx_channels` are assumed to have HDLC framing.
161*61c4878aSAndroid Build Coastguard Worker   HdlcRpcIngress<kMaxPacketSize> b_ingress(b_rx_channels);
162*61c4878aSAndroid Build Coastguard Worker
163*61c4878aSAndroid Build Coastguard Worker   // Local egress needs to know how to send received packets to their target
164*61c4878aSAndroid Build Coastguard Worker   // pw_rpc service.
165*61c4878aSAndroid Build Coastguard Worker   ServiceRegistry registry(tx_channels);
166*61c4878aSAndroid Build Coastguard Worker   local_egress.set_packet_processor(registry);
167*61c4878aSAndroid Build Coastguard Worker   // Socket transport needs to be aware of what ingress it's handling.
168*61c4878aSAndroid Build Coastguard Worker   a_to_b_transport.set_ingress(b_ingress);
169*61c4878aSAndroid Build Coastguard Worker
170*61c4878aSAndroid Build Coastguard Worker   // Both RpcSocketTransport and LocalRpcEgress are ThreadCore's and
171*61c4878aSAndroid Build Coastguard Worker   // need to be started in order for packet processing to start.
172*61c4878aSAndroid Build Coastguard Worker   DetachedThread(/*...*/, a_to_b_transport);
173*61c4878aSAndroid Build Coastguard Worker   DetachedThread(/*...*/, local_egress);
174*61c4878aSAndroid Build Coastguard Worker
175*61c4878aSAndroid Build Coastguard WorkerNode B setup is the most complicated since it needs to deal with egress
176*61c4878aSAndroid Build Coastguard Workerand ingress from both A and B and needs to support two kinds of transports. Note
177*61c4878aSAndroid Build Coastguard Workerthat A is unaware of which transport and framing B is using when talking to C:
178*61c4878aSAndroid Build Coastguard Worker
179*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
180*61c4878aSAndroid Build Coastguard Worker
181*61c4878aSAndroid Build Coastguard Worker   // This is the server counterpart to A's client socket.
182*61c4878aSAndroid Build Coastguard Worker   SocketRpcTransport<kSocketReadBufferSize> b_to_a_transport(
183*61c4878aSAndroid Build Coastguard Worker     SocketRpcTransport<kSocketReadBufferSize>::kAsServer, "localhost",
184*61c4878aSAndroid Build Coastguard Worker     kNodeBPortNumber);
185*61c4878aSAndroid Build Coastguard Worker
186*61c4878aSAndroid Build Coastguard Worker   SharedMemoryRpcTransport b_to_c_transport(/*...*/);
187*61c4878aSAndroid Build Coastguard Worker
188*61c4878aSAndroid Build Coastguard Worker   LocalRpcEgress<kLocalEgressQueueSize, kMaxPacketSize> local_egress;
189*61c4878aSAndroid Build Coastguard Worker   HdlcRpcEgress<kMaxPacketSize> b_to_a_egress("b->a", b_to_a_transport);
190*61c4878aSAndroid Build Coastguard Worker   // SimpleRpcEgress applies a very simple length-prefixed framing to B->C
191*61c4878aSAndroid Build Coastguard Worker   // traffic (because HDLC adds unnecessary overhead over shared memory).
192*61c4878aSAndroid Build Coastguard Worker   SimpleRpcEgress<kMaxPacketSize> b_to_c_egress("b->c", b_to_c_transport);
193*61c4878aSAndroid Build Coastguard Worker
194*61c4878aSAndroid Build Coastguard Worker   // List of channels for all packets originated locally at B (note that in
195*61c4878aSAndroid Build Coastguard Worker   // this example B doesn't need to talk to C directly; it only proxies for A).
196*61c4878aSAndroid Build Coastguard Worker   std::array tx_channels = {
197*61c4878aSAndroid Build Coastguard Worker     Channel::Create<kChannelAB>(&b_to_a_egress),
198*61c4878aSAndroid Build Coastguard Worker   };
199*61c4878aSAndroid Build Coastguard Worker
200*61c4878aSAndroid Build Coastguard Worker   // Here we list all egresses for the packets _incoming_ from A.
201*61c4878aSAndroid Build Coastguard Worker   std::array a_rx_channels = {
202*61c4878aSAndroid Build Coastguard Worker     ChannelEgress{kChannelAB, local_egress},
203*61c4878aSAndroid Build Coastguard Worker     ChannelEgress{kChannelAC, b_to_c_egress},
204*61c4878aSAndroid Build Coastguard Worker   };
205*61c4878aSAndroid Build Coastguard Worker
206*61c4878aSAndroid Build Coastguard Worker   // Here we list all egresses for the packets _incoming_ from C.
207*61c4878aSAndroid Build Coastguard Worker   std::array c_rx_channels = {
208*61c4878aSAndroid Build Coastguard Worker     ChannelEgress{kChannelAC, b_to_a_egress},
209*61c4878aSAndroid Build Coastguard Worker   };
210*61c4878aSAndroid Build Coastguard Worker
211*61c4878aSAndroid Build Coastguard Worker   HdlcRpcIngress<kMaxPacketSize> b_ingress(b_rx_channels);
212*61c4878aSAndroid Build Coastguard Worker   SimpleRpcIngress<kMaxPacketSize> c_ingress(c_rx_channels);
213*61c4878aSAndroid Build Coastguard Worker
214*61c4878aSAndroid Build Coastguard Worker   ServiceRegistry registry(tx_channels);
215*61c4878aSAndroid Build Coastguard Worker   local_egress.set_packet_processor(registry);
216*61c4878aSAndroid Build Coastguard Worker
217*61c4878aSAndroid Build Coastguard Worker   b_to_a_transport.set_ingress(a_ingress);
218*61c4878aSAndroid Build Coastguard Worker   b_to_c_transport.set_ingress(c_ingress);
219*61c4878aSAndroid Build Coastguard Worker
220*61c4878aSAndroid Build Coastguard Worker   DetachedThread({}, b_to_a_transport);
221*61c4878aSAndroid Build Coastguard Worker   DetachedThread({}, b_to_c_transport);
222*61c4878aSAndroid Build Coastguard Worker   DetachedThread({}, local_egress);
223*61c4878aSAndroid Build Coastguard Worker
224*61c4878aSAndroid Build Coastguard WorkerNode C setup is straightforward since it only needs to handle ingress from B:
225*61c4878aSAndroid Build Coastguard Worker
226*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
227*61c4878aSAndroid Build Coastguard Worker
228*61c4878aSAndroid Build Coastguard Worker   SharedMemoryRpcTransport c_to_b_transport(/*...*/);
229*61c4878aSAndroid Build Coastguard Worker   LocalRpcEgress<kLocalEgressQueueSize, kMaxPacketSize> local_egress;
230*61c4878aSAndroid Build Coastguard Worker   SimpleRpcEgress<kMaxPacketSize> c_to_b_egress("c->b", c_to_b_transport);
231*61c4878aSAndroid Build Coastguard Worker
232*61c4878aSAndroid Build Coastguard Worker   std::array tx_channels = {
233*61c4878aSAndroid Build Coastguard Worker     Channel::Create<kChannelAC>(&c_to_b_egress),
234*61c4878aSAndroid Build Coastguard Worker   };
235*61c4878aSAndroid Build Coastguard Worker
236*61c4878aSAndroid Build Coastguard Worker   // Here we list all egresses for the packets _incoming_ from B.
237*61c4878aSAndroid Build Coastguard Worker   std::array b_rx_channels = {
238*61c4878aSAndroid Build Coastguard Worker     ChannelEgress{kChannelAC, local_egress},
239*61c4878aSAndroid Build Coastguard Worker   };
240*61c4878aSAndroid Build Coastguard Worker
241*61c4878aSAndroid Build Coastguard Worker   SimpleRpcIngress<kMaxPacketSize> b_ingress(b_rx_channels);
242*61c4878aSAndroid Build Coastguard Worker
243*61c4878aSAndroid Build Coastguard Worker   ServiceRegistry registry(tx_channels);
244*61c4878aSAndroid Build Coastguard Worker   local_egress.set_packet_processor(registry);
245*61c4878aSAndroid Build Coastguard Worker
246*61c4878aSAndroid Build Coastguard Worker   c_to_b_transport.set_ingress(b_ingress);
247*61c4878aSAndroid Build Coastguard Worker
248*61c4878aSAndroid Build Coastguard Worker   DetachedThread(/*...*/, c_to_b_transport);
249*61c4878aSAndroid Build Coastguard Worker   DetachedThread(/*...*/, local_egress);
250