blob: 3004891a304511d26d5a9dc3403c6e69d3564c38 [file] [log] [blame]
Alexei Frolovd3e5cb72021-01-08 13:08:45 -08001.. _module-pw_hdlc:
Wyatt Hepler455b4922020-09-18 00:19:21 -07002
Alexei Frolovd3e5cb72021-01-08 13:08:45 -08003-------
4pw_hdlc
5-------
Wyatt Hepler455b4922020-09-18 00:19:21 -07006`High-Level Data Link Control (HDLC)
7<https://en.wikipedia.org/wiki/High-Level_Data_Link_Control>`_ is a data link
8layer protocol intended for serial communication between devices. HDLC is
9standardized as `ISO/IEC 13239:2002 <https://www.iso.org/standard/37010.html>`_.
shaneajg0869f2c2020-07-08 10:39:14 -040010
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080011The ``pw_hdlc`` module provides a simple, robust frame-oriented transport that
12uses a subset of the HDLC protocol. ``pw_hdlc`` supports sending between
13embedded devices or the host. It can be used with :ref:`module-pw_rpc` to enable
14remote procedure calls (RPCs) on embedded on devices.
shaneajg0869f2c2020-07-08 10:39:14 -040015
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080016**Why use the pw_hdlc module?**
shaneajgf8325f92020-08-05 12:35:10 -040017
Wyatt Hepler455b4922020-09-18 00:19:21 -070018 * Enables the transmission of RPCs and other data between devices over serial.
19 * Detects corruption and data loss.
20 * Light-weight, simple, and easy to use.
21 * Supports streaming to transport without buffering, since the length is not
22 encoded.
shaneajgf8325f92020-08-05 12:35:10 -040023
Wyatt Hepler455b4922020-09-18 00:19:21 -070024.. admonition:: Try it out!
shaneajgf8325f92020-08-05 12:35:10 -040025
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -070026 For an example of how to use HDLC with :ref:`module-pw_rpc`, see the
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080027 :ref:`module-pw_hdlc-rpc-example`.
shaneajgf8325f92020-08-05 12:35:10 -040028
Wyatt Hepler455b4922020-09-18 00:19:21 -070029.. toctree::
30 :maxdepth: 1
31 :hidden:
shaneajgf8325f92020-08-05 12:35:10 -040032
Wyatt Hepler455b4922020-09-18 00:19:21 -070033 rpc_example/docs
shaneajgf8325f92020-08-05 12:35:10 -040034
35Protocol Description
36====================
37
Wyatt Hepler455b4922020-09-18 00:19:21 -070038Frames
39------
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080040The HDLC implementation in ``pw_hdlc`` supports only HDLC unnumbered
Alexei Frolov6053c312020-12-09 22:43:55 -080041information frames. These frames are encoded as follows:
Wyatt Hepler455b4922020-09-18 00:19:21 -070042
43.. code-block:: text
44
45 _________________________________________
46 | | | | | | |...
47 | | | | | | |... [More frames]
48 |_|_|_|__________________________|____|_|...
49 F A C Payload FCS F
50
51 F = flag byte (0x7e, the ~ character)
52 A = address field
53 C = control field
54 FCS = frame check sequence (CRC-32)
55
56
shaneajgf8325f92020-08-05 12:35:10 -040057Encoding and sending data
58-------------------------
59This module first writes an initial frame delimiter byte (0x7E) to indicate the
60beginning of the frame. Before sending any of the payload data through serial,
Wyatt Hepler455b4922020-09-18 00:19:21 -070061the special bytes are escaped:
shaneajgf8325f92020-08-05 12:35:10 -040062
Wyatt Hepler455b4922020-09-18 00:19:21 -070063 +-------------------------+-----------------------+
64 | Unescaped Special Bytes | Escaped Special Bytes |
65 +=========================+=======================+
66 | 7E | 7D 5E |
67 +-------------------------+-----------------------+
68 | 7D | 7D 5D |
69 +-------------------------+-----------------------+
shaneajgf8325f92020-08-05 12:35:10 -040070
71The bytes of the payload are escaped and written in a single pass. The
Wyatt Hepler455b4922020-09-18 00:19:21 -070072frame check sequence is calculated, escaped, and written after. After this, a
73final frame delimiter byte (0x7E) is written to mark the end of the frame.
shaneajgf8325f92020-08-05 12:35:10 -040074
75Decoding received bytes
76-----------------------
Wyatt Hepler455b4922020-09-18 00:19:21 -070077Frames may be received in multiple parts, so we need to store the received data
shaneajgf8325f92020-08-05 12:35:10 -040078in a buffer until the ending frame delimiter (0x7E) is read. When the
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080079``pw_hdlc`` decoder receives data, it unescapes it and adds it to a buffer.
Wyatt Hepler455b4922020-09-18 00:19:21 -070080When the frame is complete, it calculates and verifies the frame check sequence
81and does the following:
shaneajgf8325f92020-08-05 12:35:10 -040082
Wyatt Hepler455b4922020-09-18 00:19:21 -070083* If correctly verified, the decoder returns the decoded frame.
84* If the checksum verification fails, the frame is discarded and an error is
85 reported.
shaneajgf8325f92020-08-05 12:35:10 -040086
87API Usage
88=========
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080089There are two primary functions of the ``pw_hdlc`` module:
Wyatt Hepler455b4922020-09-18 00:19:21 -070090
91 * **Encoding** data by constructing a frame with the escaped payload bytes and
92 frame check sequence.
93 * **Decoding** data by unescaping the received bytes, verifying the frame
94 check sequence, and returning successfully decoded frames.
shaneajgf8325f92020-08-05 12:35:10 -040095
96Encoder
97-------
Wyatt Hepler455b4922020-09-18 00:19:21 -070098The Encoder API provides a single function that encodes data as an HDLC
Alexei Frolov6053c312020-12-09 22:43:55 -080099unnumbered information frame.
shaneajgf8325f92020-08-05 12:35:10 -0400100
101C++
102^^^
Wyatt Hepler455b4922020-09-18 00:19:21 -0700103.. cpp:namespace:: pw
104
Alexei Frolovb9fda582021-03-13 18:02:52 -0800105.. cpp:function:: Status hdlc::WriteUIFrame(uint64_t address, ConstByteSpan data, stream::Writer& writer)
Wyatt Hepler455b4922020-09-18 00:19:21 -0700106
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700107 Writes a span of data to a :ref:`pw::stream::Writer <module-pw_stream>` and
108 returns the status. This implementation uses the :ref:`module-pw_checksum`
Wyatt Hepler455b4922020-09-18 00:19:21 -0700109 module to compute the CRC-32 frame check sequence.
shaneajgf8325f92020-08-05 12:35:10 -0400110
111.. code-block:: cpp
112
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800113 #include "pw_hdlc/encoder.h"
114 #include "pw_hdlc/sys_io_stream.h"
shaneajgf8325f92020-08-05 12:35:10 -0400115
116 int main() {
Wyatt Hepler455b4922020-09-18 00:19:21 -0700117 pw::stream::SysIoWriter serial_writer;
Alexei Frolov6053c312020-12-09 22:43:55 -0800118 Status status = WriteUIFrame(123 /* address */,
119 data,
120 serial_writer);
Wyatt Hepler455b4922020-09-18 00:19:21 -0700121 if (!status.ok()) {
122 PW_LOG_INFO("Writing frame failed! %s", status.str());
123 }
shaneajgf8325f92020-08-05 12:35:10 -0400124 }
125
shaneajgf8325f92020-08-05 12:35:10 -0400126Python
127^^^^^^
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800128.. automodule:: pw_hdlc.encode
Wyatt Hepler455b4922020-09-18 00:19:21 -0700129 :members:
shaneajgf8325f92020-08-05 12:35:10 -0400130
131.. code-block:: python
132
133 import serial
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800134 from pw_hdlc import encode
shaneajgf8325f92020-08-05 12:35:10 -0400135
136 ser = serial.Serial()
Paul Mathieu94705a92021-05-13 15:07:41 -0700137 address = 123
138 ser.write(encode.ui_frame(address, b'your data here!'))
shaneajgf8325f92020-08-05 12:35:10 -0400139
Jared Weinstein7c658f62021-07-19 12:59:44 -0700140Typescript
141^^^^^^^^^^
142
Paul Mathieu47799392021-09-14 10:40:50 -0700143Encoder
144-------
145The Encoder class provides a way to build complete, escaped HDLC UI frames.
146
147.. js:method:: Encoder.uiFrame(address, data)
Jared Weinstein7c658f62021-07-19 12:59:44 -0700148
149 :param number address: frame address.
150 :param Uint8Array data: frame data.
151 :returns: Uint8Array containing a complete HDLC frame.
152
shaneajgf8325f92020-08-05 12:35:10 -0400153Decoder
154-------
Wyatt Hepler455b4922020-09-18 00:19:21 -0700155The decoder class unescapes received bytes and adds them to a buffer. Complete,
156valid HDLC frames are yielded as they are received.
shaneajgf8325f92020-08-05 12:35:10 -0400157
Paul Mathieu47799392021-09-14 10:40:50 -0700158.. js:method:: Decoder.process(bytes)
159
160 :param Uint8Array bytes: bytes received from the medium.
161 :yields: Frame complete frames.
162
shaneajgf8325f92020-08-05 12:35:10 -0400163C++
164^^^
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800165.. cpp:class:: pw::hdlc::Decoder
shaneajgf8325f92020-08-05 12:35:10 -0400166
Wyatt Hepler455b4922020-09-18 00:19:21 -0700167 .. cpp:function:: pw::Result<Frame> Process(std::byte b)
shaneajgf8325f92020-08-05 12:35:10 -0400168
Wyatt Hepler455b4922020-09-18 00:19:21 -0700169 Parses a single byte of an HDLC stream. Returns a Result with the complete
170 frame if the byte completes a frame. The status is the following:
171
172 - OK - A frame was successfully decoded. The Result contains the Frame,
173 which is invalidated by the next Process call.
174 - UNAVAILABLE - No frame is available.
175 - RESOURCE_EXHAUSTED - A frame completed, but it was too large to fit in
176 the decoder's buffer.
177 - DATA_LOSS - A frame completed, but it was invalid. The frame was
178 incomplete or the frame check sequence verification failed.
179
180 .. cpp:function:: void Process(pw::ConstByteSpan data, F&& callback, Args&&... args)
181
182 Processes a span of data and calls the provided callback with each frame or
183 error.
184
185This example demonstrates reading individual bytes from ``pw::sys_io`` and
186decoding HDLC frames:
shaneajgf8325f92020-08-05 12:35:10 -0400187
188.. code-block:: cpp
189
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800190 #include "pw_hdlc/decoder.h"
shaneajgf8325f92020-08-05 12:35:10 -0400191 #include "pw_sys_io/sys_io.h"
192
193 int main() {
Wyatt Hepler455b4922020-09-18 00:19:21 -0700194 std::byte data;
shaneajgf8325f92020-08-05 12:35:10 -0400195 while (true) {
196 if (!pw::sys_io::ReadByte(&data).ok()) {
197 // Log serial reading error
198 }
Wyatt Hepler455b4922020-09-18 00:19:21 -0700199 Result<Frame> decoded_frame = decoder.Process(data);
shaneajgf8325f92020-08-05 12:35:10 -0400200
Wyatt Hepler455b4922020-09-18 00:19:21 -0700201 if (decoded_frame.ok()) {
202 // Handle the decoded frame
shaneajgf8325f92020-08-05 12:35:10 -0400203 }
204 }
205 }
206
207Python
208^^^^^^
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800209.. autoclass:: pw_hdlc.decode.FrameDecoder
Wyatt Hepler455b4922020-09-18 00:19:21 -0700210 :members:
shaneajgf8325f92020-08-05 12:35:10 -0400211
Wyatt Hepler455b4922020-09-18 00:19:21 -0700212Below is an example using the decoder class to decode data read from serial:
shaneajgf8325f92020-08-05 12:35:10 -0400213
214.. code-block:: python
215
216 import serial
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800217 from pw_hdlc import decode
shaneajgf8325f92020-08-05 12:35:10 -0400218
219 ser = serial.Serial()
Wyatt Hepler455b4922020-09-18 00:19:21 -0700220 decoder = decode.FrameDecoder()
shaneajgf8325f92020-08-05 12:35:10 -0400221
Wyatt Hepler455b4922020-09-18 00:19:21 -0700222 while True:
223 for frame in decoder.process_valid_frames(ser.read()):
224 # Handle the decoded frame
shaneajgf8325f92020-08-05 12:35:10 -0400225
Jared Weinstein23958102021-07-29 12:46:12 -0700226Typescript
227^^^^^^^^^^
228
229Decodes one or more HDLC frames from a stream of data.
230
231.. js:method:: process(data)
232
233 :param Uint8Array data: bytes to be decoded.
234 :yields: HDLC frames, including corrupt frames.
235 The Frame.ok() method whether the frame is valid.
236
237.. js:method:: processValidFrames(data)
238
239 :param Uint8Array data: bytes to be decoded.
240 :yields: Valid HDLC frames, logging any errors.
241
Wyatt Hepler455b4922020-09-18 00:19:21 -0700242Additional features
243===================
shaneajgf8325f92020-08-05 12:35:10 -0400244
Wyatt Hepler455b4922020-09-18 00:19:21 -0700245pw::stream::SysIoWriter
shaneajg0869f2c2020-07-08 10:39:14 -0400246------------------------
Wyatt Hepler455b4922020-09-18 00:19:21 -0700247The ``SysIoWriter`` C++ class implements the ``Writer`` interface with
248``pw::sys_io``. This Writer may be used by the C++ encoder to send HDLC frames
249over serial.
shaneajg0869f2c2020-07-08 10:39:14 -0400250
Wyatt Hepler455b4922020-09-18 00:19:21 -0700251HdlcRpcClient
252-------------
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800253.. autoclass:: pw_hdlc.rpc.HdlcRpcClient
Wyatt Hepler455b4922020-09-18 00:19:21 -0700254 :members:
shaneajg0869f2c2020-07-08 10:39:14 -0400255
Wyatt Heplere04d4682021-07-21 08:52:28 -0700256.. autoclass:: pw_hdlc.rpc.HdlcRpcLocalServerAndClient
257 :members:
258
Wyatt Hepler455b4922020-09-18 00:19:21 -0700259Roadmap
260=======
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800261- **Expanded protocol support** - ``pw_hdlc`` currently only supports
Alexei Frolovb9fda582021-03-13 18:02:52 -0800262 unnumbered information frames. Support for different frame types and
263 extended control fields may be added in the future.
shaneajgf8325f92020-08-05 12:35:10 -0400264
265- **Higher performance** - We plan to improve the overall performance of the
266 decoder and encoder implementations by using SIMD/NEON.
Wyatt Hepler455b4922020-09-18 00:19:21 -0700267
268Compatibility
269=============
270C++17
Yuval Peressb8f3ad22021-10-26 22:55:27 -0600271
272Zephyr
273======
274To enable ``pw_hdlc`` for Zephyr add ``CONFIG_PIGWEED_HDLC=y`` to the project's
275configuration.