1use std::collections::VecDeque;
19use std::time::Duration;
20
21use broadcast_common::{Parse, Serialize};
22use dvb_ci::tpdu::{CommandTpdu, DataBlock, ResponseTpdu, SbValue, TcObject, create_t_c, tags};
23
24const SB_OBJECT_LEN: usize = 4;
26
27fn parse_sb(bytes: &[u8]) -> Option<(u8, SbValue)> {
29 if bytes.len() >= SB_OBJECT_LEN && bytes[0] == tags::SB && bytes[1] == 0x02 {
30 Some((bytes[2], SbValue(bytes[3])))
31 } else {
32 None
33 }
34}
35
36pub const DEFAULT_POLL_INTERVAL: Duration = Duration::from_millis(100);
39pub const DEFAULT_REPLY_TIMEOUT: Duration = Duration::from_millis(1000);
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45#[non_exhaustive]
46pub enum TcState {
47 Idle,
49 Creating,
51 Active,
53}
54
55#[derive(Debug, Default, Clone, PartialEq, Eq)]
57pub struct Out {
58 pub writes: Vec<Vec<u8>>,
60 pub spdus: Vec<Vec<u8>>,
62 pub timer: Option<Duration>,
64 pub error: Option<TransportError>,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
70#[non_exhaustive]
71pub enum TransportError {
72 #[error("transport connection setup timed out")]
74 SetupTimeout,
75 #[error("unexpected t_c_id {got} (expected {expected})")]
77 WrongTcId {
78 got: u8,
80 expected: u8,
82 },
83 #[error("module reported T_C_Error")]
85 ModuleError,
86 #[error("malformed R_TPDU")]
88 Malformed,
89}
90
91#[derive(Debug)]
93pub struct Transport {
94 tcid: u8,
95 state: TcState,
96 reassembly: Vec<u8>,
97 poll_interval: Duration,
98 reply_timeout: Duration,
99 since_poll: Duration,
101 awaiting: Option<Duration>,
106 outbound: VecDeque<Vec<u8>>,
111}
112
113impl Default for Transport {
114 fn default() -> Self {
115 Self::new(1)
116 }
117}
118
119impl Transport {
120 #[must_use]
122 pub fn new(tcid: u8) -> Self {
123 Self {
124 tcid,
125 state: TcState::Idle,
126 reassembly: Vec::new(),
127 poll_interval: DEFAULT_POLL_INTERVAL,
128 reply_timeout: DEFAULT_REPLY_TIMEOUT,
129 since_poll: Duration::ZERO,
130 awaiting: None,
131 outbound: VecDeque::new(),
132 }
133 }
134
135 #[must_use]
137 pub fn with_timing(mut self, poll: Duration, reply: Duration) -> Self {
138 self.poll_interval = poll;
139 self.reply_timeout = reply;
140 self
141 }
142
143 #[must_use]
145 pub fn state(&self) -> TcState {
146 self.state
147 }
148
149 fn cmd(&self, tag: u8, data: &[u8]) -> Vec<u8> {
150 let c = CommandTpdu {
151 tag,
152 t_c_id: self.tcid,
153 data,
154 };
155 let mut buf = vec![0u8; c.serialized_len()];
156 let n = c.serialize_into(&mut buf).expect("exact buffer");
158 buf.truncate(n);
159 buf
160 }
161
162 fn poll_frame(&self) -> Vec<u8> {
163 self.cmd(tags::DATA_LAST, &[])
165 }
166
167 pub fn init(&mut self) -> Out {
169 self.state = TcState::Creating;
170 self.awaiting = Some(Duration::ZERO);
171 let obj: TcObject = create_t_c(self.tcid);
172 Out {
173 writes: vec![obj.to_bytes()],
174 timer: Some(self.reply_timeout),
175 ..Out::default()
176 }
177 }
178
179 pub fn send_spdu(&mut self, spdu: &[u8]) -> Out {
183 if self.state != TcState::Active {
184 return Out::default();
185 }
186 self.outbound.push_back(spdu.to_vec());
187 self.flush()
188 }
189
190 fn flush(&mut self) -> Out {
193 if self.state != TcState::Active || self.awaiting.is_some() {
194 return Out::default();
195 }
196 match self.outbound.pop_front() {
197 Some(spdu) => {
198 self.awaiting = Some(Duration::ZERO);
199 self.since_poll = Duration::ZERO;
200 Out {
201 writes: vec![self.cmd(tags::DATA_LAST, &spdu)],
202 timer: Some(self.poll_interval),
203 ..Out::default()
204 }
205 }
206 None => Out::default(),
207 }
208 }
209
210 pub fn tick(&mut self, elapsed: Duration) -> Out {
213 match self.state {
214 TcState::Idle => Out::default(),
215 TcState::Creating => {
216 if let Some(w) = self.awaiting.as_mut() {
217 *w += elapsed;
218 if *w >= self.reply_timeout {
219 self.state = TcState::Idle;
220 self.awaiting = None;
221 return Out {
222 error: Some(TransportError::SetupTimeout),
223 ..Out::default()
224 };
225 }
226 }
227 Out {
228 timer: Some(self.reply_timeout),
229 ..Out::default()
230 }
231 }
232 TcState::Active => {
233 self.since_poll += elapsed;
234 if self.since_poll >= self.poll_interval {
235 self.since_poll = Duration::ZERO;
236 if self.awaiting.is_none() && !self.outbound.is_empty() {
239 return self.flush();
240 }
241 self.awaiting = Some(Duration::ZERO);
242 Out {
243 writes: vec![self.poll_frame()],
244 timer: Some(self.poll_interval),
245 ..Out::default()
246 }
247 } else {
248 Out {
249 timer: Some(self.poll_interval - self.since_poll),
250 ..Out::default()
251 }
252 }
253 }
254 }
255 }
256
257 pub fn on_frame(&mut self, frame: &[u8]) -> Out {
264 self.awaiting = None;
265 match frame.first().copied() {
266 Some(tags::C_T_C_REPLY) => match TcObject::parse(frame) {
268 Ok(o) if o.t_c_id == self.tcid => {
269 self.state = TcState::Active;
270 self.since_poll = Duration::ZERO;
271 let da = parse_sb(&frame[3..]).is_some_and(|(_, sb)| sb.data_available());
272 self.after_status(da)
273 }
274 Ok(o) => self.wrong_tcid(o.t_c_id),
275 Err(_) => self.malformed(),
276 },
277 Some(tags::SB) => match parse_sb(frame) {
279 Some((tcid, _)) if tcid != self.tcid => self.wrong_tcid(tcid),
280 Some((_, sb)) => self.after_status(sb.data_available()),
281 None => self.malformed(),
282 },
283 Some(tags::T_C_ERROR) => Out {
284 error: Some(TransportError::ModuleError),
285 ..Out::default()
286 },
287 Some(tags::DATA_LAST | tags::DATA_MORE) => self.on_data(frame),
288 _ => self.malformed(),
289 }
290 }
291
292 fn malformed(&self) -> Out {
293 Out {
294 error: Some(TransportError::Malformed),
295 ..Out::default()
296 }
297 }
298
299 fn wrong_tcid(&self, got: u8) -> Out {
300 Out {
301 error: Some(TransportError::WrongTcId {
302 got,
303 expected: self.tcid,
304 }),
305 ..Out::default()
306 }
307 }
308
309 fn after_status(&mut self, data_available: bool) -> Out {
312 if data_available {
313 self.awaiting = Some(Duration::ZERO);
314 Out {
315 writes: vec![self.cmd(tags::RCV, &[])],
316 ..Out::default()
317 }
318 } else {
319 if !self.outbound.is_empty() {
322 return self.flush();
323 }
324 self.since_poll = Duration::ZERO;
325 Out {
326 timer: Some(self.poll_interval),
327 ..Out::default()
328 }
329 }
330 }
331
332 fn on_data(&mut self, frame: &[u8]) -> Out {
333 let r = match ResponseTpdu::parse(frame) {
334 Ok(r) => r,
335 Err(_) => {
336 return Out {
337 error: Some(TransportError::Malformed),
338 ..Out::default()
339 };
340 }
341 };
342 if r.t_c_id != self.tcid {
343 return Out {
344 error: Some(TransportError::WrongTcId {
345 got: r.t_c_id,
346 expected: self.tcid,
347 }),
348 ..Out::default()
349 };
350 }
351 self.reassembly.extend_from_slice(r.data);
352 match r.block {
353 Some(DataBlock::More) => {
355 self.awaiting = Some(Duration::ZERO);
356 Out {
357 writes: vec![self.cmd(tags::RCV, &[])],
358 ..Out::default()
359 }
360 }
361 _ => {
364 let mut out = self.after_status(r.sb_value.data_available());
365 if !self.reassembly.is_empty() {
366 out.spdus.push(core::mem::take(&mut self.reassembly));
367 }
368 out
369 }
370 }
371 }
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377 use dvb_ci::tpdu::SbValue;
378
379 fn r_tpdu(tag: u8, tcid: u8, data: &[u8], da: bool) -> Vec<u8> {
381 let mut v = vec![tag];
383 v.push((1 + data.len()) as u8);
384 v.push(tcid);
385 v.extend_from_slice(data);
386 v.extend_from_slice(&[tags::SB, 0x02, tcid, SbValue::new(da).0]);
387 v
388 }
389
390 #[test]
391 fn init_sends_create_tc_and_arms_timeout() {
392 let mut t = Transport::new(1);
393 let out = t.init();
394 assert_eq!(out.writes, vec![vec![tags::CREATE_T_C, 0x01, 0x01]]);
395 assert_eq!(t.state(), TcState::Creating);
396 assert_eq!(out.timer, Some(DEFAULT_REPLY_TIMEOUT));
397 }
398
399 #[test]
400 fn setup_times_out_to_idle() {
401 let mut t = Transport::new(1);
402 t.init();
403 let out = t.tick(DEFAULT_REPLY_TIMEOUT);
404 assert_eq!(out.error, Some(TransportError::SetupTimeout));
405 assert_eq!(t.state(), TcState::Idle);
406 }
407
408 #[test]
409 fn reply_activates_then_polls_on_interval() {
410 let mut t = Transport::new(1);
411 t.init();
412 let out = t.on_frame(&[tags::C_T_C_REPLY, 0x01, 0x01]);
413 assert_eq!(t.state(), TcState::Active);
414 assert!(out.error.is_none());
415 let early = t.tick(DEFAULT_POLL_INTERVAL / 2);
417 assert!(early.writes.is_empty());
418 let due = t.tick(DEFAULT_POLL_INTERVAL);
420 assert_eq!(due.writes, vec![vec![tags::DATA_LAST, 0x01, 0x01]]);
421 }
422
423 #[test]
424 fn reassembles_more_then_last_into_one_spdu() {
425 let mut t = Transport::new(1);
426 t.init();
427 t.on_frame(&[tags::C_T_C_REPLY, 0x01, 0x01]);
428 let o1 = t.on_frame(&r_tpdu(tags::DATA_MORE, 1, &[0xAA, 0xBB], false));
430 assert!(o1.spdus.is_empty());
431 assert_eq!(o1.writes, vec![vec![tags::RCV, 0x01, 0x01]]);
432 let o2 = t.on_frame(&r_tpdu(tags::DATA_LAST, 1, &[0xCC], false));
434 assert_eq!(o2.spdus, vec![vec![0xAA, 0xBB, 0xCC]]);
435 }
436
437 #[test]
438 fn data_available_triggers_rcv() {
439 let mut t = Transport::new(1);
440 t.init();
441 t.on_frame(&[tags::C_T_C_REPLY, 0x01, 0x01]);
442 let o = t.on_frame(&r_tpdu(tags::DATA_LAST, 1, &[0x01], true));
444 assert_eq!(o.spdus, vec![vec![0x01]]);
445 assert_eq!(o.writes, vec![vec![tags::RCV, 0x01, 0x01]]);
446 }
447
448 #[test]
449 fn two_sends_serialize_one_block_per_module_turn() {
450 let mut t = Transport::new(1);
454 t.init();
455 t.on_frame(&[tags::C_T_C_REPLY, 0x01, 0x01]);
456
457 let first = t.send_spdu(&[0x92, 0x07]); assert_eq!(first.writes.len(), 1);
459 assert_eq!(first.writes[0][0], tags::DATA_LAST);
460
461 let second = t.send_spdu(&[0x9F, 0x80, 0x10, 0x00]); assert!(
464 second.writes.is_empty(),
465 "second block must wait for the SB"
466 );
467
468 let after_sb = t.on_frame(&[tags::SB, 0x02, 0x01, SbValue::new(false).0]);
470 assert_eq!(
471 after_sb.writes.len(),
472 1,
473 "second block flushes after the SB"
474 );
475 assert_eq!(after_sb.writes[0][0], tags::DATA_LAST);
476 assert!(
478 after_sb.writes[0]
479 .windows(4)
480 .any(|w| w == [0x9F, 0x80, 0x10, 0x00])
481 );
482 }
483
484 #[test]
485 fn wrong_tcid_is_flagged() {
486 let mut t = Transport::new(1);
487 t.init();
488 let o = t.on_frame(&[tags::C_T_C_REPLY, 0x01, 0x09]);
489 assert_eq!(
490 o.error,
491 Some(TransportError::WrongTcId {
492 got: 9,
493 expected: 1
494 })
495 );
496 }
497}