pw_hdlc_lite: Switch to U-frames for unreliable transmission

This adds functions to write unnumbered information frames to the C++
and Python HDLC encoders, and switches over the RPC channel and host
client to send them instead of I-frames.

Change-Id: I845d7b8746a5f910fd390ed1d4f4ea33426235eb
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/27220
Reviewed-by: Wyatt Hepler <hepler@google.com>
diff --git a/pw_hdlc_lite/docs.rst b/pw_hdlc_lite/docs.rst
index e42d917..606ebbf 100644
--- a/pw_hdlc_lite/docs.rst
+++ b/pw_hdlc_lite/docs.rst
@@ -38,8 +38,8 @@
 
 Frames
 ------
-The HDLC implementation in ``pw_hdlc_lite`` supports only HDLC information
-frames. These frames are encoded as follows:
+The HDLC implementation in ``pw_hdlc_lite`` supports only HDLC unnumbered
+information frames. These frames are encoded as follows:
 
 .. code-block:: text
 
@@ -97,13 +97,13 @@
 Encoder
 -------
 The Encoder API provides a single function that encodes data as an HDLC
-information frame.
+unnumbered information frame.
 
 C++
 ^^^
 .. cpp:namespace:: pw
 
-.. cpp:function:: Status hdlc_lite::WriteInformationFrame(uint8_t address, ConstByteSpan data, stream::Writer& writer)
+.. cpp:function:: Status hdlc_lite::WriteUIFrame(uint8_t address, ConstByteSpan data, stream::Writer& writer)
 
   Writes a span of data to a :ref:`pw::stream::Writer <module-pw_stream>` and
   returns the status. This implementation uses the :ref:`module-pw_checksum`
@@ -116,9 +116,9 @@
 
   int main() {
     pw::stream::SysIoWriter serial_writer;
-    Status status = WriteInformationFrame(123 /* address */,
-                                          data,
-                                          serial_writer);
+    Status status = WriteUIFrame(123 /* address */,
+                                 data,
+                                 serial_writer);
     if (!status.ok()) {
       PW_LOG_INFO("Writing frame failed! %s", status.str());
     }
@@ -135,7 +135,7 @@
   from pw_hdlc_lite import encode
 
   ser = serial.Serial()
-  ser.write(encode.information_frame(b'your data here!'))
+  ser.write(encode.ui_frame(b'your data here!'))
 
 Decoder
 -------
@@ -222,9 +222,9 @@
 Roadmap
 =======
 - **Expanded protocol support** - ``pw_hdlc_lite`` currently only supports
-  information frames with a single address byte and control byte. Support for
-  different frame types and extended address or control fields may be added in
-  the future.
+  unnumbered information frames with a single address byte and control byte.
+  Support for different frame types and extended address or control fields may
+  be added in the future.
 
 - **Higher performance** - We plan to improve the overall performance of the
   decoder and encoder implementations by using SIMD/NEON.
diff --git a/pw_hdlc_lite/encoder.cc b/pw_hdlc_lite/encoder.cc
index 4a785ee..01b73f6 100644
--- a/pw_hdlc_lite/encoder.cc
+++ b/pw_hdlc_lite/encoder.cc
@@ -49,7 +49,11 @@
 
   // Writes the header for an I-frame. After successfully calling
   // StartInformationFrame, WriteData may be called any number of times.
-  Status StartInformationFrame(uint8_t address);
+  [[maybe_unused]] Status StartInformationFrame(uint8_t address);
+
+  // Writes the header for an U-frame. After successfully calling
+  // StartUnnumberedFrame, WriteData may be called any number of times.
+  Status StartUnnumberedFrame(uint8_t address);
 
   // Writes data for an ongoing frame. Must only be called after a successful
   // StartInformationFrame call, and prior to a FinishFrame() call.
@@ -69,7 +73,19 @@
     return status;
   }
 
-  const byte address_and_control[] = {std::byte{address}, kUnusedControl};
+  const byte address_and_control[] = {
+      std::byte{address}, kUnusedControl, kUnusedControl};
+  return WriteData(address_and_control);
+}
+
+Status Encoder::StartUnnumberedFrame(uint8_t address) {
+  fcs_.clear();
+  if (Status status = writer_.Write(kFlag); !status.ok()) {
+    return status;
+  }
+
+  const byte address_and_control[] = {
+      std::byte{address}, UFrameControl::UnnumberedInformation().data()};
   return WriteData(address_and_control);
 }
 
@@ -103,12 +119,12 @@
 
 }  // namespace
 
-Status WriteInformationFrame(uint8_t address,
-                             ConstByteSpan payload,
-                             stream::Writer& writer) {
+Status WriteUIFrame(uint8_t address,
+                    ConstByteSpan payload,
+                    stream::Writer& writer) {
   Encoder encoder(writer);
 
-  if (Status status = encoder.StartInformationFrame(address); !status.ok()) {
+  if (Status status = encoder.StartUnnumberedFrame(address); !status.ok()) {
     return status;
   }
   if (Status status = encoder.WriteData(payload); !status.ok()) {
diff --git a/pw_hdlc_lite/encoder_test.cc b/pw_hdlc_lite/encoder_test.cc
index 72a66ce..7c03bb5 100644
--- a/pw_hdlc_lite/encoder_test.cc
+++ b/pw_hdlc_lite/encoder_test.cc
@@ -29,15 +29,6 @@
 namespace {
 
 constexpr uint8_t kAddress = 0x7B;  // 123
-constexpr byte kControl = byte{0};
-
-class WriteInfoFrame : public ::testing::Test {
- protected:
-  WriteInfoFrame() : writer_(buffer_) {}
-
-  stream::MemoryWriter writer_;
-  std::array<byte, 32> buffer_;
-};
 
 #define EXPECT_ENCODER_WROTE(...)                                           \
   do {                                                                      \
@@ -49,121 +40,127 @@
         0);                                                                 \
   } while (0)
 
-TEST_F(WriteInfoFrame, EmptyPayload) {
-  ASSERT_EQ(Status::Ok(),
-            WriteInformationFrame(kAddress, std::span<byte>(), writer_));
-  EXPECT_ENCODER_WROTE(
-      bytes::Concat(kFlag, kAddress, kControl, uint32_t{0x8D12B2C2}, kFlag));
-}
+class WriteUnnumberedFrame : public ::testing::Test {
+ protected:
+  WriteUnnumberedFrame() : writer_(buffer_) {}
 
-TEST_F(WriteInfoFrame, OneBytePayload) {
-  ASSERT_EQ(Status::Ok(),
-            WriteInformationFrame(kAddress, bytes::String("A"), writer_));
+  stream::MemoryWriter writer_;
+  std::array<byte, 32> buffer_;
+};
+
+constexpr byte kUnnumberedControl = byte{0x3};
+
+TEST_F(WriteUnnumberedFrame, EmptyPayload) {
+  ASSERT_EQ(Status::Ok(), WriteUIFrame(kAddress, std::span<byte>(), writer_));
   EXPECT_ENCODER_WROTE(bytes::Concat(
-      kFlag, kAddress, kControl, 'A', uint32_t{0xA63E2FA5}, kFlag));
+      kFlag, kAddress, kUnnumberedControl, uint32_t{0x141BE378}, kFlag));
 }
 
-TEST_F(WriteInfoFrame, OneBytePayload_Escape0x7d) {
+TEST_F(WriteUnnumberedFrame, OneBytePayload) {
+  ASSERT_EQ(Status::Ok(), WriteUIFrame(kAddress, bytes::String("A"), writer_));
+  EXPECT_ENCODER_WROTE(bytes::Concat(
+      kFlag, kAddress, kUnnumberedControl, 'A', uint32_t{0x8D137C66}, kFlag));
+}
+
+TEST_F(WriteUnnumberedFrame, OneBytePayload_Escape0x7d) {
   ASSERT_EQ(Status::Ok(),
-            WriteInformationFrame(kAddress, bytes::Array<0x7d>(), writer_));
+            WriteUIFrame(kAddress, bytes::Array<0x7d>(), writer_));
   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
                                      kAddress,
-                                     kControl,
+                                     kUnnumberedControl,
                                      kEscape,
                                      byte{0x7d} ^ byte{0x20},
-                                     uint32_t{0x89515322},
+                                     uint32_t{0xA27C00E1},
                                      kFlag));
 }
 
-TEST_F(WriteInfoFrame, OneBytePayload_Escape0x7E) {
+TEST_F(WriteUnnumberedFrame, OneBytePayload_Escape0x7E) {
   ASSERT_EQ(Status::Ok(),
-            WriteInformationFrame(kAddress, bytes::Array<0x7e>(), writer_));
+            WriteUIFrame(kAddress, bytes::Array<0x7e>(), writer_));
   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
                                      kAddress,
-                                     kControl,
+                                     kUnnumberedControl,
                                      kEscape,
                                      byte{0x7e} ^ byte{0x20},
-                                     uint32_t{0x10580298},
+                                     uint32_t{0x3B75515B},
                                      kFlag));
 }
 
-TEST_F(WriteInfoFrame, AddressNeedsEscaping) {
-  ASSERT_EQ(Status::Ok(),
-            WriteInformationFrame(0x7d, bytes::String("A"), writer_));
-  EXPECT_ENCODER_WROTE(bytes::Concat(
-      kFlag, kEscape, byte{0x5d}, kControl, 'A', uint32_t{0xA2B35317}, kFlag));
+TEST_F(WriteUnnumberedFrame, AddressNeedsEscaping) {
+  ASSERT_EQ(Status::Ok(), WriteUIFrame(0x7d, bytes::String("A"), writer_));
+  EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
+                                     kEscape,
+                                     byte{0x5d},
+                                     kUnnumberedControl,
+                                     'A',
+                                     uint32_t{0x899E00D4},
+                                     kFlag));
 }
 
-TEST_F(WriteInfoFrame, Crc32NeedsEscaping) {
-  ASSERT_EQ(Status::Ok(),
-            WriteInformationFrame(kAddress, bytes::String("abcdefg"), writer_));
+TEST_F(WriteUnnumberedFrame, Crc32NeedsEscaping) {
+  ASSERT_EQ(Status::Ok(), WriteUIFrame(kAddress, bytes::String("a"), writer_));
 
-  // The CRC-32 is 0x38B9FC7E, so the 0x7E must be escaped.
-  constexpr auto expected_crc32 = bytes::Array<0x7d, 0x5e, 0xfc, 0xb9, 0x38>();
+  // The CRC-32 is 0xB67D5CAE, so the 0x7D must be escaped.
+  constexpr auto expected_crc32 = bytes::Array<0xae, 0x5c, 0x7d, 0x5d, 0xb6>();
   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
                                      kAddress,
-                                     kControl,
-                                     bytes::String("abcdefg"),
+                                     kUnnumberedControl,
+                                     bytes::String("a"),
                                      expected_crc32,
                                      kFlag));
 }
 
-TEST_F(WriteInfoFrame, MultiplePayloads) {
+TEST_F(WriteUnnumberedFrame, MultiplePayloads) {
   ASSERT_EQ(Status::Ok(),
-            WriteInformationFrame(kAddress, bytes::String("ABC"), writer_));
+            WriteUIFrame(kAddress, bytes::String("ABC"), writer_));
   ASSERT_EQ(Status::Ok(),
-            WriteInformationFrame(kAddress, bytes::String("DEF"), writer_));
+            WriteUIFrame(kAddress, bytes::String("DEF"), writer_));
   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
                                      kAddress,
-                                     kControl,
+                                     kUnnumberedControl,
                                      bytes::String("ABC"),
-                                     uint32_t{0x14E2FC99},
+                                     uint32_t{0x06575377},
                                      kFlag,
                                      kFlag,
                                      kAddress,
-                                     kControl,
+                                     kUnnumberedControl,
                                      bytes::String("DEF"),
-                                     uint32_t{0x2D025C3A},
+                                     uint32_t{0x3FB7F3D4},
                                      kFlag));
 }
 
-TEST_F(WriteInfoFrame, PayloadWithNoEscapes) {
-  ASSERT_EQ(Status::Ok(),
-            WriteInformationFrame(
-                kAddress, bytes::String("123456789012345678901234"), writer_));
-
-  // Fill the memory writer's buffer.
-  ASSERT_EQ(writer_.bytes_written(), buffer_.size());
+TEST_F(WriteUnnumberedFrame, PayloadWithNoEscapes) {
+  ASSERT_EQ(
+      Status::Ok(),
+      WriteUIFrame(kAddress, bytes::String("1995 toyota corolla"), writer_));
 
   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
                                      kAddress,
-                                     kControl,
-                                     bytes::String("123456789012345678901234"),
-                                     uint32_t{0x50AA35EC},
+                                     kUnnumberedControl,
+                                     bytes::String("1995 toyota corolla"),
+                                     uint32_t{0x56560172},
                                      kFlag));
 }
 
-TEST_F(WriteInfoFrame, PayloadWithMultipleEscapes) {
-  ASSERT_EQ(Status::Ok(),
-            WriteInformationFrame(
-                kAddress,
-                bytes::Array<0x7E, 0x7B, 0x61, 0x62, 0x63, 0x7D, 0x7E>(),
-                writer_));
+TEST_F(WriteUnnumberedFrame, PayloadWithMultipleEscapes) {
+  ASSERT_EQ(
+      Status::Ok(),
+      WriteUIFrame(kAddress,
+                   bytes::Array<0x7E, 0x7B, 0x61, 0x62, 0x63, 0x7D, 0x7E>(),
+                   writer_));
   EXPECT_ENCODER_WROTE(bytes::Concat(
       kFlag,
       kAddress,
-      kControl,
+      kUnnumberedControl,
       bytes::
           Array<0x7D, 0x5E, 0x7B, 0x61, 0x62, 0x63, 0x7D, 0x5D, 0x7D, 0x5E>(),
-      uint32_t{0x1B8D505E},
+      uint32_t{0x950257BD},
       kFlag));
 }
 
-TEST_F(WriteInfoFrame, WriterError) {
+TEST_F(WriteUnnumberedFrame, WriterError) {
   constexpr auto data = bytes::Initialized<sizeof(buffer_)>(0x7e);
-
-  EXPECT_EQ(Status::ResourceExhausted(),
-            WriteInformationFrame(kAddress, data, writer_));
+  EXPECT_EQ(Status::ResourceExhausted(), WriteUIFrame(kAddress, data, writer_));
 }
 
 }  // namespace
diff --git a/pw_hdlc_lite/public/pw_hdlc_lite/encoder.h b/pw_hdlc_lite/public/pw_hdlc_lite/encoder.h
index ba2388c..485828d 100644
--- a/pw_hdlc_lite/public/pw_hdlc_lite/encoder.h
+++ b/pw_hdlc_lite/public/pw_hdlc_lite/encoder.h
@@ -19,18 +19,18 @@
 
 namespace pw::hdlc_lite {
 
-// Writes an HDLC information frame (I-frame) to the provided writer. The frame
-// contains the following:
+// Writes an HDLC unnumbered information frame (UI-frame) to the provided
+// writer. The frame contains the following:
 //
 //   - HDLC flag byte (0x7e)
 //   - Address
-//   - Control byte (fixed at 0; sequence numbers are not used currently).
+//   - UI-frame control byte
 //   - Data (0 or more bytes)
 //   - Frame check sequence (CRC-32)
 //   - HDLC flag byte (0x7e)
 //
-Status WriteInformationFrame(uint8_t address,
-                             ConstByteSpan data,
-                             stream::Writer& writer);
+Status WriteUIFrame(uint8_t address,
+                    ConstByteSpan payload,
+                    stream::Writer& writer);
 
 }  // namespace pw::hdlc_lite
diff --git a/pw_hdlc_lite/public/pw_hdlc_lite/rpc_channel.h b/pw_hdlc_lite/public/pw_hdlc_lite/rpc_channel.h
index 8289784..03ecd44 100644
--- a/pw_hdlc_lite/public/pw_hdlc_lite/rpc_channel.h
+++ b/pw_hdlc_lite/public/pw_hdlc_lite/rpc_channel.h
@@ -49,7 +49,7 @@
     if (buffer.empty()) {
       return Status::Ok();
     }
-    return hdlc_lite::WriteInformationFrame(address_, buffer, writer_);
+    return hdlc_lite::WriteUIFrame(address_, buffer, writer_);
   }
 
  private:
@@ -77,7 +77,7 @@
     if (buffer.empty()) {
       return Status::Ok();
     }
-    return hdlc_lite::WriteInformationFrame(address_, buffer, writer_);
+    return hdlc_lite::WriteUIFrame(address_, buffer, writer_);
   }
 
  private:
diff --git a/pw_hdlc_lite/pw_hdlc_lite_private/protocol.h b/pw_hdlc_lite/pw_hdlc_lite_private/protocol.h
index 25159dbc..e675d29 100644
--- a/pw_hdlc_lite/pw_hdlc_lite_private/protocol.h
+++ b/pw_hdlc_lite/pw_hdlc_lite_private/protocol.h
@@ -29,4 +29,28 @@
   return (b == kFlag || b == kEscape);
 }
 
+// Class that manages the 1-byte control field of an HDLC U-frame.
+class UFrameControl {
+ public:
+  static constexpr UFrameControl UnnumberedInformation() {
+    return UFrameControl(kUnnumberedInformation);
+  }
+
+  constexpr std::byte data() const { return data_; }
+
+ private:
+  // Types of HDLC U-frames and their bit patterns.
+  enum Type : uint8_t {
+    kUnnumberedInformation = 0x00,
+  };
+
+  constexpr UFrameControl(Type type)
+      : data_(kUFramePattern | std::byte{type}) {}
+
+  // U-frames are identified by having the bottom two control bits set.
+  static constexpr std::byte kUFramePattern = std::byte{0x03};
+
+  std::byte data_;
+};
+
 }  // namespace pw::hdlc_lite
diff --git a/pw_hdlc_lite/py/encode_test.py b/pw_hdlc_lite/py/encode_test.py
index b0e68ab..bf219d8 100755
--- a/pw_hdlc_lite/py/encode_test.py
+++ b/pw_hdlc_lite/py/encode_test.py
@@ -27,30 +27,30 @@
     return data + _fcs(data)
 
 
-class TestEncodeInformationFrame(unittest.TestCase):
+class TestEncodeUIFrame(unittest.TestCase):
     """Tests Encoding bytes with different arguments using a custom serial."""
     def test_empty(self):
-        self.assertEqual(encode.information_frame(0, b''),
-                         FLAG + _with_fcs(b'\0\0') + FLAG)
-        self.assertEqual(encode.information_frame(0x1a, b''),
-                         FLAG + _with_fcs(b'\x1a\0') + FLAG)
+        self.assertEqual(encode.ui_frame(0, b''),
+                         FLAG + _with_fcs(b'\0\x03') + FLAG)
+        self.assertEqual(encode.ui_frame(0x1a, b''),
+                         FLAG + _with_fcs(b'\x1a\x03') + FLAG)
 
     def test_1byte(self):
-        self.assertEqual(encode.information_frame(0, b'A'),
-                         FLAG + _with_fcs(b'\0\0A') + FLAG)
+        self.assertEqual(encode.ui_frame(0, b'A'),
+                         FLAG + _with_fcs(b'\0\x03A') + FLAG)
 
     def test_multibyte(self):
-        self.assertEqual(encode.information_frame(0, b'123456789'),
-                         FLAG + _with_fcs(b'\x00\x00123456789') + FLAG)
+        self.assertEqual(encode.ui_frame(0, b'123456789'),
+                         FLAG + _with_fcs(b'\x00\x03123456789') + FLAG)
 
     def test_escape(self):
         self.assertEqual(
-            encode.information_frame(0x7e, b'\x7d'),
-            FLAG + b'\x7d\x5e\x00\x7d\x5d' + _fcs(b'\x7e\x00\x7d') + FLAG)
+            encode.ui_frame(0x7e, b'\x7d'),
+            FLAG + b'\x7d\x5e\x03\x7d\x5d' + _fcs(b'\x7e\x03\x7d') + FLAG)
         self.assertEqual(
-            encode.information_frame(0x7d, b'A\x7e\x7dBC'),
-            FLAG + b'\x7d\x5d\x00A\x7d\x5e\x7d\x5dBC' +
-            _fcs(b'\x7d\x00A\x7e\x7dBC') + FLAG)
+            encode.ui_frame(0x7d, b'A\x7e\x7dBC'),
+            FLAG + b'\x7d\x5d\x03A\x7d\x5e\x7d\x5dBC' +
+            _fcs(b'\x7d\x03A\x7e\x7dBC') + FLAG)
 
 
 if __name__ == '__main__':
diff --git a/pw_hdlc_lite/py/pw_hdlc_lite/encode.py b/pw_hdlc_lite/py/pw_hdlc_lite/encode.py
index 9db8a30..c38f9c3 100644
--- a/pw_hdlc_lite/py/pw_hdlc_lite/encode.py
+++ b/pw_hdlc_lite/py/pw_hdlc_lite/encode.py
@@ -17,12 +17,13 @@
 
 _ESCAPE_BYTE = bytes([protocol.ESCAPE])
 _FLAG_BYTE = bytes([protocol.FLAG])
-_CONTROL = 0  # Currently, hard-coded to 0; no sequence numbers are used
 
 
-def information_frame(address: int, data: bytes) -> bytes:
-    """Encodes an HDLC I-frame with a CRC-32 frame check sequence."""
-    frame = bytearray([address, _CONTROL]) + data
+def ui_frame(address: int, data: bytes) -> bytes:
+    """Encodes an HDLC UI-frame with a CRC-32 frame check sequence."""
+    frame = bytearray([
+        address
+    ]) + protocol.UFrameControl.unnumbered_information().data + data
     frame += protocol.frame_check_sequence(frame)
     frame = frame.replace(_ESCAPE_BYTE, b'\x7d\x5d')
     frame = frame.replace(_FLAG_BYTE, b'\x7d\x5e')
diff --git a/pw_hdlc_lite/py/pw_hdlc_lite/protocol.py b/pw_hdlc_lite/py/pw_hdlc_lite/protocol.py
index 4f9098a..a9e7251 100644
--- a/pw_hdlc_lite/py/pw_hdlc_lite/protocol.py
+++ b/pw_hdlc_lite/py/pw_hdlc_lite/protocol.py
@@ -29,3 +29,16 @@
 
 def frame_check_sequence(data: bytes) -> bytes:
     return zlib.crc32(data).to_bytes(4, 'little')
+
+
+class UFrameControl:
+    def __init__(self, frame_type: int):
+        self._data: bytes = bytes([0x03 | frame_type])
+
+    @property
+    def data(self):
+        return self._data
+
+    @classmethod
+    def unnumbered_information(cls):
+        return UFrameControl(0x00)
diff --git a/pw_hdlc_lite/py/pw_hdlc_lite/rpc.py b/pw_hdlc_lite/py/pw_hdlc_lite/rpc.py
index 7ee008c..78e0450 100644
--- a/pw_hdlc_lite/py/pw_hdlc_lite/rpc.py
+++ b/pw_hdlc_lite/py/pw_hdlc_lite/rpc.py
@@ -46,10 +46,10 @@
                 time.sleep(delay_s)
                 writer(bytes([byte]))
 
-        return lambda data: slow_write(encode.information_frame(address, data))
+        return lambda data: slow_write(encode.ui_frame(address, data))
 
     def write_hdlc(data: bytes):
-        frame = encode.information_frame(address, data)
+        frame = encode.ui_frame(address, data)
         _LOG.debug('Write %2d B: %s', len(frame), frame)
         writer(frame)
 
diff --git a/pw_hdlc_lite/rpc_channel_test.cc b/pw_hdlc_lite/rpc_channel_test.cc
index 8e9c7db..a7d4af3 100644
--- a/pw_hdlc_lite/rpc_channel_test.cc
+++ b/pw_hdlc_lite/rpc_channel_test.cc
@@ -42,8 +42,8 @@
 namespace {
 
 constexpr byte kFlag = byte{0x7E};
-constexpr uint8_t kAddress = 0x7b;  // 123
-constexpr byte kControl = byte{0};
+constexpr uint8_t kAddress = 0x7b;    // 123
+constexpr byte kControl = byte{0x3};  // UI-frame control sequence.
 
 // Size of the in-memory buffer to use for this test.
 constexpr size_t kSinkBufferSize = 15;
@@ -60,7 +60,7 @@
   std::memcpy(buffer.data(), &test_data, sizeof(test_data));
 
   constexpr auto expected = bytes::Concat(
-      kFlag, kAddress, kControl, 'A', uint32_t{0xA63E2FA5}, kFlag);
+      kFlag, kAddress, kControl, 'A', uint32_t{0x8D137C66}, kFlag);
 
   EXPECT_EQ(Status::Ok(),
             output.SendAndReleaseBuffer(buffer.first(sizeof(test_data))));
@@ -88,7 +88,7 @@
                                           kControl,
                                           byte{0x7d},
                                           byte{0x7d} ^ byte{0x20},
-                                          uint32_t{0x89515322},
+                                          uint32_t{0xA27C00E1},
                                           kFlag);
   EXPECT_EQ(Status::Ok(),
             output.SendAndReleaseBuffer(buffer.first(test_data.size())));
@@ -111,7 +111,7 @@
   std::memcpy(buffer.data(), &test_data, sizeof(test_data));
 
   constexpr auto expected = bytes::Concat(
-      kFlag, kAddress, kControl, 'A', uint32_t{0xA63E2FA5}, kFlag);
+      kFlag, kAddress, kControl, 'A', uint32_t{0x8D137C66}, kFlag);
 
   EXPECT_EQ(Status::Ok(),
             output.SendAndReleaseBuffer(buffer.first(sizeof(test_data))));
diff --git a/pw_hdlc_lite/rpc_example/hdlc_rpc_server.cc b/pw_hdlc_lite/rpc_example/hdlc_rpc_server.cc
index 88a13ca..08ceb03 100644
--- a/pw_hdlc_lite/rpc_example/hdlc_rpc_server.cc
+++ b/pw_hdlc_lite/rpc_example/hdlc_rpc_server.cc
@@ -38,6 +38,7 @@
 
 void Start() {
   pw::rpc::system_server::Init();
+
   // Set up the server and start processing data.
   RegisterServices();
 
diff --git a/pw_log_tokenized/base64_over_hdlc.cc b/pw_log_tokenized/base64_over_hdlc.cc
index ee2f5d6..ce2d9cb 100644
--- a/pw_log_tokenized/base64_over_hdlc.cc
+++ b/pw_log_tokenized/base64_over_hdlc.cc
@@ -43,10 +43,9 @@
   base64_buffer[base64_bytes] = '\0';
 
   // HDLC-encode the Base64 string via a SysIoWriter.
-  hdlc_lite::WriteInformationFrame(
-      PW_LOG_TOKENIZED_BASE64_LOG_HDLC_ADDRESS,
-      std::as_bytes(std::span(base64_buffer, base64_bytes)),
-      writer);
+  hdlc_lite::WriteUIFrame(PW_LOG_TOKENIZED_BASE64_LOG_HDLC_ADDRESS,
+                          std::as_bytes(std::span(base64_buffer, base64_bytes)),
+                          writer);
 }
 
 }  // namespace pw::log_tokenized
diff --git a/pw_rpc/system_server/socket_rpc_server.cc b/pw_rpc/system_server/socket_rpc_server.cc
index 1f3c77f..bc8ec37 100644
--- a/pw_rpc/system_server/socket_rpc_server.cc
+++ b/pw_rpc/system_server/socket_rpc_server.cc
@@ -37,8 +37,7 @@
 
 void Init() {
   log_basic::SetOutput([](std::string_view log) {
-    hdlc_lite::WriteInformationFrame(
-        1, std::as_bytes(std::span(log)), socket_stream);
+    hdlc_lite::WriteUIFrame(1, std::as_bytes(std::span(log)), socket_stream);
   });
 
   socket_stream.Init(kSocketPort);
diff --git a/pw_rpc/system_server/sys_io_rpc_server.cc b/pw_rpc/system_server/sys_io_rpc_server.cc
index 80a9378..2a66fb8 100644
--- a/pw_rpc/system_server/sys_io_rpc_server.cc
+++ b/pw_rpc/system_server/sys_io_rpc_server.cc
@@ -41,8 +41,7 @@
   // Send log messages to HDLC address 1. This prevents logs from interfering
   // with pw_rpc communications.
   pw::log_basic::SetOutput([](std::string_view log) {
-    pw::hdlc_lite::WriteInformationFrame(
-        1, std::as_bytes(std::span(log)), writer);
+    pw::hdlc_lite::WriteUIFrame(1, std::as_bytes(std::span(log)), writer);
   });
 }
 
diff --git a/pw_unit_test/rpc_main.cc b/pw_unit_test/rpc_main.cc
index 04a70ad..dd634f1 100644
--- a/pw_unit_test/rpc_main.cc
+++ b/pw_unit_test/rpc_main.cc
@@ -50,7 +50,7 @@
   // Send log messages to HDLC address 1. This prevents logs from interfering
   // with pw_rpc communications.
   log_basic::SetOutput([](std::string_view log) {
-    hdlc_lite::WriteInformationFrame(1, std::as_bytes(std::span(log)), writer);
+    hdlc_lite::WriteUIFrame(1, std::as_bytes(std::span(log)), writer);
   });
 
   RegisterServices();