1use crate::error::{Error, Result};
20use crate::objects;
21use crate::tag::ApduTag;
22use alloc::vec::Vec;
23use broadcast_common::{Parse, Serialize};
24
25pub mod tag {
27 use crate::tag::ApduTag;
28 pub const DELIVERY_SYSTEM_INFO_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x00);
30 pub const DELIVERY_SYSTEM_INFO_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x01);
32 pub const SCAN_START_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x02);
34 pub const SCAN_NEXT_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x03);
36 pub const SCAN_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x04);
38 pub const TUNE_TS_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x05);
40 pub const TUNE_TS_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x06);
42}
43
44pub const TUNING_INFO_MESSAGE_LEN: usize = 11;
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize))]
50#[non_exhaustive]
51pub enum SystemIdentifier {
52 Abstract,
54 DvbC,
56 DvbS,
58 DvbT,
60 Reserved(u8),
62}
63
64impl SystemIdentifier {
65 #[must_use]
67 pub fn from_u8(v: u8) -> Self {
68 match v {
69 0 => Self::Abstract,
70 1 => Self::DvbC,
71 2 => Self::DvbS,
72 3 => Self::DvbT,
73 other => Self::Reserved(other),
74 }
75 }
76 #[must_use]
78 pub const fn to_u8(self) -> u8 {
79 match self {
80 Self::Abstract => 0,
81 Self::DvbC => 1,
82 Self::DvbS => 2,
83 Self::DvbT => 3,
84 Self::Reserved(v) => v,
85 }
86 }
87 #[must_use]
89 pub fn name(&self) -> &'static str {
90 match self {
91 Self::Abstract => "Abstract",
92 Self::DvbC => "DVB-C",
93 Self::DvbS => "DVB-S",
94 Self::DvbT => "DVB-T",
95 Self::Reserved(_) => "reserved",
96 }
97 }
98}
99broadcast_common::impl_spec_display!(SystemIdentifier, Reserved);
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize))]
104pub struct DeliverySystemInfoReq;
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
108#[cfg_attr(feature = "serde", derive(serde::Serialize))]
109pub struct ScanStartReq;
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize))]
114pub struct ScanNextReq;
115
116#[derive(Debug, Clone, PartialEq, Eq, Default)]
119#[cfg_attr(feature = "serde", derive(serde::Serialize))]
120pub struct DeliverySystemInfoAck {
121 pub systems: Vec<SystemIdentifier>,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
127#[cfg_attr(feature = "serde", derive(serde::Serialize))]
128pub struct ScanAck<'a> {
129 pub ts_state: u8,
132 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
135 pub tuning_information_message: &'a [u8],
136 pub scan_progress: u8,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Default)]
143#[cfg_attr(feature = "serde", derive(serde::Serialize))]
144pub struct TuneTSReq<'a> {
145 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
147 pub tuning_information_message: &'a [u8],
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
152#[cfg_attr(feature = "serde", derive(serde::Serialize))]
153pub struct TuneTSAck {
154 pub ts_state: u8,
157}
158
159macro_rules! empty_object {
162 ($ty:ty, $tag:expr, $what:literal) => {
163 impl<'a> Parse<'a> for $ty {
164 type Error = Error;
165 fn parse(bytes: &'a [u8]) -> Result<Self> {
166 objects::parse_empty_apdu(bytes, $tag, $what)?;
167 Ok(Self)
168 }
169 }
170 impl Serialize for $ty {
171 type Error = Error;
172 fn serialized_len(&self) -> usize {
173 objects::empty_apdu_len()
174 }
175 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
176 objects::serialize_empty_apdu($tag, buf)
177 }
178 }
179 };
180}
181
182empty_object!(
183 DeliverySystemInfoReq,
184 tag::DELIVERY_SYSTEM_INFO_REQ,
185 "DeliverySystemInfoReq"
186);
187empty_object!(ScanStartReq, tag::SCAN_START_REQ, "ScanStartReq");
188empty_object!(ScanNextReq, tag::SCAN_NEXT_REQ, "ScanNextReq");
189
190impl<'a> Parse<'a> for DeliverySystemInfoAck {
193 type Error = Error;
194 fn parse(bytes: &'a [u8]) -> Result<Self> {
195 let body = objects::parse_apdu_header(
196 bytes,
197 tag::DELIVERY_SYSTEM_INFO_ACK,
198 "DeliverySystemInfoAck",
199 )?;
200 let mut systems = Vec::with_capacity(body.len());
201 for &b in body {
202 systems.push(SystemIdentifier::from_u8(b));
203 }
204 Ok(Self { systems })
205 }
206}
207impl Serialize for DeliverySystemInfoAck {
208 type Error = Error;
209 fn serialized_len(&self) -> usize {
210 objects::apdu_len(self.systems.len())
211 }
212 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
213 let body_len = self.systems.len();
214 let mut pos = objects::write_apdu_header(tag::DELIVERY_SYSTEM_INFO_ACK, body_len, buf)?;
215 for s in &self.systems {
216 buf[pos] = s.to_u8();
217 pos += 1;
218 }
219 Ok(pos)
220 }
221}
222
223const SCAN_ACK_BODY: usize = 1 + TUNING_INFO_MESSAGE_LEN + 1;
227
228impl<'a> Parse<'a> for ScanAck<'a> {
229 type Error = Error;
230 fn parse(bytes: &'a [u8]) -> Result<Self> {
231 let body = objects::parse_apdu_header(bytes, tag::SCAN_ACK, "ScanAck")?;
232 if body.len() < SCAN_ACK_BODY {
233 return Err(Error::BufferTooShort {
234 need: SCAN_ACK_BODY,
235 have: body.len(),
236 what: "ScanAck",
237 });
238 }
239 Ok(Self {
240 ts_state: body[0],
241 tuning_information_message: &body[1..1 + TUNING_INFO_MESSAGE_LEN],
242 scan_progress: body[1 + TUNING_INFO_MESSAGE_LEN],
243 })
244 }
245}
246impl Serialize for ScanAck<'_> {
247 type Error = Error;
248 fn serialized_len(&self) -> usize {
249 objects::apdu_len(SCAN_ACK_BODY)
250 }
251 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
252 if self.tuning_information_message.len() != TUNING_INFO_MESSAGE_LEN {
253 return Err(Error::InvalidObject {
254 what: "ScanAck",
255 reason: "TuningInformationMessage must be exactly 11 bytes",
256 });
257 }
258 let mut pos = objects::write_apdu_header(tag::SCAN_ACK, SCAN_ACK_BODY, buf)?;
259 buf[pos] = self.ts_state;
260 pos += 1;
261 buf[pos..pos + TUNING_INFO_MESSAGE_LEN].copy_from_slice(self.tuning_information_message);
262 pos += TUNING_INFO_MESSAGE_LEN;
263 buf[pos] = self.scan_progress;
264 Ok(pos + 1)
265 }
266}
267
268impl<'a> Parse<'a> for TuneTSReq<'a> {
271 type Error = Error;
272 fn parse(bytes: &'a [u8]) -> Result<Self> {
273 let body = objects::parse_apdu_header(bytes, tag::TUNE_TS_REQ, "TuneTSReq")?;
274 if !body.is_empty() && body.len() != TUNING_INFO_MESSAGE_LEN {
276 return Err(Error::InvalidObject {
277 what: "TuneTSReq",
278 reason: "TuningInformationMessage must be absent or exactly 11 bytes",
279 });
280 }
281 Ok(Self {
282 tuning_information_message: body,
283 })
284 }
285}
286impl Serialize for TuneTSReq<'_> {
287 type Error = Error;
288 fn serialized_len(&self) -> usize {
289 objects::apdu_len(self.tuning_information_message.len())
290 }
291 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
292 if !self.tuning_information_message.is_empty()
293 && self.tuning_information_message.len() != TUNING_INFO_MESSAGE_LEN
294 {
295 return Err(Error::InvalidObject {
296 what: "TuneTSReq",
297 reason: "TuningInformationMessage must be absent or exactly 11 bytes",
298 });
299 }
300 let body_len = self.tuning_information_message.len();
301 let pos = objects::write_apdu_header(tag::TUNE_TS_REQ, body_len, buf)?;
302 buf[pos..pos + body_len].copy_from_slice(self.tuning_information_message);
303 Ok(pos + body_len)
304 }
305}
306
307const TUNE_TS_ACK_BODY: usize = 1;
311
312impl<'a> Parse<'a> for TuneTSAck {
313 type Error = Error;
314 fn parse(bytes: &'a [u8]) -> Result<Self> {
315 let body = objects::parse_apdu_header(bytes, tag::TUNE_TS_ACK, "TuneTSAck")?;
316 if body.len() < TUNE_TS_ACK_BODY {
317 return Err(Error::BufferTooShort {
318 need: TUNE_TS_ACK_BODY,
319 have: body.len(),
320 what: "TuneTSAck",
321 });
322 }
323 Ok(Self { ts_state: body[0] })
324 }
325}
326impl Serialize for TuneTSAck {
327 type Error = Error;
328 fn serialized_len(&self) -> usize {
329 objects::apdu_len(TUNE_TS_ACK_BODY)
330 }
331 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
332 let pos = objects::write_apdu_header(tag::TUNE_TS_ACK, TUNE_TS_ACK_BODY, buf)?;
333 buf[pos] = self.ts_state;
334 Ok(pos + TUNE_TS_ACK_BODY)
335 }
336}
337
338#[derive(Debug, Clone, PartialEq, Eq)]
340#[cfg_attr(feature = "serde", derive(serde::Serialize))]
341#[non_exhaustive]
342pub enum StreamInputApdu<'a> {
343 DeliverySystemInfoReq(DeliverySystemInfoReq),
345 DeliverySystemInfoAck(DeliverySystemInfoAck),
347 ScanStartReq(ScanStartReq),
349 ScanNextReq(ScanNextReq),
351 ScanAck(ScanAck<'a>),
353 TuneTSReq(TuneTSReq<'a>),
355 TuneTSAck(TuneTSAck),
357}
358
359impl<'a> StreamInputApdu<'a> {
360 pub fn parse(body: &'a [u8]) -> Result<Self> {
362 if body.len() < 3 {
363 return Err(Error::BufferTooShort {
364 need: 3,
365 have: body.len(),
366 what: "stream_input apdu_tag",
367 });
368 }
369 let t = ApduTag::from_bytes(body[0], body[1], body[2]);
370 match t {
371 tag::DELIVERY_SYSTEM_INFO_REQ => Ok(Self::DeliverySystemInfoReq(
372 DeliverySystemInfoReq::parse(body)?,
373 )),
374 tag::DELIVERY_SYSTEM_INFO_ACK => Ok(Self::DeliverySystemInfoAck(
375 DeliverySystemInfoAck::parse(body)?,
376 )),
377 tag::SCAN_START_REQ => Ok(Self::ScanStartReq(ScanStartReq::parse(body)?)),
378 tag::SCAN_NEXT_REQ => Ok(Self::ScanNextReq(ScanNextReq::parse(body)?)),
379 tag::SCAN_ACK => Ok(Self::ScanAck(ScanAck::parse(body)?)),
380 tag::TUNE_TS_REQ => Ok(Self::TuneTSReq(TuneTSReq::parse(body)?)),
381 tag::TUNE_TS_ACK => Ok(Self::TuneTSAck(TuneTSAck::parse(body)?)),
382 _ => Err(Error::UnexpectedApduTag {
383 got: t.as_u24(),
384 expected: tag::DELIVERY_SYSTEM_INFO_REQ.as_u24(),
385 what: "stream_input",
386 }),
387 }
388 }
389}
390
391impl Serialize for StreamInputApdu<'_> {
392 type Error = Error;
393 fn serialized_len(&self) -> usize {
394 match self {
395 Self::DeliverySystemInfoReq(o) => o.serialized_len(),
396 Self::DeliverySystemInfoAck(o) => o.serialized_len(),
397 Self::ScanStartReq(o) => o.serialized_len(),
398 Self::ScanNextReq(o) => o.serialized_len(),
399 Self::ScanAck(o) => o.serialized_len(),
400 Self::TuneTSReq(o) => o.serialized_len(),
401 Self::TuneTSAck(o) => o.serialized_len(),
402 }
403 }
404 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
405 match self {
406 Self::DeliverySystemInfoReq(o) => o.serialize_into(buf),
407 Self::DeliverySystemInfoAck(o) => o.serialize_into(buf),
408 Self::ScanStartReq(o) => o.serialize_into(buf),
409 Self::ScanNextReq(o) => o.serialize_into(buf),
410 Self::ScanAck(o) => o.serialize_into(buf),
411 Self::TuneTSReq(o) => o.serialize_into(buf),
412 Self::TuneTSAck(o) => o.serialize_into(buf),
413 }
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420
421 #[test]
422 fn header_only_objects_round_trip() {
423 assert_eq!(DeliverySystemInfoReq.to_bytes(), [0x9F, 0x80, 0x00, 0x00]);
424 assert_eq!(ScanStartReq.to_bytes(), [0x9F, 0x80, 0x02, 0x00]);
425 assert_eq!(ScanNextReq.to_bytes(), [0x9F, 0x80, 0x03, 0x00]);
426 assert_eq!(
427 DeliverySystemInfoReq::parse(&[0x9F, 0x80, 0x00, 0x00]).unwrap(),
428 DeliverySystemInfoReq
429 );
430 assert_eq!(
431 ScanNextReq::parse(&[0x9F, 0x80, 0x03, 0x00]).unwrap(),
432 ScanNextReq
433 );
434 }
435
436 #[test]
437 fn delivery_system_info_ack_multi_round_trips_and_bites() {
438 let ack = DeliverySystemInfoAck {
439 systems: alloc::vec![
440 SystemIdentifier::DvbC,
441 SystemIdentifier::DvbS,
442 SystemIdentifier::DvbT,
443 ],
444 };
445 let bytes = ack.to_bytes();
446 assert_eq!(bytes, [0x9F, 0x80, 0x01, 0x03, 0x01, 0x02, 0x03]);
448 assert_eq!(DeliverySystemInfoAck::parse(&bytes).unwrap(), ack);
449 assert_eq!(ack.systems[0].name(), "DVB-C");
450 let mut other = ack.clone();
451 other.systems[2] = SystemIdentifier::Abstract;
452 assert_ne!(bytes, other.to_bytes());
453 }
454
455 #[test]
456 fn delivery_system_info_ack_reserved_value() {
457 let ack = DeliverySystemInfoAck {
458 systems: alloc::vec![SystemIdentifier::from_u8(0x7F)],
459 };
460 assert_eq!(ack.systems[0], SystemIdentifier::Reserved(0x7F));
461 let bytes = ack.to_bytes();
462 assert_eq!(bytes, [0x9F, 0x80, 0x01, 0x01, 0x7F]);
463 assert_eq!(DeliverySystemInfoAck::parse(&bytes).unwrap(), ack);
464 }
465
466 #[test]
467 fn scan_ack_round_trips_and_bites() {
468 let tim = [
469 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
470 ];
471 let ack = ScanAck {
472 ts_state: 0xC8,
473 tuning_information_message: &tim,
474 scan_progress: 0x40,
475 };
476 let bytes = ack.to_bytes();
477 assert_eq!(
479 bytes,
480 [
481 0x9F, 0x80, 0x04, 0x0D, 0xC8, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
482 0xAA, 0xBB, 0x40
483 ]
484 );
485 assert_eq!(ScanAck::parse(&bytes).unwrap(), ack);
486 let mut other = ack.clone();
487 other.scan_progress = 0x41;
488 assert_ne!(bytes, other.to_bytes());
489 }
490
491 #[test]
492 fn tune_ts_req_with_message_round_trips_and_bites() {
493 let tim = [
494 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
495 ];
496 let req = TuneTSReq {
497 tuning_information_message: &tim,
498 };
499 let bytes = req.to_bytes();
500 assert_eq!(
501 bytes,
502 [
503 0x9F, 0x80, 0x05, 0x0B, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
504 0x0B
505 ]
506 );
507 assert_eq!(TuneTSReq::parse(&bytes).unwrap(), req);
508 let mut tim2 = tim;
509 tim2[10] = 0xFF;
510 let other = TuneTSReq {
511 tuning_information_message: &tim2,
512 };
513 assert_ne!(bytes, other.to_bytes());
514 }
515
516 #[test]
517 fn tune_ts_req_disconnect_empty_message() {
518 let req = TuneTSReq {
519 tuning_information_message: &[],
520 };
521 let bytes = req.to_bytes();
522 assert_eq!(bytes, [0x9F, 0x80, 0x05, 0x00]);
523 assert_eq!(TuneTSReq::parse(&bytes).unwrap(), req);
524 }
525
526 #[test]
527 fn tune_ts_req_rejects_wrong_length() {
528 let bad = [0x9F, 0x80, 0x05, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05];
530 assert!(matches!(
531 TuneTSReq::parse(&bad),
532 Err(Error::InvalidObject { .. })
533 ));
534 }
535
536 #[test]
537 fn tune_ts_ack_round_trips_and_bites() {
538 let ack = TuneTSAck { ts_state: 0xFF };
539 let bytes = ack.to_bytes();
540 assert_eq!(bytes, [0x9F, 0x80, 0x06, 0x01, 0xFF]);
541 assert_eq!(TuneTSAck::parse(&bytes).unwrap(), ack);
542 let other = TuneTSAck { ts_state: 0x00 };
543 assert_ne!(bytes, other.to_bytes());
544 }
545
546 #[test]
547 fn dispatch_routes_each_tag() {
548 let req = DeliverySystemInfoReq.to_bytes();
549 assert!(matches!(
550 StreamInputApdu::parse(&req).unwrap(),
551 StreamInputApdu::DeliverySystemInfoReq(_)
552 ));
553 let ack = TuneTSAck { ts_state: 1 }.to_bytes();
554 let parsed = StreamInputApdu::parse(&ack).unwrap();
555 assert!(matches!(parsed, StreamInputApdu::TuneTSAck(_)));
556 assert_eq!(parsed.to_bytes(), ack);
557 let next = ScanNextReq.to_bytes();
559 assert!(matches!(
560 StreamInputApdu::parse(&next).unwrap(),
561 StreamInputApdu::ScanNextReq(_)
562 ));
563 }
564}