leodos_protocols/coding/framing/
cltu.rs1use crate::physical::{PhysicalWrite};
10
11const START_SEQUENCE: &[u8] = &[0xEB, 0x90];
12const TAIL_SEQUENCE: &[u8] = &[0xC5; 8];
13
14#[derive(Debug, Copy, Clone, Eq, PartialEq)]
16pub enum CltuError {
17 OutputBufferTooSmall {
19 required: usize,
21 provided: usize,
23 },
24}
25
26pub fn encoded_cltu_len(tc_frame_len: usize) -> usize {
31 let num_blocks = (tc_frame_len + 6) / 7; START_SEQUENCE.len() + num_blocks * 8 + TAIL_SEQUENCE.len()
33}
34
35pub fn encode_cltu(tc_frame_bytes: &[u8], output_buffer: &mut [u8]) -> Result<usize, CltuError> {
47 let required_len = encoded_cltu_len(tc_frame_bytes.len());
48 if output_buffer.len() < required_len {
49 return Err(CltuError::OutputBufferTooSmall {
50 required: required_len,
51 provided: output_buffer.len(),
52 });
53 }
54
55 let mut writer_idx = 0;
56
57 output_buffer[writer_idx..writer_idx + START_SEQUENCE.len()].copy_from_slice(START_SEQUENCE);
59 writer_idx += START_SEQUENCE.len();
60
61 for chunk in tc_frame_bytes.chunks(7) {
63 let mut block = [0x55; 7]; block[..chunk.len()].copy_from_slice(chunk);
65
66 let parity = bch::compute_bch_parity(&block);
67
68 output_buffer[writer_idx..writer_idx + 7].copy_from_slice(&block);
69 writer_idx += 7;
70 output_buffer[writer_idx] = parity;
71 writer_idx += 1;
72 }
73
74 output_buffer[writer_idx..writer_idx + TAIL_SEQUENCE.len()].copy_from_slice(TAIL_SEQUENCE);
76 writer_idx += TAIL_SEQUENCE.len();
77
78 Ok(writer_idx)
79}
80
81mod bch {
82 const fn generate_lookup_table() -> [u8; 256] {
90 const POLY_ALIGNED: u8 = 0x8A; let mut table = [0u8; 256];
92 let mut i = 0;
93 while i < 256 {
94 let mut val = i as u8;
95 let mut bit = 0;
96 while bit < 8 {
97 val = if val & 0x80 != 0 {
98 (val << 1) ^ POLY_ALIGNED
99 } else {
100 val << 1
101 };
102 bit += 1;
103 }
104 table[i] = val;
105 i += 1;
106 }
107 table
108 }
109
110 static LOOKUP_TABLE: [u8; 256] = generate_lookup_table();
111
112 pub fn compute_bch_parity(bytes: &[u8; 7]) -> u8 {
119 let remainder = bytes
120 .iter()
121 .fold(0u8, |acc, &val| LOOKUP_TABLE[(acc ^ val) as usize]);
122 !remainder & 0xFE
123 }
124}
125
126pub struct CltuFramer;
128
129impl crate::coding::Framer for CltuFramer {
130 type Error = CltuError;
131
132 fn frame(&self, data: &[u8], output: &mut [u8]) -> Result<usize, Self::Error> {
133 encode_cltu(data, output)
134 }
135}
136
137#[derive(Debug, Clone)]
139pub enum CltuWriterError<E> {
140 Cltu(CltuError),
142 Writer(E),
144}
145
146impl<E: core::fmt::Display> core::fmt::Display for CltuWriterError<E> {
147 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
148 match self {
149 Self::Cltu(e) => write!(f, "CLTU encoding error: {e:?}"),
150 Self::Writer(e) => write!(f, "writer error: {e}"),
151 }
152 }
153}
154
155impl<E: core::error::Error> core::error::Error for CltuWriterError<E> {}
156
157pub struct CltuWriter<W, const BUF: usize> {
160 writer: W,
161 buffer: [u8; BUF],
162}
163
164impl<W, const BUF: usize> CltuWriter<W, BUF> {
165 pub fn new(writer: W) -> Self {
167 Self {
168 writer,
169 buffer: [0u8; BUF],
170 }
171 }
172
173 pub fn into_inner(self) -> W {
175 self.writer
176 }
177}
178
179impl<W: PhysicalWrite, const BUF: usize> CltuWriter<W, BUF> {
180 pub async fn write_frame(
182 &mut self,
183 tc_frame: &[u8],
184 ) -> Result<(), CltuWriterError<W::Error>> {
185 let required = encoded_cltu_len(tc_frame.len());
186 if required > BUF {
187 return Err(CltuWriterError::Cltu(
188 CltuError::OutputBufferTooSmall {
189 required,
190 provided: BUF,
191 },
192 ));
193 }
194
195 let len = encode_cltu(tc_frame, &mut self.buffer)
196 .map_err(CltuWriterError::Cltu)?;
197 self.writer
198 .write(&self.buffer[..len])
199 .await
200 .map_err(CltuWriterError::Writer)
201 }
202}
203
204impl<W: PhysicalWrite, const BUF: usize> PhysicalWrite
205 for CltuWriter<W, BUF>
206{
207 type Error = CltuWriterError<W::Error>;
208
209 async fn write(
210 &mut self,
211 data: &[u8],
212 ) -> Result<(), Self::Error> {
213 self.write_frame(data).await
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn bch_test_vectors() {
223 assert_eq!(
224 bch::compute_bch_parity(&[0x22, 0xF6, 0x00, 0xFF, 0x00, 0x42, 0x1A]),
225 0x12
226 );
227 assert_eq!(
228 bch::compute_bch_parity(&[0x8C, 0xC0, 0x0E, 0x01, 0x0D, 0x19, 0x06]),
229 0x5A
230 );
231 }
232
233 #[test]
234 fn cltu_encoding() {
235 let tc_frame: &[u8] = &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
236 let mut cltu_buffer = [0u8; 34]; let len = encode_cltu(tc_frame, &mut cltu_buffer).unwrap();
239 assert_eq!(len, 26);
240
241 let expected_start = &cltu_buffer[0..2];
242 assert_eq!(expected_start, &[0xEB, 0x90]);
243
244 assert_eq!(
246 cltu_buffer[2..9],
247 [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]
248 );
249 assert_eq!(cltu_buffer[9], 0x70);
250
251 assert_eq!(
253 cltu_buffer[10..17],
254 [0x08, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55]
255 );
256 assert_eq!(cltu_buffer[17], 0x90);
257
258 let expected_tail = &cltu_buffer[18..26];
259 assert_eq!(expected_tail, &[0xC5; 8]);
260 }
261}