pci_ids/
lib.rs

1//!
2//! Rust wrappers for the [PCI ID Repository](https://pci-ids.ucw.cz/v2.2/pci.ids).
3//!
4//! The PCI ID Repository is the canonical source of PCI device information for most
5//! Linux userspaces; this crate vendors the PCI ID database to allow non-Linux hosts to
6//! access the same canonical information.
7//!
8//! # Usage
9//!
10//! Iterating over all known vendors:
11//!
12//! ```rust
13//! use pci_ids::Vendors;
14//!
15//! for vendor in Vendors::iter() {
16//!     for device in vendor.devices() {
17//!         println!("vendor: {}, device: {}", vendor.name(), device.name());
18//!     }
19//! }
20//! ```
21//!
22//! Iterating over all known subclasses:
23//!
24//! ```rust
25//! use pci_ids::Classes;
26//!
27//! for class in Classes::iter() {
28//!     for subclass in class.subclasses() {
29//!         println!("class: {}, subclass: {}", class.name(), subclass.name());
30//!     }
31//! }
32//! ```
33//!
34//! See the individual documentation for each structure for more details.
35//!
36
37#![no_std]
38#![warn(missing_docs)]
39
40// Codegen: introduces VENDORS, a phf::Map<u16, Vendor>.
41include!(concat!(env!("OUT_DIR"), "/pci_ids.cg.rs"));
42
43/// An abstraction for iterating over all vendors in the PCI database.
44pub struct Vendors;
45impl Vendors {
46    /// Returns an iterator over all vendors in the PCI database.
47    pub fn iter() -> impl Iterator<Item = &'static Vendor> {
48        VENDORS.values()
49    }
50}
51
52/// Represents a PCI device vendor in the PCI database.
53///
54/// Every device vendor has a vendor ID, a pretty name, and a
55/// list of associated [`Device`]s.
56#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub struct Vendor {
58    id: u16,
59    name: &'static str,
60    devices: &'static [Device],
61}
62
63impl Vendor {
64    /// Returns the vendor's ID.
65    pub fn id(&self) -> u16 {
66        self.id
67    }
68
69    /// Returns the vendor's name.
70    pub fn name(&self) -> &'static str {
71        self.name
72    }
73
74    /// Returns an iterator over the vendor's devices.
75    pub fn devices(&self) -> impl Iterator<Item = &'static Device> {
76        self.devices.iter()
77    }
78}
79
80/// Represents a single device in the PCI database.
81///
82/// Every device has a corresponding vendor, a device ID, a pretty name,
83/// and a list of associated [`SubSystem`]s.
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85pub struct Device {
86    vendor_id: u16,
87    id: u16,
88    name: &'static str,
89    subsystems: &'static [SubSystem],
90}
91
92impl Device {
93    /// Returns the [`Device`] corresponding to the given vendor and product IDs,
94    /// or `None` if no such device exists in the DB.
95    pub fn from_vid_pid(vid: u16, pid: u16) -> Option<&'static Device> {
96        let vendor = Vendor::from_id(vid);
97
98        vendor.and_then(|v| v.devices().find(|d| d.id == pid))
99    }
100
101    /// Returns the [`Vendor`] that this device belongs to.
102    ///
103    /// Looking up a vendor by device is cheap (`O(1)`).
104    pub fn vendor(&self) -> &'static Vendor {
105        VENDORS.get(&self.vendor_id).unwrap()
106    }
107
108    /// Returns a tuple of (vendor ID, device/"product" ID) for this device.
109    ///
110    /// This is convenient for interactions with other PCI libraries.
111    pub fn as_vid_pid(&self) -> (u16, u16) {
112        (self.vendor_id, self.id)
113    }
114
115    /// Returns the device's ID.
116    pub fn id(&self) -> u16 {
117        self.id
118    }
119
120    /// Returns the device's name.
121    pub fn name(&self) -> &'static str {
122        self.name
123    }
124
125    /// Returns an iterator over the device's subsystems.
126    ///
127    /// **NOTE**: The PCI database does not include subsystem information for
128    /// most devices. This list is not authoritative.
129    pub fn subsystems(&self) -> impl Iterator<Item = &'static SubSystem> {
130        self.subsystems.iter()
131    }
132}
133
134/// Represents an subsystem to a PCI device in the PCI database.
135///
136/// Every subsystem has subvendor and subdevice ID
137/// and a pretty name.
138///
139/// **NOTE**: The PCI database is not a canonical or authoritative source
140/// of subsystems information for devices. Users who wish to discover subsystems
141/// on their PCI devices should query those devices directly.
142#[derive(Clone, Copy, Debug, PartialEq, Eq)]
143pub struct SubSystem {
144    subvendor: u16,
145    subdevice: u16,
146    name: &'static str,
147}
148
149impl SubSystem {
150    /// Returns the subsystem's ID.
151    pub fn subvendor(&self) -> u16 {
152        self.subvendor
153    }
154
155    /// Returns the subsystem's subdevice.
156    pub fn subdevice(&self) -> u16 {
157        self.subdevice
158    }
159
160    /// Returns the subsystem's name.
161    pub fn name(&self) -> &'static str {
162        self.name
163    }
164}
165
166/// An abstraction for iterating over all classes in the PCI database.
167pub struct Classes;
168
169impl Classes {
170    /// Returns an iterator over all classes in the PCI database.
171    pub fn iter() -> impl Iterator<Item = &'static Class> {
172        CLASSES.values()
173    }
174}
175
176/// Represents a PCI device class in the PCI database.
177///
178/// Every device class has a class ID, a pretty name, and a list of associated [`Subclass`]es.
179#[derive(Clone, Copy, Debug, PartialEq, Eq)]
180pub struct Class {
181    id: u8,
182    name: &'static str,
183    subclasses: &'static [Subclass],
184}
185
186impl Class {
187    /// Returns the class' ID.
188    pub fn id(&self) -> u8 {
189        self.id
190    }
191
192    /// Returns the class' name.
193    pub fn name(&self) -> &'static str {
194        self.name
195    }
196
197    /// Returns an iterator over the class' devices.
198    pub fn subclasses(&self) -> impl Iterator<Item = &'static Subclass> {
199        self.subclasses.iter()
200    }
201}
202
203/// Represents a PCI device subclass in the PCI database.
204///
205/// Every subclass has a corresponding class, a subclass id, a pretty name, and a list of associated [`ProgIf`]s.
206#[derive(Clone, Copy, Debug, PartialEq, Eq)]
207pub struct Subclass {
208    class_id: u8,
209    id: u8,
210    name: &'static str,
211    prog_ifs: &'static [ProgIf],
212}
213
214impl Subclass {
215    /// Returns the [`Subclass`] corresponding to the given class and subclass IDs, or `None` if no such device exists in the DB.
216    pub fn from_cid_sid(cid: u8, sid: u8) -> Option<&'static Self> {
217        let class = Class::from_id(cid);
218
219        class.and_then(|c| c.subclasses().find(|s| s.id == sid))
220    }
221
222    /// Returns the [`Class`] that this subclass belongs to.
223    ///
224    /// Looking up a class by subclass is cheap (`O(1)`).
225    pub fn class(&self) -> &'static Class {
226        CLASSES.get(&self.class_id).unwrap()
227    }
228
229    /// Returns a tuple of (class ID, subclass ID) for this subclass.
230    ///
231    /// This is conveniont for interactions with other PCI libraries.
232    pub fn as_cid_sid(&self) -> (u8, u8) {
233        (self.class_id, self.id)
234    }
235
236    /// Returns the subclass' ID.
237    pub fn id(&self) -> u8 {
238        self.id
239    }
240
241    /// Returns the subclass' name.
242    pub fn name(&self) -> &'static str {
243        self.name
244    }
245
246    /// Returns an iterator over the subclass' programming interfaces.
247    ///
248    /// **NOTE**: The PCI database does not include programming interface information for most devices.
249    /// This list is not authoritative.
250    pub fn prog_ifs(&self) -> impl Iterator<Item = &'static ProgIf> {
251        self.prog_ifs.iter()
252    }
253}
254
255/// Represents a programming interface to a PCI subclass in the PCI database.
256///
257/// Every programming interface has an ID and a pretty name.
258///
259/// **NOTE**: The PCI database is not a canonical or authoritative source of programming interface information for subclasses.
260/// Users who wish to discover programming interfaces on their PCI devices should query those devices directly.
261#[derive(Clone, Copy, Debug, PartialEq, Eq)]
262pub struct ProgIf {
263    id: u8,
264    name: &'static str,
265}
266
267impl ProgIf {
268    /// Returns the programming interface's ID.
269    pub fn id(&self) -> u8 {
270        self.id
271    }
272
273    /// Returns the programming interface's name.
274    pub fn name(&self) -> &'static str {
275        self.name
276    }
277}
278
279/// A convenience trait for retrieving a top-level entity (like a [`Vendor`]) from the PCI
280/// database by its unique ID.
281pub trait FromId<T> {
282    /// Returns the entity corresponding to `id`, or `None` if none exists.
283    fn from_id(id: T) -> Option<&'static Self>;
284}
285
286impl FromId<u16> for Vendor {
287    fn from_id(id: u16) -> Option<&'static Self> {
288        VENDORS.get(&id)
289    }
290}
291
292impl FromId<u8> for Class {
293    fn from_id(id: u8) -> Option<&'static Self> {
294        CLASSES.get(&id)
295    }
296}
297
298#[cfg(test)]
299mod tests {
300    use super::*;
301
302    #[test]
303    fn test_vendor_from_id() {
304        let vendor = Vendor::from_id(0x14c3).unwrap();
305
306        assert_eq!(vendor.name(), "MEDIATEK Corp.");
307        assert_eq!(vendor.id(), 0x14c3);
308    }
309
310    #[test]
311    fn test_vendor_devices() {
312        let vendor = Vendor::from_id(0x17cb).unwrap();
313
314        for device in vendor.devices() {
315            assert_eq!(device.vendor(), vendor);
316            assert!(!device.name().is_empty());
317        }
318    }
319
320    #[test]
321    fn test_device_from_vid_pid() {
322        let device = Device::from_vid_pid(0x16ae, 0x000a).unwrap();
323
324        assert_eq!(device.name(), "SafeXcel 1841");
325
326        let (vid, pid) = device.as_vid_pid();
327
328        assert_eq!(vid, device.vendor().id());
329        assert_eq!(pid, device.id());
330
331        let device2 = Device::from_vid_pid(vid, pid).unwrap();
332
333        assert_eq!(device, device2);
334    }
335
336    #[test]
337    fn test_class_from_id() {
338        let class = Class::from_id(0x08).unwrap();
339
340        assert_eq!(class.name(), "Generic system peripheral");
341        assert_eq!(class.id(), 0x08);
342    }
343
344    #[test]
345    fn test_class_subclasses() {
346        let class = Class::from_id(0x01).unwrap();
347
348        for subclass in class.subclasses() {
349            assert_eq!(subclass.class(), class);
350            assert!(!subclass.name().is_empty());
351        }
352    }
353
354    #[test]
355    fn test_subclass_from_cid_sid() {
356        let subclass = Subclass::from_cid_sid(0x07, 0x00).unwrap();
357
358        assert_eq!(subclass.name(), "Serial controller");
359
360        let (cid, sid) = subclass.as_cid_sid();
361
362        assert_eq!(cid, subclass.class().id());
363        assert_eq!(sid, subclass.id());
364
365        let subclass2 = Subclass::from_cid_sid(cid, sid).unwrap();
366
367        assert_eq!(subclass, subclass2);
368    }
369}