1use crate::xdma::{Error as XdmaError, XdmaOps};
2use enum_iterator::Sequence;
3use log::debug;
4use num_enum::TryFromPrimitive;
5use num_enum::TryFromPrimitiveError;
6use std::collections::BTreeSet;
7use std::convert::TryFrom;
8use std::iter;
9use std::thread;
10use std::time::Duration;
11
12pub type Result<T> = std::result::Result<T, Error>;
13
14#[derive(Debug)]
15pub enum Error {
16 XdmaFailed(XdmaError),
17 HostStatusNotReady,
18 MailboxNotAvailable,
19 CmsRegMaskNotAsExpected,
20 HostMsgError(u32),
21 CardInfoParseError(CardInfoParseError),
22}
23
24impl From<CardInfoParseError> for Error {
25 fn from(e: CardInfoParseError) -> Self {
26 Self::CardInfoParseError(e)
27 }
28}
29
30#[derive(Copy, Clone, Debug, Sequence, PartialEq)]
32#[repr(u64)]
33pub enum CmsReg {
34 MicroblazeResetN = 0x2_0000,
36 FwVersion = 0x2_8004,
37 CmsStatus = 0x2_8008,
38 Error = 0x2_800c,
39 ProfileName = 0x2_8014,
40 Control = 0x2_8018,
41 Pex12VMax = 0x2_8020,
43 Pex12VAvg = 0x2_8024,
44 Pex12VInst = 0x2_8028,
45 Pex3V3Max = 0x2_802c,
46 Pex3V3Avg = 0x2_8030,
47 Pex3V3Inst = 0x2_8034,
48 Aux12VMax = 0x2_8044,
52 Aux12VAvg = 0x2_8048,
53 Aux12VInst = 0x2_804c,
54 Sys5V5Max = 0x2_805c,
55 Sys5V5Avg = 0x2_8060,
56 Sys5V5Inst = 0x2_8064,
57 Vcc1V8Max = 0x2_8074,
61 Vcc1V8Avg = 0x2_8078,
62 Vcc1V8Inst = 0x2_807c,
63 Mgt0V9AVccMax = 0x2_8098,
67 Mgt0V9AVccAvg = 0x2_809c,
68 Mgt0V9AVccInst = 0x2_80A0,
69 MgtAVttMax = 0x2_80b0,
73 MgtAVttAvg = 0x2_80b4,
74 MgtAVttInst = 0x2_80b8,
75 Pex12VCurrentInMax = 0x2_80c8,
80 Pex12VCurrentInAvg = 0x2_80cc,
81 Pex12VCurrentInInst = 0x2_80d0,
82 Aux12VCurrentInMax = 0x2_80d4,
83 Aux12VCurrentInAvg = 0x2_80d8,
84 Aux12VCurrentInInst = 0x2_80dc,
85 VccIntMax = 0x2_80e0,
86 VccIntAvg = 0x2_80e4,
87 VccIntInst = 0x2_80e8,
88 VccCurrentMax = 0x2_80ec,
89 VccCurrentAvg = 0x2_80f0,
90 VccCurrentInst = 0x2_80f4,
91 FpgaTempMax = 0x2_80f8,
93 FpgaTempAvg = 0x2_80fc,
94 FpgaTempInst = 0x2_8100,
95 Hbm0TempMax = 0x2_8260,
108 Hbm0TempAvg = 0x2_8264,
109 Hbm0TempInst = 0x2_8268,
110 Vcc3V3Max = 0x2_826c,
111 Vcc3V3Avg = 0x2_8270,
112 Vcc3V3Inst = 0x2_8274,
113 Pex3V3CurrentInMax = 0x2_8278,
114 Pex3V3CurrentInAvg = 0x2_827c,
115 Pex3V3CurrentInInst = 0x2_8280,
116 VccIntCurrentMax = 0x2_8284,
117 VccIntCurrentAvg = 0x2_8288,
118 VccIntCurrentInst = 0x2_828c,
119 Hbm1TempMax = 0x2_82b4,
120 Hbm1TempAvg = 0x2_82b8,
121 Hbm1TempInst = 0x2_82bc,
122 VccIntTempMax = 0x2_82cc,
126 VccIntTempAvg = 0x2_82d0,
127 VccIntTempInst = 0x2_82d4,
128 HostMsgOffset = 0x2_8300,
137 HostMsgError = 0x2_8304,
138 HostStatus = 0x2_830c,
140}
141
142#[repr(u32)]
143pub enum ControlRegBit {
144 MaxAgvValuesReset = 1,
145 ErrorRegReset = 1 << 1,
146 MailboxStatus = 1 << 5,
147 MicroblazeReset = 1 << 6,
148 QsfpGpioEnable = 1 << 26,
149 HbmTempMonitorEnable = 1 << 27,
150}
151
152#[repr(u32)]
153pub enum MailboxMsgOpcode {
154 CardInfo = 4,
159}
160
161#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive)]
162#[repr(u8)]
163pub enum CardInfoKey {
164 SerialNumber = 0x21,
165 MacAddress0 = 0x22,
166 MacAddress1 = 0x23,
167 MacAddress2 = 0x24,
168 MacAddress3 = 0x25,
169 CardRev = 0x26,
170 CardName = 0x27,
171 SatelliteVersion = 0x28,
172 TotalPowerAvail = 0x29,
173 FanPresence = 0x2a,
174 ConfigMode = 0x2b,
175 NewMacScheme = 0x4b,
176 CageType0 = 0x50,
177 CageType1 = 0x51,
178 CageType2 = 0x52,
179 CageType3 = 0x53,
180}
181
182pub type OldMacAddress = [u8; 16];
183pub type MacAddress = [u8; 6];
184
185#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive)]
186#[repr(u8)]
187pub enum TotalPowerAvail {
188 Power75W,
189 Power150W,
190 Power225W,
191 Power300W,
192}
193
194#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive)]
195#[repr(u8)]
196pub enum ConfigMode {
197 SlaveSerialX1,
198 SlaveSelectMapX8,
199 SlaveMapX16,
200 SlaveSelectMapX32,
201 JtagBoudaryScanX1,
202 MasterSpiX1,
203 MasterSpiX2,
204 MasterSpiX4,
205 MasterSpiX8,
206 MasterSpiX16,
207 MasterSerialX1,
208 MasterSelectMapX8,
209 MasterSelectMapX16,
210}
211
212#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive)]
213#[repr(u8)]
214pub enum CageType {
215 Qsfp,
216 Dsfp,
217 Sfp,
218}
219
220#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
221pub enum CardInfoItem {
222 SerialNumber(Vec<u8>),
223 MacAddress0(OldMacAddress),
224 MacAddress1(OldMacAddress),
225 MacAddress2(OldMacAddress),
226 MacAddress3(OldMacAddress),
227 CardRev(Vec<u8>),
228 CardName(Vec<u8>),
229 SatelliteVersion(Vec<u8>),
230 TotalPowerAvail(TotalPowerAvail),
231 FanPresence(u8),
232 ConfigMode(ConfigMode),
233 NewMacScheme(u8, MacAddress),
234 CageType0(CageType),
235 CageType1(CageType),
236 CageType2(CageType),
237 CageType3(CageType),
238}
239
240#[derive(Copy, Clone, Debug, PartialEq)]
241pub enum CardInfoItemParseError {
242 IncompleteInput,
243 IncorrectLength,
244 NonNullTerminator,
245 CardInfoKey(TryFromPrimitiveError<CardInfoKey>),
246 TotalPowerAvail(TryFromPrimitiveError<TotalPowerAvail>),
247 ConfigMode(TryFromPrimitiveError<ConfigMode>),
248 CageType(TryFromPrimitiveError<CageType>),
249}
250
251impl From<TryFromPrimitiveError<CardInfoKey>> for CardInfoItemParseError {
252 fn from(e: TryFromPrimitiveError<CardInfoKey>) -> Self {
253 Self::CardInfoKey(e)
254 }
255}
256
257impl From<TryFromPrimitiveError<TotalPowerAvail>> for CardInfoItemParseError {
258 fn from(e: TryFromPrimitiveError<TotalPowerAvail>) -> Self {
259 Self::TotalPowerAvail(e)
260 }
261}
262
263impl From<TryFromPrimitiveError<ConfigMode>> for CardInfoItemParseError {
264 fn from(e: TryFromPrimitiveError<ConfigMode>) -> Self {
265 Self::ConfigMode(e)
266 }
267}
268
269impl From<TryFromPrimitiveError<CageType>> for CardInfoItemParseError {
270 fn from(e: TryFromPrimitiveError<CageType>) -> Self {
271 Self::CageType(e)
272 }
273}
274
275impl TryFrom<&[u8]> for CardInfoItem {
276 type Error = CardInfoItemParseError;
277
278 fn try_from(input: &[u8]) -> std::result::Result<Self, Self::Error> {
279 let mut iter = input.iter();
280 let mut next = || iter.next().ok_or(CardInfoItemParseError::IncompleteInput);
281 let key = CardInfoKey::try_from(*next()?)?;
282 let len = *next()? as usize;
283 match key {
284 CardInfoKey::SerialNumber => {
285 if len == 0 {
286 return Err(CardInfoItemParseError::IncorrectLength);
287 }
288 let mut v = vec![];
289 for _ in 0..len - 1 {
290 v.push(*next()?);
291 }
292 if *next()? != 0 {
293 return Err(CardInfoItemParseError::NonNullTerminator);
294 }
295 Ok(Self::SerialNumber(v))
296 }
297 CardInfoKey::MacAddress0 => {
298 if len != 18 {
299 return Err(CardInfoItemParseError::IncorrectLength);
300 }
301 let mut v = vec![];
302 for _ in 0..len - 1 {
303 v.push(*next()?);
304 }
305 if *next()? != 0 {
306 return Err(CardInfoItemParseError::NonNullTerminator);
307 }
308 let old_mac = v
309 .try_into()
310 .map_err(|_| CardInfoItemParseError::IncorrectLength)?;
311 Ok(Self::MacAddress0(old_mac))
312 }
313 CardInfoKey::MacAddress1 => {
314 if len != 18 {
315 return Err(CardInfoItemParseError::IncorrectLength);
316 }
317 let mut v = vec![];
318 for _ in 0..len - 1 {
319 v.push(*next()?);
320 }
321 if *next()? != 0 {
322 return Err(CardInfoItemParseError::NonNullTerminator);
323 }
324 let old_mac = v
325 .try_into()
326 .map_err(|_| CardInfoItemParseError::IncorrectLength)?;
327 Ok(Self::MacAddress1(old_mac))
328 }
329 CardInfoKey::MacAddress2 => {
330 if len != 18 {
331 return Err(CardInfoItemParseError::IncorrectLength);
332 }
333 let mut v = vec![];
334 for _ in 0..len - 1 {
335 v.push(*next()?);
336 }
337 if *next()? != 0 {
338 return Err(CardInfoItemParseError::NonNullTerminator);
339 }
340 let old_mac = v
341 .try_into()
342 .map_err(|_| CardInfoItemParseError::IncorrectLength)?;
343 Ok(Self::MacAddress2(old_mac))
344 }
345 CardInfoKey::MacAddress3 => {
346 if len != 18 {
347 return Err(CardInfoItemParseError::IncorrectLength);
348 }
349 let mut v = vec![];
350 for _ in 0..len - 1 {
351 v.push(*next()?);
352 }
353 if *next()? != 0 {
354 return Err(CardInfoItemParseError::NonNullTerminator);
355 }
356 let old_mac = v
357 .try_into()
358 .map_err(|_| CardInfoItemParseError::IncorrectLength)?;
359 Ok(Self::MacAddress3(old_mac))
360 }
361 CardInfoKey::CardRev => {
362 if len == 0 {
363 return Err(CardInfoItemParseError::IncorrectLength);
364 }
365 let mut v = vec![];
366 for _ in 0..len - 1 {
367 v.push(*next()?);
368 }
369 if *next()? != 0 {
370 return Err(CardInfoItemParseError::NonNullTerminator);
371 }
372 Ok(Self::CardRev(v))
373 }
374 CardInfoKey::CardName => {
375 if len == 0 {
376 return Err(CardInfoItemParseError::IncorrectLength);
377 }
378 let mut v = vec![];
379 for _ in 0..len - 1 {
380 v.push(*next()?);
381 }
382 if *next()? != 0 {
383 return Err(CardInfoItemParseError::NonNullTerminator);
384 }
385 Ok(Self::CardName(v))
386 }
387 CardInfoKey::SatelliteVersion => {
388 if len == 0 {
389 return Err(CardInfoItemParseError::IncorrectLength);
390 }
391 let mut v = vec![];
392 for _ in 0..len - 1 {
393 v.push(*next()?);
394 }
395 if *next()? != 0 {
396 return Err(CardInfoItemParseError::NonNullTerminator);
397 }
398 Ok(Self::SatelliteVersion(v))
399 }
400 CardInfoKey::TotalPowerAvail => {
401 if len != 1 {
402 return Err(CardInfoItemParseError::IncorrectLength);
403 }
404 Ok(Self::TotalPowerAvail(TotalPowerAvail::try_from_primitive(
405 *next()?,
406 )?))
407 }
408 CardInfoKey::FanPresence => {
409 if len != 1 {
410 return Err(CardInfoItemParseError::IncorrectLength);
411 }
412 Ok(Self::FanPresence(*next()?))
413 }
414 CardInfoKey::ConfigMode => {
415 if len != 1 {
416 return Err(CardInfoItemParseError::IncorrectLength);
417 }
418 Ok(Self::ConfigMode(ConfigMode::try_from_primitive(*next()?)?))
419 }
420 CardInfoKey::NewMacScheme => {
421 if len != 8 {
422 return Err(CardInfoItemParseError::IncorrectLength);
423 }
424 let num_addresses = *next()?;
425 next()?;
427 let mut v = vec![];
428 for _ in 0..6 {
429 v.push(*next()?);
430 }
431 let mac = v
432 .try_into()
433 .map_err(|_| CardInfoItemParseError::IncorrectLength)?;
434 Ok(Self::NewMacScheme(num_addresses, mac))
435 }
436 CardInfoKey::CageType0 => {
437 if len != 1 {
438 return Err(CardInfoItemParseError::IncorrectLength);
439 }
440 Ok(Self::CageType0(CageType::try_from_primitive(*next()?)?))
441 }
442 CardInfoKey::CageType1 => {
443 if len != 1 {
444 return Err(CardInfoItemParseError::IncorrectLength);
445 }
446 Ok(Self::CageType1(CageType::try_from_primitive(*next()?)?))
447 }
448 CardInfoKey::CageType2 => {
449 if len != 1 {
450 return Err(CardInfoItemParseError::IncorrectLength);
451 }
452 Ok(Self::CageType2(CageType::try_from_primitive(*next()?)?))
453 }
454 CardInfoKey::CageType3 => {
455 if len != 1 {
456 return Err(CardInfoItemParseError::IncorrectLength);
457 }
458 Ok(Self::CageType3(CageType::try_from_primitive(*next()?)?))
459 }
460 }
461 }
462}
463
464#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
465pub struct CardInfo(pub BTreeSet<CardInfoItem>);
466
467#[derive(Copy, Clone, Debug, PartialEq)]
468pub enum CardInfoParseError {
469 CardInfoItem(CardInfoItemParseError),
470 ItemLengthOutOfBounds,
471}
472
473impl From<CardInfoItemParseError> for CardInfoParseError {
474 fn from(e: CardInfoItemParseError) -> Self {
475 Self::CardInfoItem(e)
476 }
477}
478
479impl TryFrom<&[u8]> for CardInfo {
480 type Error = CardInfoParseError;
481
482 fn try_from(input: &[u8]) -> std::result::Result<Self, Self::Error> {
483 let mut set = BTreeSet::new();
484 let mut parsed_len = 0;
485 while parsed_len + 2 < input.len() {
486 let item_len = input[parsed_len + 1];
487 let to_parse_len = item_len as usize + 2;
488 let next_item_pos = parsed_len + to_parse_len;
489 debug!(
490 "parsed_len {}, item_len {}, next_item_pos {}",
491 parsed_len, item_len, next_item_pos
492 );
493 if next_item_pos - 1 > input.len() {
494 return Err(CardInfoParseError::ItemLengthOutOfBounds);
495 }
496 let item = CardInfoItem::try_from(&input[parsed_len..next_item_pos])?;
497 set.insert(item);
498 parsed_len += to_parse_len;
499 }
500 Ok(Self(set))
501 }
502}
503
504pub trait CardMgmtSys {
506 fn get_cms_addr(&self, addr: u64) -> Result<u32>;
508
509 fn set_cms_addr(&self, addr: u64, value: u32) -> Result<()>;
511
512 fn get_cms_reg(&self, reg: CmsReg) -> Result<u32> {
514 self.get_cms_addr(reg as u64)
515 }
516
517 fn set_cms_reg(&self, reg: CmsReg, value: u32) -> Result<()> {
519 self.set_cms_addr(reg as u64, value)
520 }
521
522 fn get_cms_control_reg(&self) -> Result<u32> {
524 self.get_cms_reg(CmsReg::Control)
525 }
526
527 fn set_cms_control_reg(&self, value: u32) -> Result<()> {
529 self.set_cms_reg(CmsReg::Control, value)
530 }
531
532 fn poll_cms_reg_mask(&self, reg: CmsReg, mask: u32, expected: u32, n: usize) -> Result<()> {
535 iter::repeat_with(|| self.get_cms_reg(reg).ok())
536 .take(n)
537 .position(|ready| ready.map(|ready| ready & mask) == Some(expected))
538 .map(|_| ())
539 .ok_or(Error::CmsRegMaskNotAsExpected)
540 }
541
542 fn poll_cms_reg_mask_sleep(
546 &self,
547 reg: CmsReg,
548 mask: u32,
549 expected: u32,
550 n: usize,
551 duration: Duration,
552 ) -> Result<usize> {
553 iter::repeat_with(|| {
554 thread::sleep(duration);
555 self.get_cms_reg(reg).ok()
556 })
557 .take(n)
558 .position(|ready| ready.map(|ready| ready & mask) == Some(expected))
559 .map(|pos| pos + 1)
560 .ok_or(Error::CmsRegMaskNotAsExpected)
561 }
562
563 fn poll_cms_reg_clear(&self, reg: CmsReg, mask: u32, n: usize) -> Result<()> {
566 self.poll_cms_reg_mask(reg, mask, 0, n)
567 }
568
569 fn poll_cms_reg_set(&self, reg: CmsReg, mask: u32, n: usize) -> Result<()> {
572 self.poll_cms_reg_mask(reg, mask, mask, n)
573 }
574}
575
576pub trait CardMgmtSysParam {
578 const BASE_ADDR: u64;
579}
580
581pub trait CardMgmtOps {
582 fn init_cms(&self) -> Result<()>;
584
585 fn expect_ready_host_status(&self, ms: usize) -> Result<usize>;
588
589 fn enable_hbm_temp_monitoring(&self) -> Result<()>;
591
592 fn get_mailbox_offset(&self) -> Result<u64>;
594
595 fn get_card_info(&self) -> Result<CardInfo>;
600}
601
602impl<T> CardMgmtOps for T
603where
604 T: XdmaOps + CardMgmtSysParam,
605{
606 fn init_cms(&self) -> Result<()> {
607 self.set_cms_reg(CmsReg::MicroblazeResetN, 1)
608 }
609
610 fn expect_ready_host_status(&self, ms: usize) -> Result<usize> {
611 self.poll_cms_reg_mask_sleep(CmsReg::HostStatus, 1, 1, ms, Duration::from_millis(1))
612 .map_err(|_| Error::HostStatusNotReady)
613 }
614
615 fn enable_hbm_temp_monitoring(&self) -> Result<()> {
616 let v = self.get_cms_control_reg()?;
617 self.set_cms_control_reg(v | ControlRegBit::HbmTempMonitorEnable as u32)
618 }
619
620 fn get_mailbox_offset(&self) -> Result<u64> {
621 let control = self.get_cms_control_reg()?;
622 if 0 != control & ControlRegBit::MailboxStatus as u32 {
623 return Err(Error::MailboxNotAvailable);
624 }
625 let v = self.get_cms_reg(CmsReg::HostMsgOffset)?;
626 Ok(0x2_8000u64 + v as u64)
627 }
628
629 fn get_card_info(&self) -> Result<CardInfo> {
644 let mbox_offset = self.get_mailbox_offset()?;
645 debug!("mbox_offset 0x{:x}", mbox_offset);
646 self.set_cms_addr(mbox_offset, (MailboxMsgOpcode::CardInfo as u32) << 24)?;
647 let control = self.get_cms_control_reg()?;
648 self.set_cms_control_reg(control | ControlRegBit::MailboxStatus as u32)?;
649 self.poll_cms_reg_mask_sleep(
651 CmsReg::Control,
652 ControlRegBit::MailboxStatus as u32,
653 0,
654 100,
655 Duration::from_millis(10),
656 )?;
657
658 let error = self.get_cms_reg(CmsReg::HostMsgError)?;
659 if error != 0 {
660 return Err(Error::HostMsgError(error));
661 }
662
663 let len = self.get_cms_addr(mbox_offset)? & 0xfff;
664 let mut info_bytes = Vec::with_capacity(len as usize);
665 let mut data_offset = 4;
666 let mut remaining = len as isize;
667 while remaining > 0 {
668 let w = self.get_cms_addr(mbox_offset + data_offset as u64)?;
669 let bytes = w.to_le_bytes();
670 let num_bytes = remaining.min(4) as usize;
671 info_bytes.extend_from_slice(&bytes[..num_bytes]);
672 data_offset += 4;
673 remaining -= 4;
674 }
675 Ok(CardInfo::try_from(info_bytes.as_slice())?)
676 }
677}
678
679impl<T> CardMgmtSys for T
680where
681 T: XdmaOps + CardMgmtSysParam,
682{
683 fn get_cms_addr(&self, addr: u64) -> Result<u32> {
684 let mut data = [0u8; 4];
685 self.shell_read(&mut data, T::BASE_ADDR + addr)
686 .map_err(Error::XdmaFailed)?;
687 Ok(u32::from_le_bytes(data))
688 }
689
690 fn set_cms_addr(&self, addr: u64, value: u32) -> Result<()> {
691 let data = value.to_le_bytes();
692 self.shell_write(&data, T::BASE_ADDR + addr)
693 .map_err(Error::XdmaFailed)
694 }
695}
696
697#[cfg(test)]
698mod test {
699 use super::*;
700 use test_log::test;
701
702 #[test]
703 fn parse_pg348_example_card_info() {
704 let card_info_bytes: Vec<u8> = vec![
705 0x27, 0x0d, 0x41, 0x4c, 0x56, 0x45, 0x4f, 0x20, 0x55, 0x35, 0x30, 0x20, 0x50, 0x51,
706 0x00, 0x26, 0x02, 0x31, 0x00, 0x21, 0x0d, 0x35, 0x30, 0x31, 0x32, 0x31, 0x31, 0x31,
707 0x39, 0x43, 0x53, 0x50, 0x4d, 0x00, 0x4b, 0x08, 0x04, 0x00, 0x00, 0x0a, 0x35, 0x05,
708 0x0f, 0xd8, 0x2a, 0x01, 0x50, 0x2b, 0x01, 0x07, 0x29, 0x01, 0x00, 0x28, 0x04, 0x35,
709 0x2e, 0x30, 0x00,
710 ];
711 let expected_card_info = CardInfo(
712 vec![
713 CardInfoItem::SerialNumber(b"50121119CSPM".to_vec()),
714 CardInfoItem::CardRev(b"1".to_vec()),
715 CardInfoItem::CardName(b"ALVEO U50 PQ".to_vec()),
716 CardInfoItem::SatelliteVersion(b"5.0".to_vec()),
717 CardInfoItem::TotalPowerAvail(TotalPowerAvail::Power75W),
718 CardInfoItem::FanPresence(b'P'),
719 CardInfoItem::ConfigMode(ConfigMode::MasterSpiX4),
720 CardInfoItem::NewMacScheme(4, [0x00, 0x0a, 0x35, 0x05, 0x0f, 0xd8]),
721 ]
722 .into_iter()
723 .collect(),
724 );
725 assert_eq!(
726 CardInfo::try_from(card_info_bytes.as_slice()),
727 Ok(expected_card_info)
728 );
729 }
730
731 #[test]
732 fn card_info_sernum_zero_length() {
733 let card_info_bytes: Vec<u8> = vec![0x21, 0x00, 0x31, 0x32, 0x33, 0x00];
734 assert_eq!(
735 CardInfo::try_from(card_info_bytes.as_slice()),
736 Err(CardInfoParseError::CardInfoItem(
737 CardInfoItemParseError::IncorrectLength
738 ))
739 );
740 }
741
742 #[test]
743 fn card_info_sernum_length_out_of_bounds() {
744 let card_info_bytes: Vec<u8> = vec![0x21, 0xff, 0x31, 0x32, 0x33, 0x00];
745 assert_eq!(
746 CardInfo::try_from(card_info_bytes.as_slice()),
747 Err(CardInfoParseError::ItemLengthOutOfBounds)
748 );
749 }
750
751 #[test]
752 fn card_info_sernum_non_null_terminator() {
753 let card_info_bytes: Vec<u8> = vec![0x21, 0x04, 0x31, 0x32, 0x33, 0xff];
754 assert_eq!(
755 CardInfo::try_from(card_info_bytes.as_slice()),
756 Err(CardInfoParseError::CardInfoItem(
757 CardInfoItemParseError::NonNullTerminator
758 ))
759 );
760 }
761}