mycelium_pci/
class.rs

1use crate::{device, error};
2use core::{cmp, fmt};
3use pci_ids::FromId;
4
5#[derive(Copy, Clone, Debug, Eq, PartialEq)]
6pub struct Classes {
7    class: Class,
8    subclass: Subclass,
9}
10
11/// A code describing a PCI device's device class.
12///
13/// This type represents a class code that exists in the PCI class database.
14#[derive(Copy, Clone, Debug, Eq, PartialEq)]
15pub struct Class(&'static pci_ids::Class);
16
17/// A code describing a PCI device's subclass within its [`Class`].
18///
19/// This type represents a subclass code that exists in the PCI class database.
20#[derive(Copy, Clone, Debug, Eq, PartialEq)]
21pub struct Subclass(&'static pci_ids::Subclass);
22
23#[derive(Debug, Copy, Clone)]
24#[repr(C)]
25pub struct RawClasses {
26    pub(crate) subclass: u8,
27    pub(crate) class: u8,
28}
29
30// === impl Classes ===
31
32impl Classes {
33    #[inline]
34    #[must_use]
35    pub fn class(&self) -> Class {
36        self.class
37    }
38
39    #[inline]
40    #[must_use]
41    pub fn subclass(&self) -> Subclass {
42        self.subclass
43    }
44
45    #[inline]
46    #[must_use]
47    pub fn class_id(&self) -> u8 {
48        self.class.0.id()
49    }
50
51    #[inline]
52    #[must_use]
53    pub fn subclass_id(&self) -> u8 {
54        self.subclass.0.id()
55    }
56}
57
58impl fmt::Display for Classes {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        let Self { class, subclass } = self;
61        write!(f, "{}: {}", class.name(), subclass.name())
62    }
63}
64
65// === impl Class ===
66
67impl Class {
68    pub fn from_id(id: u8) -> Result<Self, error::UnexpectedValue<u8>> {
69        let inner =
70            pci_ids::Class::from_id(id).ok_or_else(|| error::unexpected(id).named("PCI class"))?;
71        Ok(Self(inner))
72    }
73
74    pub fn subclass(self, id: u8) -> Result<Subclass, error::UnexpectedValue<u8>> {
75        let inner = pci_ids::Subclass::from_cid_sid(self.id(), id)
76            .ok_or_else(|| error::unexpected(id).named("PCI subclass"))?;
77        Ok(Subclass(inner))
78    }
79
80    #[inline]
81    #[must_use]
82    pub fn name(self) -> &'static str {
83        self.0.name()
84    }
85
86    #[inline]
87    #[must_use]
88    pub fn id(self) -> u8 {
89        self.0.id()
90    }
91}
92
93impl PartialOrd for Class {
94    #[inline]
95    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
96        Some(self.cmp(other))
97    }
98}
99
100impl Ord for Class {
101    #[inline]
102    fn cmp(&self, other: &Self) -> cmp::Ordering {
103        self.id().cmp(&other.id())
104    }
105}
106
107// === impl Subclass ===
108
109impl Subclass {
110    #[inline]
111    #[must_use]
112    pub fn name(self) -> &'static str {
113        self.0.name()
114    }
115
116    #[inline]
117    #[must_use]
118    pub fn id(self) -> u8 {
119        self.0.id()
120    }
121
122    #[inline]
123    #[must_use]
124    pub fn class(self) -> Class {
125        Class(self.0.class())
126    }
127
128    /// Resolves a register-level programming interface code ("prog IF") for
129    /// this subclass.
130    ///
131    /// Note that this is an O(_N_) operation.
132    #[must_use]
133    pub fn prog_if(self, code: u8) -> device::ProgIf {
134        self.0
135            .prog_ifs()
136            .find(|prog_if| prog_if.id() == code)
137            .map(device::ProgIf::Known)
138            .unwrap_or(device::ProgIf::Unknown(code))
139    }
140}
141
142impl PartialOrd for Subclass {
143    #[inline]
144    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
145        Some(self.cmp(other))
146    }
147}
148
149impl Ord for Subclass {
150    #[inline]
151    fn cmp(&self, other: &Self) -> cmp::Ordering {
152        self.0
153            .class()
154            .id()
155            .cmp(&other.class().id())
156            .then_with(|| self.id().cmp(&other.id()))
157    }
158}
159
160// === impl RawClasses ===
161
162impl RawClasses {
163    pub fn resolve(&self) -> Result<Classes, error::UnexpectedValue<Self>> {
164        let class = self
165            .resolve_class()
166            .map_err(|_| error::unexpected(*self).named("PCI device class"))?;
167        let subclass = self
168            .resolve_subclass()
169            .map_err(|_| error::unexpected(*self).named("PCI device subclass"))?;
170        Ok(crate::Classes { class, subclass })
171    }
172
173    pub fn resolve_class(&self) -> Result<Class, error::UnexpectedValue<u8>> {
174        pci_ids::Class::from_id(self.class)
175            .ok_or_else(|| error::unexpected(self.class).named("PCI device class"))
176            .map(Class)
177    }
178
179    pub fn resolve_subclass(&self) -> Result<Subclass, error::UnexpectedValue<u8>> {
180        pci_ids::Subclass::from_cid_sid(self.class, self.subclass)
181            .ok_or_else(|| error::unexpected(self.subclass).named("PCI device subclass"))
182            .map(Subclass)
183    }
184}
185
186impl fmt::LowerHex for RawClasses {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        let Self { class, subclass } = self;
189        // should the formatted ID be prefaced with a leading `0x`?
190        let leading = if f.alternate() { "0x" } else { "" };
191        write!(f, "{leading}{class:x}:{subclass:x}")
192    }
193}
194
195// macro_rules! replace_tt {
196//     ($old:tt $new:tt) => {
197//         $new
198//     };
199// }
200
201// macro_rules! class_enum {
202//     (
203//         $(#[$m:meta])*
204//         $v:vis enum $name:ident<NoProgIf> {
205//             $(
206//                 $(#[$($mm:tt)*])*
207//                 $variant:ident = $value:expr
208//             ),+
209//             $(,)?
210//         }
211//     ) => {
212//         class_enum! {
213//             $(#[$m])*
214//             $v enum $name {
215//                 $(
216//                     $(#[$($mm)*])*
217//                     $variant = $value
218//                 ),+
219//             }
220//         }
221
222//         impl TryFrom<(u8, u8)> for $name {
223//             type Error = error::UnexpectedValue<u8>;
224//             fn try_from((u, rest): (u8, u8)) -> Result<Self, Self::Error> {
225//                 if rest != 0 {
226//                     return Err(error::unexpected(rest));
227//                 }
228
229//                 Self::try_from(u)
230//             }
231//         }
232//     };
233
234//     (
235//         $(#[$m:meta])*
236//         $v:vis enum $name:ident<$kind:ident, $rest:ty> {
237//             $(
238//                 $(#[$($mm:tt)*])*
239//                 $variant:ident $(($next:ty))? = $value:expr
240//             ),+
241//             $(,)?
242//         }
243//     ) => {
244//         $(#[$m])*
245//         #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
246//         $v enum $name {
247//             $(
248//                 $(#[$($mm)*])*
249//                 $variant $( ($next) )?
250//             ),+
251//         }
252
253//         impl TryFrom<(u8, $rest)> for $name {
254//             type Error = error::UnexpectedValue<u8>;
255//             fn try_from((u, rest): (u8, $rest)) -> Result<Self, Self::Error> {
256//                 match $kind::try_from(u)? {
257//                     $(
258//                         $kind::$variant => Ok($name::$variant $((<$next>::try_from(rest)?) )?)
259//                     ),+
260//                 }
261//             }
262//         }
263
264//         impl fmt::Display for $name {
265//             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266//                 match self {
267//                     $(
268//                         $name::$variant $((replace_tt!($next next)))? => {
269//                             fmt::Display::fmt(&$kind::$variant, f)?;
270//                             $(next_display_helper(f, replace_tt!($next next))?;)?
271//                         }
272//                     ),*
273//                 }
274//                 Ok(())
275//             }
276//         }
277
278//         class_enum!{
279//             enum $kind {
280//                 $(
281//                     $(#[$($mm)*])*
282//                     $variant = $value
283//                 ),+
284//             }
285//         }
286//     };
287
288//     (
289//         $(#[$m:meta])*
290//         $v:vis enum $name:ident {
291//             $(
292//                 #[doc = $doc:expr]
293//                 $(#[$mm:meta])*
294//                 $variant:ident = $value:expr
295//             ),+
296//             $(,)?
297//         }
298//     ) => {
299//         $(#[$m])*
300//         #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
301//         #[repr(u8)]
302//         $v enum $name {
303//             $(
304//                 #[doc = $doc]
305//                 $(#[$mm])*
306//                 $variant = $value
307//             ),+
308//         }
309
310//         impl TryFrom<u8> for $name {
311//             type Error = error::UnexpectedValue<u8>;
312//             fn try_from(num: u8) -> Result<Self, Self::Error> {
313//                 match num {
314//                     $(
315//                         $value => Ok($name::$variant),
316//                     )+
317//                     num => Err(error::unexpected(num)),
318//                 }
319//             }
320//         }
321
322//         impl fmt::Display for $name {
323//             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
324//                 match self {
325//                     $(
326//                         $name::$variant => f.write_str($doc.trim())
327//                     ),*
328//                 }
329//             }
330//         }
331//     };
332// }
333
334// fn next_display_helper(f: &mut fmt::Formatter, next: &impl fmt::Display) -> fmt::Result {
335//     // Precision of `0` means we're done, no precision implies infinite precision.
336//     match f.precision() {
337//         Some(0) => Ok(()),
338//         Some(precision) => write!(f, ": {:.*}", precision - 1, next),
339//         None => write!(f, ": {}", next),
340//     }
341// }
342
343// class_enum! {
344//     pub enum Class<ClassValue, (u8, u8)> {
345//         /// Unclassified
346//         Unclassified(Unclassified) = 0x00,
347//         /// Mass Storage
348//         MassStorage(MassStorage) = 0x01,
349//         /// Network
350//         Network(Network) = 0x02,
351//         /// Display
352//         Display(Display) = 0x03,
353//         /// Multimedia
354//         Multimedia(Multimedia) = 0x04,
355//         /// Memory Controller
356//         Memory = 0x05,
357//         /// Bridge Device
358//         Bridge = 0x06,
359//         /// Simple Communication Controller
360//         SimpleComm = 0x07,
361//         /// Base System Peripheral
362//         BaseSystemPeripheral = 0x08,
363//         /// Input Device Controller
364//         Input = 0x09,
365//         /// Docking Station
366//         DockingStation = 0x0A,
367//         /// Processor
368//         Processor = 0x0B,
369//         /// Serial Bus Controller
370//         SerialBus = 0x0C,
371//         /// Wireless Controller
372//         Wireless = 0x0D,
373//         /// Intelligent Controller
374//         Intelligent = 0x0E,
375//         /// Satellite Communication Controller
376//         SatelliteComm = 0x0F,
377//         /// Encryption Controller
378//         Encryption = 0x10,
379//         /// Signal Processing Controller
380//         SignalProcessing = 0x11,
381//         /// Processing Accelerator
382//         ProcessingAccelerator = 0x12,
383//         /// Non-Essential Instrumentation
384//         NonEssentialInstrumentation = 0x13
385//     }
386// }
387
388// class_enum! {
389//     pub enum Unclassified<NoProgIf> {
390//         /// Non-VGA-Compatible Device
391//         NonVga = 0x00,
392//         /// VGA-Compatible Device
393//         Vga = 0x01
394//     }
395// }
396
397// class_enum! {
398//     pub enum MassStorage<MassStorageKind, u8> {
399//         /// SCSI Bus Controller
400//         ScsiBus = 0x00,
401//         /// IDE Controller
402//         Ide(iface::Ide) = 0x01,
403//         /// Floppy Disk Controller
404//         Floppy = 0x02,
405//         /// IPI Bus Controller
406//         IpiBus = 0x03,
407//         /// RAID Controller
408//         Raid = 0x04,
409//         /// ATA Controller
410//         Ata(iface::Ata) = 0x05,
411//         /// Serial ATA Controller
412//         Sata(iface::Sata) = 0x06,
413//         /// Serial Attached SCSI
414//         SerialAttachedScsi(iface::SerialAttachedScsi) = 0x07,
415//         /// Non-Volatile Memory Controller
416//         NonVolatileMem(iface::Nvm) = 0x08,
417//         /// Other
418//         Other = 0x80
419//     }
420// }
421
422// class_enum! {
423//     pub enum Network<NoProgIf> {
424//         /// Ethernet Controller
425//         Ethernet = 0x00,
426//         /// Token Ring Controller
427//         TokenRing = 0x01,
428//         /// FDDI Controller
429//         Fddi = 0x02,
430//         /// ATM Controller
431//         Atm = 0x03,
432//         /// ISDN Controller
433//         Isdn = 0x04,
434//         /// WorldFlip Controller
435//         WorldFip = 0x05,
436//         /// PICMG 2.14 Multi Computing
437//         Picmig2_14 = 0x06,
438//         /// Infiniband Controller
439//         Infiniband = 0x07,
440//         /// Fabric Controller
441//         Fabric = 0x08,
442//         /// Other
443//         Other = 0x80,
444//     }
445// }
446
447// class_enum! {
448//     pub enum Display<DisplayValue, u8> {
449//         /// VGA Compatible
450//         VgaCompatible(iface::VgaCompatible) = 0x00,
451//         /// XGA Controller
452//         Xga = 0x01,
453//         /// 3D Controller (Not VGA-Compatible)
454//         ThreeD = 0x02,
455//         /// Other
456//         Other = 0x80,
457//     }
458// }
459
460// class_enum! {
461//     pub enum Multimedia<NoProgIf> {
462//         /// Multimedia Video Controller
463//         MultimediaVideo = 0x00,
464//         /// Multimedia Audio Controller
465//         MultimediaAudio = 0x01,
466//         /// Computer Telephony Device
467//         ComputerTelephony = 0x02,
468//         /// Audio Device
469//         Audio = 0x03,
470//         /// Other
471//         Other = 0x80,
472//     }
473// }
474
475// class_enum! {
476//     pub enum Memory<NoProgIf> {
477//         /// RAM Controller
478//         Ram = 0x00,
479//         /// Flash Controller
480//         Flash = 0x01,
481//         /// Other
482//         Other = 0x80,
483//     }
484// }
485
486// impl TryFrom<(RawClasses, u8)> for Class {
487//     type Error = error::UnexpectedValue<u8>;
488//     fn try_from(
489//         (RawClasses { class, subclass }, prog_if): (RawClasses, u8),
490//     ) -> Result<Self, Self::Error> {
491//         Self::try_from((class, (subclass, prog_if)))
492//     }
493// }
494
495// pub mod iface {
496//     use super::*;
497
498//     #[derive(Debug, Eq, PartialEq, Copy, Clone, Ord, PartialOrd)]
499//     #[repr(transparent)]
500//     pub struct Ide(u8);
501
502//     impl Ide {
503//         const PCI_NATIVE: u8 = 0b0101;
504//         const SWITCHABLE: u8 = 0b1010;
505//         const BUS_MASTERING: u8 = 0x8;
506
507//         pub fn supports_bus_mastering(&self) -> bool {
508//             self.0 & Self::BUS_MASTERING == Self::BUS_MASTERING
509//         }
510
511//         pub fn is_switchable(&self) -> bool {
512//             self.0 & Self::SWITCHABLE == Self::SWITCHABLE
513//         }
514
515//         pub fn is_isa_native(&self) -> bool {
516//             !self.is_pci_native()
517//         }
518
519//         pub fn is_pci_native(&self) -> bool {
520//             self.0 & Self::PCI_NATIVE == Self::PCI_NATIVE
521//         }
522//     }
523
524//     impl TryFrom<u8> for Ide {
525//         type Error = error::UnexpectedValue<u8>;
526//         fn try_from(u: u8) -> Result<Self, Self::Error> {
527//             if u > 0x8f {
528//                 return Err(error::unexpected(u));
529//             }
530//             Ok(Self(u))
531//         }
532//     }
533
534//     impl fmt::Display for Ide {
535//         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
536//             let mode = match (self.is_pci_native(), self.is_switchable()) {
537//                 (false, false) => "ISA compatibility mode-only",
538//                 (false, true) => {
539//                     "ISA compatibility mode, supports both channels switched to PCI native mode"
540//                 }
541//                 (true, false) => "PCI native mode-only",
542//                 (true, true) => {
543//                     "PCI native mode, supports both channels switched to ISA compatibility mode"
544//                 }
545//             };
546
547//             if self.supports_bus_mastering() {
548//                 write!(f, "{}, supports bus mastering", mode)?;
549//             } else {
550//                 write!(f, "{}", mode)?;
551//             }
552//             Ok(())
553//         }
554//     }
555
556//     class_enum! {
557//         pub enum Ata {
558//             /// Single DMA
559//             SingleDma = 0x20,
560//             /// Chained DMA
561//             ChainedDma = 0x30,
562//         }
563//     }
564
565//     class_enum! {
566//         pub enum Sata {
567//             /// Vendor Specific Interface
568//             VendorSpecific = 0x00,
569//             /// AHCI 1.0
570//             Achi1 = 0x01,
571//             /// Serial Storage Bus
572//             SerialStorageBus = 0x02,
573//         }
574//     }
575
576//     class_enum! {
577//         pub enum SerialAttachedScsi {
578//             /// SAS
579//             Sas = 0x00,
580//             /// Serial Storage Bus
581//             SerialStorageBus = 0x02,
582//         }
583//     }
584
585//     class_enum! {
586//         pub enum Nvm {
587//             /// NVMHCI
588//             Nvmhci = 0x01,
589//             /// NVM Express
590//             NvmExpress = 0x02,
591//         }
592//     }
593
594//     class_enum! {
595//         pub enum VgaCompatible {
596//             /// VGA Controller
597//             VgaController = 0x00,
598//             /// 8514-Compatible Controller
599//             Compat8514 = 0x01,
600//         }
601//     }
602// }
603
604// #[cfg(test)]
605// mod test {
606//     use super::*;
607
608//     #[test]
609//     fn test_parsing() {
610//         let mass_storage_sata_achi = (
611//             RawClasses {
612//                 class: 0x01,
613//                 subclass: 0x06,
614//             },
615//             0x01,
616//         );
617//         let class = Class::try_from(mass_storage_sata_achi);
618//         assert_eq!(
619//             class,
620//             Ok(Class::MassStorage(MassStorage::Sata(iface::Sata::Achi1))),
621//         );
622//     }
623
624//     #[test]
625//     fn test_display() {
626//         assert_eq!(
627//             Class::MassStorage(MassStorage::Sata(iface::Sata::Achi1)).to_string(),
628//             "Mass Storage: Serial ATA Controller: AHCI 1.0"
629//         );
630
631//         let ide_iface = iface::Ide::try_from(0x8F).unwrap();
632//         assert_eq!(
633//             Class::MassStorage(MassStorage::Ide(ide_iface)).to_string(),
634//             "Mass Storage: IDE Controller: PCI native mode, supports both channels switched to ISA compatibility mode, supports bus mastering"
635//         );
636//         assert_eq!(
637//             format!("{:.1}", Class::MassStorage(MassStorage::Ide(ide_iface))),
638//             "Mass Storage: IDE Controller"
639//         );
640//     }
641// }