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}