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// }