1use crate::error::{Error, Result};
16use crate::objects;
17use crate::tag::ApduTag;
18use alloc::vec::Vec;
19use broadcast_common::{Parse, Serialize};
20
21pub mod tag {
23 use crate::tag::ApduTag;
24 pub const CICAM_MULTISTREAM_CAPABILITY: ApduTag = ApduTag::from_bytes(0x9F, 0x92, 0x00);
26 pub const PID_SELECT_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x92, 0x01);
28 pub const PID_SELECT_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x92, 0x02);
30}
31
32pub const NULL_PID: u16 = 0x1FFF;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize))]
41pub struct CicamMultistreamCapability {
42 pub max_local_ts: u8,
45 pub max_descramblers: u16,
48}
49
50const CAPABILITY_BODY: usize = 1 + 2;
52
53impl<'a> Parse<'a> for CicamMultistreamCapability {
54 type Error = Error;
55 fn parse(bytes: &'a [u8]) -> Result<Self> {
56 let body = objects::parse_apdu_header(
57 bytes,
58 tag::CICAM_MULTISTREAM_CAPABILITY,
59 "CICAM_multistream_capability",
60 )?;
61 if body.len() < CAPABILITY_BODY {
62 return Err(Error::BufferTooShort {
63 need: CAPABILITY_BODY,
64 have: body.len(),
65 what: "CICAM_multistream_capability",
66 });
67 }
68 Ok(Self {
69 max_local_ts: body[0],
70 max_descramblers: u16::from_be_bytes([body[1], body[2]]),
71 })
72 }
73}
74impl Serialize for CicamMultistreamCapability {
75 type Error = Error;
76 fn serialized_len(&self) -> usize {
77 objects::apdu_len(CAPABILITY_BODY)
78 }
79 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
80 let pos =
81 objects::write_apdu_header(tag::CICAM_MULTISTREAM_CAPABILITY, CAPABILITY_BODY, buf)?;
82 buf[pos] = self.max_local_ts;
83 buf[pos + 1..pos + 3].copy_from_slice(&self.max_descramblers.to_be_bytes());
84 Ok(pos + CAPABILITY_BODY)
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize))]
94pub struct PidSelectRequest {
95 pub critical_for_descrambling: bool,
98 pub pid: u16,
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize))]
106pub struct PidSelectReq {
107 pub lts_id: u8,
109 pub pids: Vec<PidSelectRequest>,
111}
112
113const PID_ENTRY_LEN: usize = 2;
115const PID_MASK: u16 = 0x1FFF;
117const CRITICAL_FLAG_BIT: u8 = 0x20;
119
120impl<'a> Parse<'a> for PidSelectReq {
121 type Error = Error;
122 fn parse(bytes: &'a [u8]) -> Result<Self> {
123 let body = objects::parse_apdu_header(bytes, tag::PID_SELECT_REQ, "PID_select_req")?;
124 if body.len() < 2 {
126 return Err(Error::BufferTooShort {
127 need: 2,
128 have: body.len(),
129 what: "PID_select_req",
130 });
131 }
132 let lts_id = body[0];
133 let num_pid = body[1] as usize;
134 let loop_bytes = &body[2..];
135 if loop_bytes.len() < num_pid * PID_ENTRY_LEN {
136 return Err(Error::BufferTooShort {
137 need: num_pid * PID_ENTRY_LEN,
138 have: loop_bytes.len(),
139 what: "PID_select_req loop",
140 });
141 }
142 let mut pids = Vec::with_capacity(num_pid);
143 for chunk in loop_bytes[..num_pid * PID_ENTRY_LEN].chunks_exact(PID_ENTRY_LEN) {
144 let critical = chunk[0] & CRITICAL_FLAG_BIT != 0;
145 let pid = u16::from_be_bytes([chunk[0], chunk[1]]) & PID_MASK;
146 pids.push(PidSelectRequest {
147 critical_for_descrambling: critical,
148 pid,
149 });
150 }
151 Ok(Self { lts_id, pids })
152 }
153}
154impl Serialize for PidSelectReq {
155 type Error = Error;
156 fn serialized_len(&self) -> usize {
157 objects::apdu_len(2 + self.pids.len() * PID_ENTRY_LEN)
158 }
159 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
160 let body_len = 2 + self.pids.len() * PID_ENTRY_LEN;
161 let mut pos = objects::write_apdu_header(tag::PID_SELECT_REQ, body_len, buf)?;
162 buf[pos] = self.lts_id;
163 buf[pos + 1] = self.pids.len() as u8;
164 pos += 2;
165 for entry in &self.pids {
166 let mut hi = (entry.pid >> 8) as u8 & (PID_MASK >> 8) as u8;
167 if entry.critical_for_descrambling {
168 hi |= CRITICAL_FLAG_BIT;
169 }
170 buf[pos] = hi;
171 buf[pos + 1] = entry.pid as u8;
172 pos += PID_ENTRY_LEN;
173 }
174 Ok(pos)
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183#[cfg_attr(feature = "serde", derive(serde::Serialize))]
184pub struct PidSelectedEntry {
185 pub pid_selected: bool,
187 pub pid: u16,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq)]
193#[cfg_attr(feature = "serde", derive(serde::Serialize))]
194pub struct PidSelectReply {
195 pub lts_id: u8,
197 pub pid_selection: bool,
200 pub pids: Vec<PidSelectedEntry>,
202}
203
204const PID_SELECTED_FLAG_BIT: u8 = 0x20;
206const PID_SELECTION_FLAG_BIT: u8 = 0x01;
208
209impl<'a> Parse<'a> for PidSelectReply {
210 type Error = Error;
211 fn parse(bytes: &'a [u8]) -> Result<Self> {
212 let body = objects::parse_apdu_header(bytes, tag::PID_SELECT_REPLY, "PID_select_reply")?;
213 if body.len() < 3 {
215 return Err(Error::BufferTooShort {
216 need: 3,
217 have: body.len(),
218 what: "PID_select_reply",
219 });
220 }
221 let lts_id = body[0];
222 let pid_selection = body[1] & PID_SELECTION_FLAG_BIT != 0;
223 let num_pid = body[2] as usize;
224 let loop_bytes = &body[3..];
225 if loop_bytes.len() < num_pid * PID_ENTRY_LEN {
226 return Err(Error::BufferTooShort {
227 need: num_pid * PID_ENTRY_LEN,
228 have: loop_bytes.len(),
229 what: "PID_select_reply loop",
230 });
231 }
232 let mut pids = Vec::with_capacity(num_pid);
233 for chunk in loop_bytes[..num_pid * PID_ENTRY_LEN].chunks_exact(PID_ENTRY_LEN) {
234 let selected = chunk[0] & PID_SELECTED_FLAG_BIT != 0;
235 let pid = u16::from_be_bytes([chunk[0], chunk[1]]) & PID_MASK;
236 pids.push(PidSelectedEntry {
237 pid_selected: selected,
238 pid,
239 });
240 }
241 Ok(Self {
242 lts_id,
243 pid_selection,
244 pids,
245 })
246 }
247}
248impl Serialize for PidSelectReply {
249 type Error = Error;
250 fn serialized_len(&self) -> usize {
251 objects::apdu_len(3 + self.pids.len() * PID_ENTRY_LEN)
252 }
253 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
254 let body_len = 3 + self.pids.len() * PID_ENTRY_LEN;
255 let mut pos = objects::write_apdu_header(tag::PID_SELECT_REPLY, body_len, buf)?;
256 buf[pos] = self.lts_id;
257 buf[pos + 1] = if self.pid_selection {
258 PID_SELECTION_FLAG_BIT
259 } else {
260 0
261 };
262 buf[pos + 2] = self.pids.len() as u8;
263 pos += 3;
264 for entry in &self.pids {
265 let mut hi = (entry.pid >> 8) as u8 & (PID_MASK >> 8) as u8;
266 if entry.pid_selected {
267 hi |= PID_SELECTED_FLAG_BIT;
268 }
269 buf[pos] = hi;
270 buf[pos + 1] = entry.pid as u8;
271 pos += PID_ENTRY_LEN;
272 }
273 Ok(pos)
274 }
275}
276
277#[derive(Debug, Clone, PartialEq, Eq)]
279#[cfg_attr(feature = "serde", derive(serde::Serialize))]
280#[non_exhaustive]
281pub enum MultistreamApdu {
282 CicamMultistreamCapability(CicamMultistreamCapability),
284 PidSelectReq(PidSelectReq),
286 PidSelectReply(PidSelectReply),
288}
289
290impl MultistreamApdu {
291 pub fn parse(body: &[u8]) -> Result<Self> {
293 if body.len() < 3 {
294 return Err(Error::BufferTooShort {
295 need: 3,
296 have: body.len(),
297 what: "multistream apdu_tag",
298 });
299 }
300 let t = ApduTag::from_bytes(body[0], body[1], body[2]);
301 match t {
302 tag::CICAM_MULTISTREAM_CAPABILITY => Ok(Self::CicamMultistreamCapability(
303 CicamMultistreamCapability::parse(body)?,
304 )),
305 tag::PID_SELECT_REQ => Ok(Self::PidSelectReq(PidSelectReq::parse(body)?)),
306 tag::PID_SELECT_REPLY => Ok(Self::PidSelectReply(PidSelectReply::parse(body)?)),
307 _ => Err(Error::UnexpectedApduTag {
308 got: t.as_u24(),
309 expected: tag::CICAM_MULTISTREAM_CAPABILITY.as_u24(),
310 what: "multistream",
311 }),
312 }
313 }
314}
315
316impl Serialize for MultistreamApdu {
317 type Error = Error;
318 fn serialized_len(&self) -> usize {
319 match self {
320 Self::CicamMultistreamCapability(o) => o.serialized_len(),
321 Self::PidSelectReq(o) => o.serialized_len(),
322 Self::PidSelectReply(o) => o.serialized_len(),
323 }
324 }
325 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
326 match self {
327 Self::CicamMultistreamCapability(o) => o.serialize_into(buf),
328 Self::PidSelectReq(o) => o.serialize_into(buf),
329 Self::PidSelectReply(o) => o.serialize_into(buf),
330 }
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337
338 #[test]
339 fn capability_round_trips_and_bites() {
340 let cap = CicamMultistreamCapability {
341 max_local_ts: 0x04,
342 max_descramblers: 0x0102,
343 };
344 let bytes = cap.to_bytes();
345 assert_eq!(bytes, [0x9F, 0x92, 0x00, 0x03, 0x04, 0x01, 0x02]);
347 assert_eq!(CicamMultistreamCapability::parse(&bytes).unwrap(), cap);
348 let mut other = cap;
349 other.max_descramblers = 0x0103;
350 assert_ne!(bytes, other.to_bytes());
351 }
352
353 #[test]
354 fn pid_select_req_round_trips_and_bites() {
355 let req = PidSelectReq {
356 lts_id: 0x07,
357 pids: alloc::vec![
358 PidSelectRequest {
359 critical_for_descrambling: true,
360 pid: 0x0123,
361 },
362 PidSelectRequest {
363 critical_for_descrambling: false,
364 pid: 0x1FFE,
365 },
366 ],
367 };
368 let bytes = req.to_bytes();
369 assert_eq!(
373 bytes,
374 [0x9F, 0x92, 0x01, 0x06, 0x07, 0x02, 0x21, 0x23, 0x1F, 0xFE]
375 );
376 assert_eq!(PidSelectReq::parse(&bytes).unwrap(), req);
377 let mut other = req.clone();
379 other.pids[0].critical_for_descrambling = false;
380 assert_ne!(bytes, other.to_bytes());
381 assert_eq!(other.to_bytes()[6], 0x01);
382 }
383
384 #[test]
385 fn pid_select_req_empty_loop() {
386 let req = PidSelectReq {
387 lts_id: 0x00,
388 pids: Vec::new(),
389 };
390 let bytes = req.to_bytes();
391 assert_eq!(bytes, [0x9F, 0x92, 0x01, 0x02, 0x00, 0x00]);
392 assert_eq!(PidSelectReq::parse(&bytes).unwrap(), req);
393 }
394
395 #[test]
396 fn pid_select_reply_round_trips_with_two_entries() {
397 let reply = PidSelectReply {
398 lts_id: 0x05,
399 pid_selection: true,
400 pids: alloc::vec![
401 PidSelectedEntry {
402 pid_selected: true,
403 pid: 0x0064,
404 },
405 PidSelectedEntry {
406 pid_selected: false,
407 pid: 0x00C8,
408 },
409 ],
410 };
411 let bytes = reply.to_bytes();
412 assert_eq!(
416 bytes,
417 [
418 0x9F, 0x92, 0x02, 0x07, 0x05, 0x01, 0x02, 0x20, 0x64, 0x00, 0xC8
419 ]
420 );
421 assert_eq!(PidSelectReply::parse(&bytes).unwrap(), reply);
422 let mut other = reply.clone();
424 other.pid_selection = false;
425 assert_eq!(other.to_bytes()[5], 0x00);
426 assert_ne!(bytes, other.to_bytes());
427 }
428
429 #[test]
430 fn pid_select_reply_whole_ts() {
431 let reply = PidSelectReply {
432 lts_id: 0x01,
433 pid_selection: false,
434 pids: Vec::new(),
435 };
436 let bytes = reply.to_bytes();
437 assert_eq!(bytes, [0x9F, 0x92, 0x02, 0x03, 0x01, 0x00, 0x00]);
438 assert_eq!(PidSelectReply::parse(&bytes).unwrap(), reply);
439 }
440
441 #[test]
442 fn dispatch_routes_each_tag() {
443 let cap = CicamMultistreamCapability {
444 max_local_ts: 1,
445 max_descramblers: 1,
446 }
447 .to_bytes();
448 assert!(matches!(
449 MultistreamApdu::parse(&cap).unwrap(),
450 MultistreamApdu::CicamMultistreamCapability(_)
451 ));
452 let reply = PidSelectReply {
453 lts_id: 0,
454 pid_selection: false,
455 pids: Vec::new(),
456 }
457 .to_bytes();
458 let parsed = MultistreamApdu::parse(&reply).unwrap();
459 assert!(matches!(parsed, MultistreamApdu::PidSelectReply(_)));
460 assert_eq!(parsed.to_bytes(), reply);
461 }
462}