1use heterob::{
10 bit_numbering::Lsb,
11 endianness::{Le, LeBytesTryInto},
12 Seq, P2, P3, U8,
13};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct PlugAndPlayResource<'a> {
18 data: &'a [u8],
19}
20
21impl<'a> PlugAndPlayResource<'a> {
22 pub fn new(data: &'a [u8]) -> Self {
23 Self { data }
24 }
25}
26
27impl<'a> Iterator for PlugAndPlayResource<'a> {
28 type Item = Resource<'a>;
29
30 fn next(&mut self) -> Option<Self::Item> {
31 let (first, data) = self.data.split_first()?;
32 let Lsb((large_item_name, is_large_item)) = P2::<_, 7, 1>(*first).into();
33 let Lsb((small_item_len, small_item_name)) = P2::<u8, 3, 4>(large_item_name).into();
34 let _: u8 = small_item_len;
35 let (resource, tail) = if is_large_item {
36 let Seq {
37 head: large_item_len,
38 tail,
39 } = data.le_bytes_try_into().ok()?;
40 if tail.len() < large_item_len as usize {
41 return None;
42 }
43 let (data, tail) = tail.split_at(large_item_len as usize);
44 let item = match large_item_name {
45 0x01 => LargeItem::MemoryRangeDescriptor,
46 0x02 => LargeItem::IdentifierStringAnsi(std::str::from_utf8(data).ok()?),
47 0x03 => LargeItem::IdentifierStringUnicode(std::str::from_utf8(data).ok()?),
48 0x04 => LargeItem::VendorDefined,
49 0x05 => LargeItem::MemoryRangeDescriptor32bit,
50 0x06 => LargeItem::FixedLocationMemoryRangeDescriptor32bit,
51 0x10 => LargeItem::VitalProductDataRo(VitalProductDataRo::new(data)),
52 0x11 => LargeItem::VitalProductDataRw(VitalProductDataRw::new(data)),
53 v => LargeItem::Reserved(v),
54 };
55 (
56 Resource::Large(Large {
57 item,
58 length: large_item_len,
59 }),
60 tail,
61 )
62 } else {
63 if data.len() < small_item_len as usize {
64 return None;
65 }
66 let (_data, tail) = data.split_at(small_item_len as usize);
67 let item = match small_item_name {
68 0x01 => SmallItem::PlugAndPlayVersionNumber,
69 0x02 => SmallItem::LogicalDeviceId,
70 0x03 => SmallItem::CompatibleDeviceId,
71 0x04 => SmallItem::IrqFormat,
72 0x05 => SmallItem::DmaFormat,
73 0x06 => SmallItem::StartDependentFunction,
74 0x07 => SmallItem::EndDependentFunction,
75 0x08 => SmallItem::IoPortDescriptor,
76 0x09 => SmallItem::FixedLocationIoPortDescriptor,
77 0x0E => SmallItem::VendorDefined,
78 0x0F => SmallItem::End,
79 v => SmallItem::Reserved(v),
80 };
81 (
82 Resource::Small(Small {
83 item,
84 length: small_item_len,
85 }),
86 tail,
87 )
88 };
89 self.data = tail;
90 Some(resource)
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq)]
97pub enum Resource<'a> {
98 Small(Small),
99 Large(Large<'a>),
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
104pub struct Small {
105 pub item: SmallItem,
106 pub length: u8,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq)]
111pub enum SmallItem {
112 PlugAndPlayVersionNumber,
114 LogicalDeviceId,
116 CompatibleDeviceId,
118 IrqFormat,
120 DmaFormat,
122 StartDependentFunction,
124 EndDependentFunction,
126 IoPortDescriptor,
128 FixedLocationIoPortDescriptor,
130 Reserved(u8),
132 VendorDefined,
134 End,
136}
137
138impl SmallItem {
139 pub fn value(&self) -> u8 {
140 match self {
141 SmallItem::PlugAndPlayVersionNumber => 0x01,
142 SmallItem::LogicalDeviceId => 0x02,
143 SmallItem::CompatibleDeviceId => 0x03,
144 SmallItem::IrqFormat => 0x04,
145 SmallItem::DmaFormat => 0x05,
146 SmallItem::StartDependentFunction => 0x06,
147 SmallItem::EndDependentFunction => 0x07,
148 SmallItem::IoPortDescriptor => 0x08,
149 SmallItem::FixedLocationIoPortDescriptor => 0x09,
150 SmallItem::VendorDefined => 0x0E,
151 SmallItem::End => 0x0F,
152 SmallItem::Reserved(v) => *v,
153 }
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq)]
159pub struct Large<'a> {
160 pub item: LargeItem<'a>,
161 pub length: u16,
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
166pub enum LargeItem<'a> {
167 MemoryRangeDescriptor,
169 IdentifierStringAnsi(&'a str),
171 IdentifierStringUnicode(&'a str),
173 VendorDefined,
175 MemoryRangeDescriptor32bit,
177 FixedLocationMemoryRangeDescriptor32bit,
179 VitalProductDataRo(VitalProductDataRo<'a>),
181 VitalProductDataRw(VitalProductDataRw<'a>),
183 Reserved(u8),
185}
186
187impl<'a> LargeItem<'a> {
188 pub fn value(&self) -> u8 {
189 match self {
190 LargeItem::MemoryRangeDescriptor => 0x01,
191 LargeItem::IdentifierStringAnsi(_) => 0x02,
192 LargeItem::IdentifierStringUnicode(_) => 0x03,
193 LargeItem::VendorDefined => 0x04,
194 LargeItem::MemoryRangeDescriptor32bit => 0x05,
195 LargeItem::FixedLocationMemoryRangeDescriptor32bit => 0x06,
196 LargeItem::VitalProductDataRo(_) => 0x10,
197 LargeItem::VitalProductDataRw(_) => 0x11,
198 LargeItem::Reserved(v) => *v,
199 }
200 }
201}
202
203#[derive(Debug, Clone, PartialEq, Eq)]
205pub struct VitalProductDataRo<'a> {
206 data: &'a [u8],
207}
208
209impl<'a> VitalProductDataRo<'a> {
210 pub fn new(data: &'a [u8]) -> Self {
211 Self { data }
212 }
213}
214
215impl<'a> Iterator for VitalProductDataRo<'a> {
216 type Item = VpdRoResource<'a>;
217
218 fn next(&mut self) -> Option<Self::Item> {
219 let Seq {
220 head: Le((U8(k0), U8(k1), U8(len))),
221 tail,
222 } = P3(self.data).try_into().ok()?;
223 if tail.len() < len {
224 return None;
225 }
226 let (data, tail) = tail.split_at(len);
227 self.data = tail;
228 let result = match (k0, k1) {
229 ('P', 'N') => VpdRoResource::PartNumber(std::str::from_utf8(data).ok()?),
230 ('E', 'C') => VpdRoResource::EngineeringChange(std::str::from_utf8(data).ok()?),
231 ('F', 'G') => VpdRoResource::FabricGeography(std::str::from_utf8(data).ok()?),
232 ('L', 'C') => VpdRoResource::Location(std::str::from_utf8(data).ok()?),
233 ('M', 'N') => VpdRoResource::ManufactureId(std::str::from_utf8(data).ok()?),
234 ('P', 'G') => VpdRoResource::PciGeography(std::str::from_utf8(data).ok()?),
235 ('S', 'N') => VpdRoResource::SerialNumber(std::str::from_utf8(data).ok()?),
236 ('V', x) => VpdRoResource::VendorSpecific(x, std::str::from_utf8(data).ok()?),
237 ('C', 'P') => {
238 let Seq {
239 head: Le((cap_id, bar_index, bar_offset)),
240 ..
241 } = P3(data).try_into().ok()?;
242 VpdRoResource::ExtendedCapability {
243 cap_id,
244 bar_index,
245 bar_offset,
246 }
247 }
248 ('R', 'V') => {
249 let Seq {
250 head: checksum,
251 tail: reserved,
252 } = data.le_bytes_try_into().ok()?;
253 VpdRoResource::ChecksumAndReserved { checksum, reserved }
254 }
255 (k0, k1) => VpdRoResource::Unknown {
256 k0: k0 as u8,
257 k1: k1 as u8,
258 len: len as u8,
259 data,
260 },
261 };
262 Some(result)
263 }
264}
265#[derive(Debug, Clone, PartialEq, Eq)]
267pub enum VpdRoResource<'a> {
268 PartNumber(&'a str),
270 EngineeringChange(&'a str),
272 FabricGeography(&'a str),
274 Location(&'a str),
276 ManufactureId(&'a str),
278 PciGeography(&'a str),
280 SerialNumber(&'a str),
282 VendorSpecific(char, &'a str),
284 ExtendedCapability {
286 cap_id: u8,
287 bar_index: u8,
288 bar_offset: u16,
289 },
290 ChecksumAndReserved { checksum: u8, reserved: &'a [u8] },
292 Unknown {
294 k0: u8,
295 k1: u8,
296 len: u8,
297 data: &'a [u8],
298 },
299}
300
301#[derive(Debug, Clone, PartialEq, Eq)]
303pub struct VitalProductDataRw<'a> {
304 data: &'a [u8],
305}
306
307impl<'a> VitalProductDataRw<'a> {
308 pub fn new(data: &'a [u8]) -> Self {
309 Self { data }
310 }
311}
312
313impl<'a> Iterator for VitalProductDataRw<'a> {
314 type Item = VpdRwResource<'a>;
315
316 fn next(&mut self) -> Option<Self::Item> {
317 let Seq {
318 head: Le((U8(k0), U8(k1), U8(len))),
319 tail,
320 } = P3(self.data).try_into().ok()?;
321 if tail.len() < len {
322 return None;
323 }
324 let (data, tail) = tail.split_at(len);
325 self.data = tail;
326 let result = match (k0, k1) {
327 ('V', x) => VpdRwResource::VendorSpecific(x, std::str::from_utf8(data).ok()?),
328 ('Y', 'A') => VpdRwResource::AssetTagIdentifier(std::str::from_utf8(data).ok()?),
329 ('Y', x) => VpdRwResource::SystemSpecific(x, data),
330 _ => VpdRwResource::RemainingRwArea(data),
331 };
332 Some(result)
333 }
334}
335
336#[derive(Debug, Clone, PartialEq, Eq)]
338pub enum VpdRwResource<'a> {
339 VendorSpecific(char, &'a str),
341 SystemSpecific(char, &'a [u8]),
343 AssetTagIdentifier(&'a str),
345 RemainingRwArea(&'a [u8]),
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352 use pretty_assertions::assert_eq;
353
354 #[test]
355 fn vital_product_data_ro() {
356 let data = [
357 b"PN",
358 [0x08].as_slice(),
359 b"6181682A",
360 b"EC",
361 [0x0A].as_slice(),
362 b"4950262536",
363 b"SN",
364 [0x08].as_slice(),
365 b"00000194",
366 b"MN",
367 [0x04].as_slice(),
368 b"1037",
369 b"RV",
370 [0x2C].as_slice(),
371 [0xFF].as_slice(), [0x00; 128 - 85].as_slice(),
373 ]
374 .concat();
375
376 let sample_vpd_r = vec![
377 VpdRoResource::PartNumber("6181682A"),
378 VpdRoResource::EngineeringChange("4950262536"),
379 VpdRoResource::SerialNumber("00000194"),
380 VpdRoResource::ManufactureId("1037"),
381 VpdRoResource::ChecksumAndReserved {
382 checksum: 0xFF,
383 reserved: &[0x00; 0x2c - 1],
384 },
385 ];
386 let result_vpd_r = VitalProductDataRo::new(&data);
387 assert_eq!(sample_vpd_r, result_vpd_r.collect::<Vec<_>>());
388 }
389
390 #[test]
391 fn vital_product_data_rw() {
392 let data = [
393 b"V1",
394 [0x05].as_slice(),
395 b"65A01",
396 b"Y1",
397 [0x0D].as_slice(),
398 b"Error Code 26",
399 b"RW",
400 [0x61].as_slice(),
401 [0x00; 255 - 158].as_slice(),
402 ]
403 .concat();
404
405 let sample_vpd_w = vec![
406 VpdRwResource::VendorSpecific('1', "65A01"),
407 VpdRwResource::SystemSpecific('1', b"Error Code 26"),
408 VpdRwResource::RemainingRwArea(&[0; 0x61]),
409 ];
410 let result_vpd_w = VitalProductDataRw::new(&data);
411 assert_eq!(sample_vpd_w, result_vpd_w.clone().collect::<Vec<_>>());
412 }
413
414 #[test]
415 fn plug_and_play_resource() {
416 let data = [
417 [0x82].as_slice(),
418 &0x0021u16.to_le_bytes(),
419 b"ABCD Super-Fast Widget Controller",
420 [0x90].as_slice(),
421 &0x0059u16.to_le_bytes(),
422 b"PN",
423 [0x08].as_slice(),
424 b"6181682A",
425 b"EC",
426 [0x0A].as_slice(),
427 b"4950262536",
428 b"SN",
429 [0x08].as_slice(),
430 b"00000194",
431 b"MN",
432 [0x04].as_slice(),
433 b"1037",
434 b"RV",
435 [0x2C].as_slice(),
436 [0xFF].as_slice(), [0x00; 128 - 85].as_slice(),
438 [0x91].as_slice(),
439 &0x007Cu16.to_le_bytes(),
440 b"V1",
441 [0x05].as_slice(),
442 b"65A01",
443 b"Y1",
444 [0x0D].as_slice(),
445 b"Error Code 26",
446 b"RW",
447 [0x61].as_slice(),
448 [0x00; 255 - 158].as_slice(),
449 [0x78].as_slice(),
450 ]
451 .concat();
452
453 let sample = vec![
454 Resource::Large(Large {
455 item: LargeItem::IdentifierStringAnsi("ABCD Super-Fast Widget Controller"),
456 length: 0x0021,
457 }),
458 Resource::Large(Large {
459 item: LargeItem::VitalProductDataRo(VitalProductDataRo::new(&data[39..128])),
460 length: 0x0059,
461 }),
462 Resource::Large(Large {
463 item: LargeItem::VitalProductDataRw(VitalProductDataRw::new(&data[131..255])),
464 length: 0x007C,
465 }),
466 Resource::Small(Small {
467 item: SmallItem::End,
468 length: 0x00,
469 }),
470 ];
471 let result = PlugAndPlayResource::new(&data);
472 assert_eq!(sample, result.clone().collect::<Vec<_>>(), "{:x?}", result);
473 }
474}