SatCat5
codec_slip.cc
1 // Copyright 2021-2024 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/codec_slip.h>
7 #include <satcat5/log.h>
8 
15 
16 // By default, log all SLIP errors.
17 #ifndef SATCAT5_SLIP_LOG_ERROR
18 #define SATCAT5_SLIP_LOG_ERROR 1
19 #endif
20 
21 // Constants for various SLIP tokens:
22 static const u8 SLIP_END = 0xC0;
23 static const u8 SLIP_ESC = 0xDB;
24 static const u8 SLIP_ESC_END = 0xDC;
25 static const u8 SLIP_ESC_ESC = 0xDD;
26 static const u16 SLIP_ESC_W_END = 0xDBDC;
27 static const u16 SLIP_ESC_W_ESC = 0xDBDD;
28 
29 SlipEncoder::SlipEncoder(Writeable* dst)
30  : m_dst(dst)
31  , m_overflow(false)
32 {
33  // Nothing else to initialize.
34 }
35 
37 {
38  // Worst-case: Every input character needs to be escaped,
39  // then allow one additional byte for the SLIP_END token.
40  unsigned dst = m_dst->get_write_space();
41  if (m_overflow)
42  return 0;
43  else if (dst < 3)
44  return 0;
45  else
46  return (dst - 1) / 2;
47 }
48 
50 {
51  // Always attempt to write the end-of-frame token. This helps prevent
52  // cascading errors for interfaces where write_abort() is a no-op.
53  m_dst->write_u8(SLIP_END);
54 
55  // Finalize the frame, or attempt to abort if possible.
56  if (m_overflow) {
57  m_overflow = false;
58  m_dst->write_abort();
59  return false;
60  } else {
61  return m_dst->write_finalize();
62  }
63 }
64 
66 {
67  m_overflow = true; // Set persistent error flag
68 }
69 
71 {
72  if (data == SLIP_END) {
73  m_dst->write_u16(SLIP_ESC_W_END);
74  } else if (data == SLIP_ESC) {
75  m_dst->write_u16(SLIP_ESC_W_ESC);
76  } else {
77  m_dst->write_u8(data);
78  }
79 }
80 
81 // Inline SLIP Decoder
83  : m_dst(dst)
84  , m_state(State::SLIP_EOF)
85 {
86  // Nothing else to initialize.
87 }
88 
90 {
91  // Worst case is one-to-one, no special tokens in input.
92  return m_dst->get_write_space();
93 }
94 
96 {
97  if (data == SLIP_END) {
98  // Finalize complete frame, or abort on incomplete data.
99  // (This includes back-to-back END tokens, which are harmless.)
100  if (m_state == State::SLIP_RDY) {
101  m_dst->write_finalize();
102  } else if (m_state != State::SLIP_EOF) {
103  if (SATCAT5_SLIP_LOG_ERROR)
104  satcat5::log::Log(satcat5::log::WARNING, "SLIP decode error");
105  m_dst->write_abort();
106  }
107  m_state = State::SLIP_EOF;
108  } else if (m_state == State::SLIP_ERR) {
109  // After error, discard data until next END.
110  } else if (data == SLIP_ESC) {
111  m_state = State::SLIP_ESC; // Escape next byte
112  } else if (m_state != State::SLIP_ESC) {
113  m_dst->write_u8(data); // Normal passthrough
114  m_state = State::SLIP_RDY;
115  } else if (data == SLIP_ESC_END) {
116  m_dst->write_u8(SLIP_END); // Escaped END
117  m_state = State::SLIP_RDY;
118  } else if (data == SLIP_ESC_ESC) {
119  m_dst->write_u8(SLIP_ESC); // Escaped ESC
120  m_state = State::SLIP_RDY;
121  } else {
122  m_state = State::SLIP_ERR; // Error
123  }
124 }
125 
127 {
128  // Discard any further data until next end-of-frame.
129  m_state = State::SLIP_ERR;
130  // Purging the destination buffer ensures we can continue parsing.
131  m_dst->write_abort();
132 }
133 
135  : SlipEncoder(dst) // Upstream writes are encoded enroute
136  , ReadableRedirect(&m_buff) // Upstream reads pull from buffer
137  , m_buff(m_rawbuff, SATCAT5_SLIP_BUFFSIZE, SATCAT5_SLIP_PACKETS)
138  , m_decode(&m_buff) // Decoder writes to buffer
139  , m_copy(src, &m_decode) // Auto-copy from source to decoder
140 {
141  // Nothing else to initialize.
142 }
143 
145  : SlipDecoder(dst) // Upstream writes are decoded enroute
146  , ReadableRedirect(&m_buff) // Upstream reads pull from buffer
147  , m_buff(m_rawbuff, SATCAT5_SLIP_BUFFSIZE, 0)
148  , m_encode(&m_buff) // Decoder writes to buffer
149  , m_copy(src, &m_encode) // Auto-copy from source to encoder
150 {
151  // Nothing else to initialize.
152 }
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
Wrapper class for forwarding reads to another object.
Definition: io_readable.h:299
Buffered SLIP encoder / decoder pair.
Definition: codec_slip.h:93
SlipCodec(satcat5::io::Writeable *dst, satcat5::io::Readable *src)
Constructor links to specified source and destination.
Definition: codec_slip.cc:134
Buffered SLIP encoder / decoder pair with opposite polarity.
Definition: codec_slip.h:118
SlipCodecInverse(satcat5::io::Writeable *dst, satcat5::io::Readable *src)
Constructor links to specified source and destination.
Definition: codec_slip.cc:144
Inline SLIP decoder.
Definition: codec_slip.h:62
unsigned get_write_space() const override
How many bytes can be written without blocking?
Definition: codec_slip.cc:89
void write_next(u8 data) override
Write the next byte to the underlying buffer or device.
Definition: codec_slip.cc:95
SlipDecoder(satcat5::io::Writeable *dst)
Permanently link this encoder to an output object.
Definition: codec_slip.cc:82
void write_overflow() override
Optional error handling for write overflow.
Definition: codec_slip.cc:126
Inline SLIP encoder.
Definition: codec_slip.h:42
void write_overflow() override
Optional error handling for write overflow.
Definition: codec_slip.cc:65
void write_next(u8 data) override
Write the next byte to the underlying buffer or device.
Definition: codec_slip.cc:70
bool write_finalize() override
Mark end of frame and release temporary working data.
Definition: codec_slip.cc:49
unsigned get_write_space() const override
How many bytes can be written without blocking?
Definition: codec_slip.cc:36
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
virtual unsigned get_write_space() const =0
How many bytes can be written without blocking?
virtual void write_abort()
If possible, abort the current partially-written packet.
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
Definition: io_writeable.cc:20
virtual bool write_finalize()
Mark end of frame and release temporary working data.
The Log class creates and formats one log message.
Definition: log.h:195
Inline SLIP encoder and decoder objects.
Diagnostic logging to UART and/or Ethernet ports.