├── .gitignore ├── Cargo.toml ├── .github └── workflows │ └── rust.yml ├── LICENSE ├── src ├── extra.rs ├── device_class.rs ├── macos │ └── mod.rs ├── netbsd │ └── mod.rs ├── windows │ └── mod.rs ├── lib.rs └── linux │ └── mod.rs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aparato" 3 | description = "A library for querying PCI devices and a parser for pci.ids." 4 | version = "6.0.2" 5 | license = "MIT" 6 | readme = "README.md" 7 | edition = "2018" 8 | repository = "https://github.com/grtcdr/aparato" 9 | documentation = "https://docs.rs/aparato" 10 | categories = ["parser-implementations"] 11 | authors = ["grtcdr "] 12 | 13 | [dependencies] 14 | cfg-if = "1.0.0" 15 | hex = "0.4.3" 16 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build 16 | run: cargo build --verbose 17 | - name: Run tests 18 | run: cargo test --verbose 19 | 20 | check-format: 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v2 25 | - name: Install rust-fmt 26 | run: rustup component add rustfmt 27 | - name: Check Formatting 28 | run: cargo fmt -- --check 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Taha Aziz Ben Ali 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/extra.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use std::fs::File; 3 | use std::io::{self, BufRead}; 4 | use std::path::{Path, PathBuf}; 5 | 6 | /// This function returns a list of entries located inside a given directory. 7 | #[doc(hidden)] 8 | pub fn list_dir_entries(path: &str) -> Vec { 9 | let mut directory_entries: Vec = Vec::new(); 10 | let directory = std::fs::read_dir(path); 11 | 12 | if let Ok(dir) = directory { 13 | for entry in dir.flatten() { 14 | directory_entries.push(entry.path()) 15 | } 16 | } 17 | directory_entries 18 | } 19 | 20 | /// This function returns the basename of a given path. 21 | #[doc(hidden)] 22 | pub fn basename<'a>(path: String) -> String { 23 | let mut pieces = path.rsplit("/"); 24 | match pieces.next() { 25 | Some(p) => p.into(), 26 | None => path.into(), 27 | } 28 | } 29 | 30 | #[allow(dead_code)] 31 | /// This function returns an iterator over the lines of a given file. 32 | #[doc(hidden)] 33 | pub fn read_lines

(filename: P) -> io::Result>> 34 | where 35 | P: AsRef, 36 | { 37 | let file = File::open(filename)?; 38 | Ok(io::BufReader::new(file).lines()) 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

aparato

3 | 4 | A library for querying connected PCI devices and a pci.ids parser. 5 | 6 | 7 | version 8 | 9 | 10 | 11 | docs 12 | 13 | 14 |
15 | 16 | ### Disclaimer 17 | 18 | - aparato is currently unmaintained. 19 | 20 | ### Usage 21 | 22 | Add the following to your project's *Cargo.toml* file: 23 | 24 | ```toml 25 | aparato = "6.0.2" # Be sure to use the latest version 26 | ``` 27 | 28 | ### Examples 29 | 30 | ```rust 31 | use aparato::{Device, PCIDevice}; 32 | 33 | fn main() { 34 | 35 | // Know the domain of the PCI device? 36 | // Instantiate a new PCIDevice so we can get to know it a bit. 37 | let device = PCIDevice::new("00:02.0"); 38 | 39 | println!("Class Name: {}", device.class_name()); // e.g. Display Controller 40 | println!("Subclass Name: {}", device.subclass_name()); // e.g. VGA compatible controller 41 | println!("Vendor Name: {}", device.vendor_name()); // e.g. Intel Corporation 42 | println!("Device Name: {}", device.device_name()); // e.g. WhiskeyLake-U GT2 [UHD Graphics 620] 43 | } 44 | 45 | ``` 46 | 47 | 48 | ### Contributing 49 | 50 | Any form of contribution is welcome, whether it be unit tests, refactoring, or bug-fixing. It's recommended you report issues before beginning to work on them. 51 | -------------------------------------------------------------------------------- /src/device_class.rs: -------------------------------------------------------------------------------- 1 | //! This module contains a crucial enum, [DeviceClass], that you can use when working with [Fetch](crate::Fetch). 2 | 3 | use std::fmt; 4 | 5 | /// This enum holds variants that are defined as classes in . 6 | pub enum DeviceClass { 7 | Unclassified, // ID: 00 8 | MassStorageController, // ID: 01 9 | NetworkController, // ID: 02 10 | DisplayController, // ID: 03 11 | MultimediaController, // ID: 04 12 | MemoryController, // ID: 05 13 | Bridge, // ID: 06 14 | CommunicationController, // ID: 07 15 | GenericSystemPeripheral, // ID: 08 16 | InputDeviceController, // ID: 09 17 | DockingStation, // ID: 0a 18 | Processor, // ID: 0b 19 | SerialBusController, // ID: 0c 20 | WirelessController, // ID: 0d 21 | IntelligentController, // ID: 0e 22 | SatelliteCommunicationsController, // ID: 0f 23 | EncryptionController, // ID: 10 24 | SignalProcessingController, // ID: 11 25 | ProcessingAccelerator, // ID: 12 26 | NonEssentialInstrumentation, // ID: 13 27 | Coprocessor, // ID: 40 28 | Unassigned, // ID: ff 29 | } 30 | 31 | impl fmt::Display for DeviceClass { 32 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 33 | match *self { 34 | DeviceClass::Unassigned => write!(f, "Unassigned"), 35 | DeviceClass::Unclassified => write!(f, "Unclassified"), 36 | DeviceClass::MassStorageController => write!(f, "Mass Storage Controller"), 37 | DeviceClass::NetworkController => write!(f, "Network Controller"), 38 | DeviceClass::DisplayController => write!(f, "Display Controller"), 39 | DeviceClass::MultimediaController => write!(f, "Multimedia Controller"), 40 | DeviceClass::MemoryController => write!(f, "Memory Controller"), 41 | DeviceClass::Bridge => write!(f, "Bridge"), 42 | DeviceClass::CommunicationController => write!(f, "Communication Controller"), 43 | DeviceClass::GenericSystemPeripheral => write!(f, "Generic System Peripheral"), 44 | DeviceClass::InputDeviceController => write!(f, "Input Device Controller"), 45 | DeviceClass::DockingStation => write!(f, "Docking Station"), 46 | DeviceClass::Processor => write!(f, "Processor"), 47 | DeviceClass::Coprocessor => write!(f, "Coprocessor"), 48 | DeviceClass::SerialBusController => write!(f, "Serial Bus Controller"), 49 | DeviceClass::WirelessController => write!(f, "Wireless Controller"), 50 | DeviceClass::IntelligentController => write!(f, "Intelligent Controller"), 51 | DeviceClass::SatelliteCommunicationsController => { 52 | write!(f, "Satellite Communications Controller") 53 | } 54 | DeviceClass::EncryptionController => write!(f, "Encryption Controller"), 55 | DeviceClass::SignalProcessingController => write!(f, "Signal Processing Controller"), 56 | DeviceClass::ProcessingAccelerator => write!(f, "Processing Accelerators"), 57 | DeviceClass::NonEssentialInstrumentation => write!(f, "Non Essential Instrumentation"), 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/macos/mod.rs: -------------------------------------------------------------------------------- 1 | #![doc(hidden)] 2 | use crate::device_class::*; 3 | use crate::extra::*; 4 | use crate::private::Properties; 5 | use crate::Device; 6 | use std::path::PathBuf; 7 | 8 | #[derive(Debug)] 9 | pub struct MacOSPCIDevice { 10 | path: PathBuf, 11 | address: String, 12 | class_id: Vec, 13 | class_name: String, 14 | subclass_name: String, 15 | vendor_id: Vec, 16 | vendor_name: String, 17 | device_id: Vec, 18 | device_name: String, 19 | revision: Vec, 20 | numa_node: isize, 21 | enabled: bool, 22 | d3cold_allowed: bool, 23 | subsystem_vendor_id: Vec, 24 | subsystem_device_id: Vec, 25 | subsystem_name: String, 26 | } 27 | 28 | impl Device for MacOSPCIDevice { 29 | fn new(path: &str) -> Self { 30 | todo!() 31 | } 32 | 33 | fn path(&self) -> PathBuf { 34 | todo!() 35 | } 36 | 37 | fn address(&self) -> String { 38 | todo!() 39 | } 40 | 41 | fn class_id(&self) -> Vec { 42 | todo!() 43 | } 44 | 45 | fn vendor_id(&self) -> Vec { 46 | todo!() 47 | } 48 | 49 | fn device_id(&self) -> Vec { 50 | todo!() 51 | } 52 | 53 | fn numa_node(&self) -> isize { 54 | todo!() 55 | } 56 | 57 | fn class_name(&self) -> String { 58 | todo!() 59 | } 60 | 61 | fn subclass_name(&self) -> String { 62 | todo!() 63 | } 64 | 65 | fn vendor_name(&self) -> String { 66 | todo!() 67 | } 68 | 69 | fn device_name(&self) -> String { 70 | todo!() 71 | } 72 | 73 | fn enabled(&self) -> bool { 74 | todo!() 75 | } 76 | 77 | fn d3cold_allowed(&self) -> bool { 78 | todo!() 79 | } 80 | 81 | fn revision(&self) -> Vec { 82 | todo!() 83 | } 84 | 85 | fn subsystem_name(&self) -> String { 86 | todo!() 87 | } 88 | 89 | fn subsystem_vendor_id(&self) -> Vec { 90 | todo!() 91 | } 92 | 93 | fn subsystem_device_id(&self) -> Vec { 94 | todo!() 95 | } 96 | } 97 | 98 | impl Properties for MacOSPCIDevice { 99 | fn reserved_new(path: &str) -> Self { 100 | todo!() 101 | } 102 | 103 | fn set_path(&mut self, p: PathBuf) { 104 | todo!() 105 | } 106 | 107 | fn set_address(&mut self) { 108 | todo!() 109 | } 110 | 111 | fn set_class_id(&mut self) { 112 | todo!() 113 | } 114 | 115 | fn set_vendor_id(&mut self) { 116 | todo!() 117 | } 118 | 119 | fn set_device_id(&mut self) { 120 | todo!() 121 | } 122 | 123 | fn set_revision(&mut self) { 124 | todo!() 125 | } 126 | 127 | fn set_numa_node(&mut self) { 128 | todo!() 129 | } 130 | 131 | fn set_subsystem_vendor_id(&mut self) { 132 | todo!() 133 | } 134 | 135 | fn set_subsystem_device_id(&mut self) { 136 | todo!() 137 | } 138 | 139 | fn set_class_name(&mut self) { 140 | todo!() 141 | } 142 | 143 | fn set_subclass_name(&mut self) { 144 | todo!() 145 | } 146 | 147 | fn set_vendor_name(&mut self) { 148 | todo!() 149 | } 150 | 151 | fn set_device_name(&mut self) { 152 | todo!() 153 | } 154 | 155 | fn set_subsystem_name(&mut self) { 156 | todo!() 157 | } 158 | 159 | fn set_enabled(&mut self) { 160 | todo!() 161 | } 162 | 163 | fn set_d3cold_allowed(&mut self) { 164 | todo!() 165 | } 166 | } 167 | 168 | impl Default for MacOSPCIDevice { 169 | fn default() -> Self { 170 | MacOSPCIDevice { 171 | path: PathBuf::new(), 172 | address: String::new(), 173 | class_name: String::new(), 174 | subclass_name: String::new(), 175 | vendor_name: String::new(), 176 | device_name: String::new(), 177 | subsystem_name: String::new(), 178 | class_id: vec![], 179 | subsystem_vendor_id: vec![], 180 | subsystem_device_id: vec![], 181 | device_id: vec![], 182 | revision: vec![], 183 | vendor_id: vec![], 184 | numa_node: -1, 185 | d3cold_allowed: false, 186 | enabled: false, 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/netbsd/mod.rs: -------------------------------------------------------------------------------- 1 | #![doc(hidden)] 2 | use crate::device_class::*; 3 | use crate::extra::*; 4 | use crate::private::Properties; 5 | use crate::Device; 6 | use std::path::PathBuf; 7 | 8 | #[derive(Debug)] 9 | pub struct NetBSDPCIDevice { 10 | path: PathBuf, 11 | address: String, 12 | class_id: Vec, 13 | class_name: String, 14 | subclass_name: String, 15 | vendor_id: Vec, 16 | vendor_name: String, 17 | device_id: Vec, 18 | device_name: String, 19 | revision: Vec, 20 | numa_node: isize, 21 | enabled: bool, 22 | d3cold_allowed: bool, 23 | subsystem_vendor_id: Vec, 24 | subsystem_device_id: Vec, 25 | subsystem_name: String, 26 | } 27 | 28 | impl Device for NetBSDPCIDevice { 29 | fn reserved_new(path: &str) -> Self { 30 | todo!() 31 | } 32 | 33 | fn new(path: &str) -> Self { 34 | todo!() 35 | } 36 | 37 | fn path(&self) -> PathBuf { 38 | todo!() 39 | } 40 | 41 | fn address(&self) -> String { 42 | todo!() 43 | } 44 | 45 | fn class_id(&self) -> Vec { 46 | todo!() 47 | } 48 | 49 | fn vendor_id(&self) -> Vec { 50 | todo!() 51 | } 52 | 53 | fn device_id(&self) -> Vec { 54 | todo!() 55 | } 56 | 57 | fn numa_node(&self) -> isize { 58 | todo!() 59 | } 60 | 61 | fn class_name(&self) -> String { 62 | todo!() 63 | } 64 | 65 | fn subclass_name(&self) -> String { 66 | todo!() 67 | } 68 | 69 | fn vendor_name(&self) -> String { 70 | todo!() 71 | } 72 | 73 | fn device_name(&self) -> String { 74 | todo!() 75 | } 76 | 77 | fn enabled(&self) -> bool { 78 | todo!() 79 | } 80 | 81 | fn d3cold_allowed(&self) -> bool { 82 | todo!() 83 | } 84 | 85 | fn revision(&self) -> Vec { 86 | todo!() 87 | } 88 | 89 | fn subsystem_name(&self) -> String { 90 | todo!() 91 | } 92 | 93 | fn subsystem_vendor_id(&self) -> Vec { 94 | todo!() 95 | } 96 | 97 | fn subsystem_device_id(&self) -> Vec { 98 | todo!() 99 | } 100 | } 101 | 102 | impl Properties for NetBSDPCIDevice { 103 | fn set_path(&mut self, p: PathBuf) { 104 | todo!() 105 | } 106 | 107 | fn set_address(&mut self) { 108 | todo!() 109 | } 110 | 111 | fn set_class_id(&mut self) { 112 | todo!() 113 | } 114 | 115 | fn set_vendor_id(&mut self) { 116 | todo!() 117 | } 118 | 119 | fn set_device_id(&mut self) { 120 | todo!() 121 | } 122 | 123 | fn set_revision(&mut self) { 124 | todo!() 125 | } 126 | 127 | fn set_numa_node(&mut self) { 128 | todo!() 129 | } 130 | 131 | fn set_subsystem_vendor_id(&mut self) { 132 | todo!() 133 | } 134 | 135 | fn set_subsystem_device_id(&mut self) { 136 | todo!() 137 | } 138 | 139 | fn set_class_name(&mut self) { 140 | todo!() 141 | } 142 | 143 | fn set_subclass_name(&mut self) { 144 | todo!() 145 | } 146 | 147 | fn set_vendor_name(&mut self) { 148 | todo!() 149 | } 150 | 151 | fn set_device_name(&mut self) { 152 | todo!() 153 | } 154 | 155 | fn set_subsystem_name(&mut self) { 156 | todo!() 157 | } 158 | 159 | fn set_enabled(&mut self) { 160 | todo!() 161 | } 162 | 163 | fn set_d3cold_allowed(&mut self) { 164 | todo!() 165 | } 166 | } 167 | 168 | impl Default for NetBSDPCIDevice { 169 | fn default() -> Self { 170 | NetBSDPCIDevice { 171 | path: PathBuf::new(), 172 | address: String::new(), 173 | class_name: String::new(), 174 | subclass_name: String::new(), 175 | vendor_name: String::new(), 176 | device_name: String::new(), 177 | subsystem_name: String::new(), 178 | class_id: vec![], 179 | subsystem_vendor_id: vec![], 180 | subsystem_device_id: vec![], 181 | device_id: vec![], 182 | revision: vec![], 183 | vendor_id: vec![], 184 | numa_node: -1, 185 | d3cold_allowed: false, 186 | enabled: false, 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/windows/mod.rs: -------------------------------------------------------------------------------- 1 | #![doc(hidden)] 2 | use crate::device_class::*; 3 | use crate::extra::*; 4 | use crate::private::Properties; 5 | use crate::Device; 6 | use std::path::PathBuf; 7 | 8 | #[derive(Debug)] 9 | pub struct WindowsPCIDevice { 10 | path: PathBuf, 11 | address: String, 12 | class_id: Vec, 13 | class_name: String, 14 | subclass_name: String, 15 | vendor_id: Vec, 16 | vendor_name: String, 17 | device_id: Vec, 18 | device_name: String, 19 | revision: Vec, 20 | numa_node: isize, 21 | enabled: bool, 22 | d3cold_allowed: bool, 23 | subsystem_vendor_id: Vec, 24 | subsystem_device_id: Vec, 25 | subsystem_name: String, 26 | } 27 | 28 | impl Device for WindowsPCIDevice { 29 | fn new(path: &str) -> Self { 30 | todo!() 31 | } 32 | 33 | fn path(&self) -> PathBuf { 34 | todo!() 35 | } 36 | 37 | fn address(&self) -> String { 38 | todo!() 39 | } 40 | 41 | fn class_id(&self) -> Vec { 42 | todo!() 43 | } 44 | 45 | fn vendor_id(&self) -> Vec { 46 | todo!() 47 | } 48 | 49 | fn device_id(&self) -> Vec { 50 | todo!() 51 | } 52 | 53 | fn numa_node(&self) -> isize { 54 | todo!() 55 | } 56 | 57 | fn class_name(&self) -> String { 58 | todo!() 59 | } 60 | 61 | fn subclass_name(&self) -> String { 62 | todo!() 63 | } 64 | 65 | fn vendor_name(&self) -> String { 66 | todo!() 67 | } 68 | 69 | fn device_name(&self) -> String { 70 | todo!() 71 | } 72 | 73 | fn enabled(&self) -> bool { 74 | todo!() 75 | } 76 | 77 | fn d3cold_allowed(&self) -> bool { 78 | todo!() 79 | } 80 | 81 | fn revision(&self) -> Vec { 82 | todo!() 83 | } 84 | 85 | fn subsystem_name(&self) -> String { 86 | todo!() 87 | } 88 | 89 | fn subsystem_vendor_id(&self) -> Vec { 90 | todo!() 91 | } 92 | 93 | fn subsystem_device_id(&self) -> Vec { 94 | todo!() 95 | } 96 | } 97 | 98 | impl Properties for WindowsPCIDevice { 99 | fn reserved_new(path: &str) -> Self { 100 | todo!() 101 | } 102 | 103 | fn set_path(&mut self, p: PathBuf) { 104 | todo!() 105 | } 106 | 107 | fn set_address(&mut self) { 108 | todo!() 109 | } 110 | 111 | fn set_class_id(&mut self) { 112 | todo!() 113 | } 114 | 115 | fn set_vendor_id(&mut self) { 116 | todo!() 117 | } 118 | 119 | fn set_device_id(&mut self) { 120 | todo!() 121 | } 122 | 123 | fn set_revision(&mut self) { 124 | todo!() 125 | } 126 | 127 | fn set_numa_node(&mut self) { 128 | todo!() 129 | } 130 | 131 | fn set_subsystem_vendor_id(&mut self) { 132 | todo!() 133 | } 134 | 135 | fn set_subsystem_device_id(&mut self) { 136 | todo!() 137 | } 138 | 139 | fn set_class_name(&mut self) { 140 | todo!() 141 | } 142 | 143 | fn set_subclass_name(&mut self) { 144 | todo!() 145 | } 146 | 147 | fn set_vendor_name(&mut self) { 148 | todo!() 149 | } 150 | 151 | fn set_device_name(&mut self) { 152 | todo!() 153 | } 154 | 155 | fn set_subsystem_name(&mut self) { 156 | todo!() 157 | } 158 | 159 | fn set_enabled(&mut self) { 160 | todo!() 161 | } 162 | 163 | fn set_d3cold_allowed(&mut self) { 164 | todo!() 165 | } 166 | } 167 | 168 | impl Default for WindowsPCIDevice { 169 | fn default() -> Self { 170 | WindowsPCIDevice { 171 | path: PathBuf::new(), 172 | address: String::new(), 173 | class_name: String::new(), 174 | subclass_name: String::new(), 175 | vendor_name: String::new(), 176 | device_name: String::new(), 177 | subsystem_name: String::new(), 178 | class_id: vec![], 179 | subsystem_vendor_id: vec![], 180 | subsystem_device_id: vec![], 181 | device_id: vec![], 182 | revision: vec![], 183 | vendor_id: vec![], 184 | numa_node: -1, 185 | d3cold_allowed: false, 186 | enabled: false, 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Welcome! 2 | //! 3 | //! aparato is a library that can provide you with information about one or all your connected PCI devices. 4 | //! 5 | //! # Examples 6 | //! 7 | //! A quick example to get started is to run the following chunk of code: 8 | //! 9 | //! ``` 10 | //! use aparato::{Device, PCIDevice}; 11 | //! 12 | //! fn main() { 13 | //! let device = PCIDevice::new("00:02.0"); 14 | //! println!("{:?}", device); 15 | //! } 16 | //! ``` 17 | //! 18 | //! Information about `02:00.0` should be printed to the screen. 19 | //! There's always a chance that the address you provided to [`Device::new()`] could be non-existant, which will result 20 | //! in an empty object being returned. 21 | //! 22 | //! If you're unsure what PCI device you want to query, you can let [`Fetch`] do that for you. 23 | //! It can return a list of PCI devices with all their information. 24 | 25 | cfg_if::cfg_if! { 26 | if #[cfg(target_os = "linux")] { 27 | pub mod linux; 28 | pub type PCIDevice = linux::LinuxPCIDevice; 29 | pub type DeviceClass = device_class::DeviceClass; 30 | } else if #[cfg(target_os = "macos")] { 31 | pub mod macos; 32 | pub type PCIDevice = macos::MacOSPCIDevice; 33 | } else if #[cfg(target_os = "netbsd")] { 34 | pub mod netbsd; 35 | pub type PCIDevice = netbsd::NetBSDPCIDevice; 36 | } else if #[cfg(target_os = "windows")] { 37 | pub mod windows; 38 | pub type PCIDevice = windows::WindowsPCIDevice; 39 | } else { 40 | compile_error!("aparato does not support this platform, at least not yet."); 41 | } 42 | } 43 | 44 | /// A trait that provides the necessary methods which can initialize a single PCIDevice and fetch its information. 45 | pub trait Device { 46 | /// This function returns a new instance of `PCIDevice` struct using the given `path`. 47 | /// 48 | /// # Examples 49 | /// 50 | /// ``` 51 | /// use aparato::{Device, PCIDevice}; 52 | /// 53 | /// // foo, bar and baz all point to the same device. 54 | /// let foo = PCIDevice::new("00:04.0"); 55 | /// let bar = PCIDevice::new("0000:00:04.0"); 56 | /// let baz = PCIDevice::new("/sys/bus/pci/devices/0000:00:04.0"); 57 | /// ``` 58 | fn new(path: &str) -> Self; 59 | 60 | // Getters... 61 | 62 | /// This function returns the `PCIDevice` path. 63 | fn path(&self) -> std::path::PathBuf; 64 | 65 | /// This function returns the `PCIDevice` address. 66 | fn address(&self) -> String; 67 | 68 | /// This function returns the `PCIDevice` class ID. 69 | /// 70 | /// The return value is a decoded hexadecimal value. 71 | fn class_id(&self) -> Vec; 72 | 73 | /// This function returns the `PCIDevice` vendor ID. 74 | /// 75 | /// The return value is a decoded hexadecimal value. 76 | fn vendor_id(&self) -> Vec; 77 | 78 | /// This function returns the `PCIDevice` device ID. 79 | /// 80 | /// The return value is a decoded hexadecimal value. 81 | fn device_id(&self) -> Vec; 82 | 83 | /// This function returns the `PCIDevice` NUMA node. 84 | fn numa_node(&self) -> isize; 85 | 86 | /// This function returns the `PCIDevice` class name. 87 | fn class_name(&self) -> String; 88 | 89 | /// This function returns the `PCIDevice` subclass name. 90 | fn subclass_name(&self) -> String; 91 | 92 | /// This function returns the `PCIDevice` vendor name. 93 | fn vendor_name(&self) -> String; 94 | 95 | /// This function returns the `PCIDevice` device name. 96 | fn device_name(&self) -> String; 97 | 98 | /// This function returns whether the `PCIDevice` is enabled. 99 | fn enabled(&self) -> bool; 100 | 101 | /// This function returns whether the `PCIDevice` is enabled. 102 | fn d3cold_allowed(&self) -> bool; 103 | 104 | /// This function returns whether the `PCIDevice` is enabled. 105 | /// 106 | /// The return value is a decoded hexadecimal value. 107 | fn revision(&self) -> Vec; 108 | 109 | /// This function returns the `PCIDevice` subsystem vendor. 110 | fn subsystem_name(&self) -> String; 111 | 112 | /// This function returns the `PCIDevice` subsystem vendor. 113 | /// 114 | /// The return value is a decoded hexadecimal value. 115 | fn subsystem_vendor_id(&self) -> Vec; 116 | 117 | /// This function returns the `PCIDevice` subsystem vendor. 118 | /// 119 | /// The return value is a decoded hexadecimal value. 120 | fn subsystem_device_id(&self) -> Vec; 121 | } 122 | 123 | pub(crate) mod private { 124 | pub(crate) trait Properties { 125 | // This trait contains exclusively the setters. 126 | 127 | /// This function is reserved for use by some of the mods provided by `Fetcher` 128 | fn reserved_new(path: &str) -> Self; 129 | 130 | /// Set the `path` field of the `PCIDevice`. 131 | fn set_path(&mut self, p: std::path::PathBuf); 132 | 133 | /// This function sets the `address` field of the `PCIDevice` 134 | fn set_address(&mut self); 135 | 136 | /// This function sets the `device_id` field of the `PCIDevice` 137 | fn set_class_id(&mut self); 138 | 139 | /// This function sets the `device_id` field of the `PCIDevice` 140 | fn set_vendor_id(&mut self); 141 | 142 | /// This function sets the `device_id` field of the `PCIDevice` 143 | fn set_device_id(&mut self); 144 | 145 | /// This function sets the `numa_node` field of the `PCIDevice` 146 | fn set_numa_node(&mut self); 147 | 148 | /// This function sets the `class_name` field of the `PCIDevice` 149 | fn set_class_name(&mut self); 150 | 151 | /// This function sets the `subclass_name` field of the `PCIDevice` 152 | fn set_subclass_name(&mut self); 153 | 154 | /// This function sets the `revision` field of the `PCIDevice` 155 | fn set_revision(&mut self); 156 | 157 | /// This function sets the `enabled` field of the `PCIDevice` 158 | fn set_enabled(&mut self); 159 | 160 | /// This function sets the `d3cold_allowed` field of the `PCIDevice` 161 | fn set_d3cold_allowed(&mut self); 162 | 163 | /// This function sets the `vendor_name` field of the `PCIDevice` 164 | fn set_vendor_name(&mut self); 165 | 166 | /// This function sets the `device_name` field of the `PCIDevice` 167 | fn set_device_name(&mut self); 168 | 169 | /// This function sets the `subsystem_vendor_id` field of the `PCIDevice` 170 | fn set_subsystem_device_id(&mut self); 171 | 172 | /// This function sets the `subsystem_device_id` field of the `PCIDevice` 173 | fn set_subsystem_vendor_id(&mut self); 174 | 175 | /// This function sets the `subsystem_name` field of the `PCIDevice` 176 | fn set_subsystem_name(&mut self); 177 | } 178 | } 179 | 180 | /// A trait that provides a set of methods which can fetch the information of multiple PCI devices all at once. 181 | /// 182 | /// `Fetch` can take care of initializing PCI devices and fetching their information for you. 183 | /// It does this by traversing the filesystem, and getting the appropriate data of each device it finds. 184 | pub trait Fetch { 185 | /// This function returns a **list** of available PCI devices and their information. 186 | /// 187 | /// If anything other than `None` or `Some(0)` is provided to the function, 188 | /// it will limit itself to fetch only the given amount. 189 | /// 190 | /// # Examples 191 | /// ``` 192 | /// use aparato::{PCIDevice, Fetch, Device}; 193 | /// 194 | /// let devices = PCIDevice::fetch(Some(2)); 195 | /// 196 | /// // Print to the screen the class name of the fetched PCI devices. 197 | /// for device in devices { 198 | /// println!("{}", device.class_name()); 199 | /// } 200 | /// ``` 201 | fn fetch(maximum_devices: Option) -> Vec; 202 | 203 | /// This function returns a **list** of available PCI devices of a specific class and their information. 204 | /// 205 | /// # Examples 206 | /// ``` 207 | /// use aparato::{Fetch, PCIDevice, DeviceClass}; 208 | /// 209 | /// fn main() { 210 | /// // foo is a list of a maximum of 3 serial bus controllers. 211 | /// let foo = PCIDevice::fetch_by_class(DeviceClass::SerialBusController, Some(3)); 212 | /// 213 | /// // bar is a list of bridges with no maximum size. 214 | /// // we'll get as many bridges as aparato can find. 215 | /// let bar = PCIDevice::fetch_by_class(DeviceClass::Bridge, Some(0)); 216 | /// 217 | /// // baz is a list of wireless controllers with no maximum size. 218 | /// let baz = PCIDevice::fetch_by_class(DeviceClass::WirelessController, None); 219 | /// 220 | /// } 221 | /// ``` 222 | fn fetch_by_class( 223 | class: crate::device_class::DeviceClass, 224 | maximum_devices: Option, 225 | ) -> Vec; 226 | 227 | /// This function returns a **list** of available and enabled GPUs, 228 | /// masking unnecessary data from device and vendor names. for example: 229 | /// - `TU117M [GeForce GTX 1650 Mobile / Max-Q]` becomes `GeForce GTX 1650 Mobile / Max-Q` 230 | /// - `NVIDIA Corporation` becomes `NVIDIA` 231 | /// 232 | /// # Examples 233 | /// ``` 234 | /// use aparato::{Fetch, PCIDevice}; 235 | /// 236 | /// fn main() { 237 | /// // Returns a list of "device_vendor + device_name" 238 | /// let devices = PCIDevice::fetch_gpus(None); 239 | /// 240 | /// // Example output: ["NVIDIA GeForce GTX 1650 Mobile / Max-Q", "Intel UHD Graphics 620"] 241 | /// println!("{:?}", devices); 242 | /// } 243 | /// ``` 244 | fn fetch_gpus(maximum_devices: Option) -> Vec; 245 | } 246 | 247 | pub mod device_class; 248 | mod extra; 249 | -------------------------------------------------------------------------------- /src/linux/mod.rs: -------------------------------------------------------------------------------- 1 | #![doc(hidden)] 2 | #![allow(unused_variables)] 3 | use crate::device_class::*; 4 | use crate::extra::*; 5 | use crate::private::Properties; 6 | use crate::Device; 7 | use crate::Fetch; 8 | use std::path::PathBuf; 9 | 10 | /// This is where PCI devices are located. 11 | const PATH_TO_PCI_DEVICES: &str = "/sys/bus/pci/devices/"; 12 | /// This is where the pci.ids file is located. 13 | const PATH_TO_PCI_IDS: &str = "/usr/share/hwdata/pci.ids"; 14 | 15 | #[derive(Debug)] 16 | pub struct LinuxPCIDevice { 17 | path: PathBuf, 18 | address: String, 19 | class_id: Vec, 20 | class_name: String, 21 | subclass_name: String, 22 | vendor_id: Vec, 23 | vendor_name: String, 24 | device_id: Vec, 25 | device_name: String, 26 | revision: Vec, 27 | numa_node: isize, 28 | enabled: bool, 29 | d3cold_allowed: bool, 30 | subsystem_vendor_id: Vec, 31 | subsystem_device_id: Vec, 32 | subsystem_name: String, 33 | } 34 | 35 | impl Device for LinuxPCIDevice { 36 | fn new(path: &str) -> Self { 37 | let mut device: LinuxPCIDevice = Default::default(); 38 | let mut path_vec = [path].to_vec(); 39 | 40 | // One of the following two conditions will try to autocomplete the path of the 41 | // PCI device if the one provided doesn't point to a real path in the filesystem. 42 | if !PathBuf::from(path_vec.concat()).is_dir() { 43 | // e.g. 0000:00:00.0 -> /sys/bus/pci/devices/0000:00:00.0 44 | path_vec.insert(0, PATH_TO_PCI_DEVICES); 45 | device.set_path(PathBuf::from(path_vec.concat())); 46 | if !PathBuf::from(path_vec.concat()).is_dir() { 47 | // e.g. 00:00.0 -> /sys/bus/pci/devices/0000:00:00.0 48 | let mut id = path.to_owned(); 49 | id.insert_str(0, "0000:"); 50 | std::mem::swap(&mut path_vec[1], &mut id.as_str()); 51 | device.set_path(PathBuf::from(path_vec.concat())); 52 | } 53 | } else { 54 | device.set_path(PathBuf::from(path_vec.concat())); 55 | } 56 | 57 | device.set_address(); 58 | device.set_class_id(); 59 | device.set_vendor_id(); 60 | device.set_device_id(); 61 | device.set_numa_node(); 62 | device.set_enabled(); 63 | device.set_d3cold_allowed(); 64 | device.set_revision(); 65 | device.set_subsystem_device_id(); 66 | device.set_subsystem_vendor_id(); 67 | device.set_class_name(); 68 | device.set_device_name(); 69 | device.set_vendor_name(); 70 | device.set_subsystem_name(); 71 | device.set_subclass_name(); 72 | 73 | device 74 | } 75 | 76 | fn path(&self) -> PathBuf { 77 | self.path.to_owned() 78 | } 79 | 80 | fn address(&self) -> String { 81 | self.address.to_owned() 82 | } 83 | 84 | fn class_id(&self) -> Vec { 85 | self.class_id.to_owned() 86 | } 87 | 88 | fn vendor_id(&self) -> Vec { 89 | self.vendor_id.to_owned() 90 | } 91 | 92 | fn device_id(&self) -> Vec { 93 | self.device_id.to_owned() 94 | } 95 | 96 | fn numa_node(&self) -> isize { 97 | self.numa_node 98 | } 99 | 100 | fn class_name(&self) -> String { 101 | self.class_name.to_owned() 102 | } 103 | 104 | fn subclass_name(&self) -> String { 105 | self.subclass_name.to_owned() 106 | } 107 | 108 | fn vendor_name(&self) -> String { 109 | self.vendor_name.to_owned() 110 | } 111 | 112 | fn device_name(&self) -> String { 113 | self.device_name.to_owned() 114 | } 115 | 116 | fn enabled(&self) -> bool { 117 | self.enabled 118 | } 119 | 120 | fn d3cold_allowed(&self) -> bool { 121 | self.d3cold_allowed 122 | } 123 | 124 | fn revision(&self) -> Vec { 125 | self.revision.to_owned() 126 | } 127 | 128 | fn subsystem_name(&self) -> String { 129 | self.subsystem_name.to_owned() 130 | } 131 | 132 | fn subsystem_vendor_id(&self) -> Vec { 133 | self.subsystem_vendor_id.to_owned() 134 | } 135 | 136 | fn subsystem_device_id(&self) -> Vec { 137 | self.subsystem_device_id.to_owned() 138 | } 139 | } 140 | 141 | impl Properties for LinuxPCIDevice { 142 | fn reserved_new(path: &str) -> Self { 143 | let mut device: LinuxPCIDevice = Default::default(); 144 | let mut path_vec = [path].to_vec(); 145 | 146 | // One of the following two conditions will try to autocomplete the path of the 147 | // PCI device if the one provided doesn't point to a real path in the filesystem. 148 | if !PathBuf::from(path_vec.concat()).is_dir() { 149 | // e.g. 0000:00:00.0 -> /sys/bus/pci/devices/0000:00:00.0 150 | path_vec.insert(0, PATH_TO_PCI_DEVICES); 151 | device.set_path(PathBuf::from(path_vec.concat())); 152 | if !PathBuf::from(path_vec.concat()).is_dir() { 153 | // e.g. 00:00.0 -> /sys/bus/pci/devices/0000:00:00.0 154 | let mut id = path.to_owned(); 155 | id.insert_str(0, "0000:"); 156 | std::mem::swap(&mut path_vec[1], &mut id.as_str()); 157 | device.set_path(PathBuf::from(path_vec.concat())); 158 | } 159 | } else { 160 | device.set_path(PathBuf::from(path_vec.concat())); 161 | } 162 | 163 | // reserved_new tries to fetch the least amount of data at first. 164 | // All the other fields can be populated later on. 165 | device.set_class_id(); 166 | device.set_class_name(); 167 | 168 | device 169 | } 170 | 171 | fn set_path(&mut self, p: PathBuf) { 172 | self.path = p; 173 | } 174 | 175 | fn set_address(&mut self) { 176 | self.address = basename( 177 | self.path() 178 | .as_path() 179 | .display() 180 | .to_string() 181 | .replace("0000:", ""), 182 | ); 183 | } 184 | 185 | fn set_class_id(&mut self) { 186 | if let Ok(str) = std::fs::read_to_string(&self.path.join("class")) { 187 | let new_str = str.trim_start_matches("0x").trim_end_matches("\n"); 188 | if let Ok(decoded) = hex::decode(&new_str[..4]) { 189 | self.class_id = decoded; 190 | } 191 | } 192 | } 193 | 194 | fn set_vendor_id(&mut self) { 195 | if let Ok(str) = std::fs::read_to_string(&self.path.join("vendor")) { 196 | let new_str = str.trim_start_matches("0x").trim_end_matches("\n"); 197 | if let Ok(decoded) = hex::decode(&new_str) { 198 | self.vendor_id = decoded; 199 | } 200 | } 201 | } 202 | 203 | fn set_device_id(&mut self) { 204 | if let Ok(str) = std::fs::read_to_string(&self.path.join("device")) { 205 | let new_str = str.trim_start_matches("0x").trim_end_matches("\n"); 206 | if let Ok(decoded) = hex::decode(&new_str) { 207 | self.device_id = decoded; 208 | } 209 | } 210 | } 211 | 212 | fn set_revision(&mut self) { 213 | if let Ok(str) = std::fs::read_to_string(&self.path.join("revision")) { 214 | let new_str = str.trim_start_matches("0x").trim_end_matches("\n"); 215 | if let Ok(decoded) = hex::decode(&new_str) { 216 | self.revision = decoded; 217 | } 218 | } 219 | } 220 | 221 | fn set_numa_node(&mut self) { 222 | if let Ok(str) = std::fs::read_to_string(&self.path.join("numa_node")) { 223 | let prefixless = str.trim_start_matches("0x").trim_end_matches("\n"); 224 | if let Ok(v) = prefixless.parse::() { 225 | self.numa_node = v; 226 | } 227 | } 228 | } 229 | 230 | fn set_subsystem_vendor_id(&mut self) { 231 | if let Ok(str) = std::fs::read_to_string(&self.path.join("subsystem_vendor")) { 232 | let new_str = str.trim_start_matches("0x").trim_end_matches("\n"); 233 | if let Ok(decoded) = hex::decode(&new_str) { 234 | self.subsystem_vendor_id = decoded; 235 | } 236 | } 237 | } 238 | 239 | fn set_subsystem_device_id(&mut self) { 240 | if let Ok(str) = std::fs::read_to_string(&self.path.join("subsystem_device")) { 241 | let new_str = str.trim_start_matches("0x").trim_end_matches("\n"); 242 | if let Ok(decoded) = hex::decode(&new_str) { 243 | self.subsystem_device_id = decoded; 244 | } 245 | } 246 | } 247 | 248 | fn set_class_name(&mut self) { 249 | if self.class_id.is_empty() { 250 | return; 251 | } 252 | 253 | // Associate class_id with class_name 254 | self.class_name = match &self.class_id[0] { 255 | 1 => DeviceClass::MassStorageController.to_string(), 256 | 2 => DeviceClass::NetworkController.to_string(), 257 | 3 => DeviceClass::DisplayController.to_string(), 258 | 4 => DeviceClass::MultimediaController.to_string(), 259 | 5 => DeviceClass::MemoryController.to_string(), 260 | 6 => DeviceClass::Bridge.to_string(), 261 | 7 => DeviceClass::CommunicationController.to_string(), 262 | 8 => DeviceClass::GenericSystemPeripheral.to_string(), 263 | 9 => DeviceClass::InputDeviceController.to_string(), 264 | 10 => DeviceClass::DockingStation.to_string(), 265 | 11 => DeviceClass::Processor.to_string(), 266 | 12 => DeviceClass::SerialBusController.to_string(), 267 | 13 => DeviceClass::WirelessController.to_string(), 268 | 14 => DeviceClass::IntelligentController.to_string(), 269 | 15 => DeviceClass::SatelliteCommunicationsController.to_string(), 270 | 16 => DeviceClass::EncryptionController.to_string(), 271 | 17 => DeviceClass::SignalProcessingController.to_string(), 272 | 18 => DeviceClass::ProcessingAccelerator.to_string(), 273 | 19 => DeviceClass::NonEssentialInstrumentation.to_string(), 274 | 46 => DeviceClass::Coprocessor.to_string(), 275 | 255 => DeviceClass::Unassigned.to_string(), 276 | _ => DeviceClass::Unclassified.to_string(), 277 | } 278 | } 279 | 280 | fn set_subclass_name(&mut self) { 281 | if self.class_id.is_empty() { 282 | return; 283 | } 284 | 285 | if let Ok(lines) = read_lines(PATH_TO_PCI_IDS) { 286 | let class: [u8; 1] = [self.class_id[0]]; 287 | let encoded_class = hex::encode(&class); 288 | let subclass: [u8; 1] = [self.class_id[1]]; 289 | let encoded_subclass = hex::encode(&subclass); 290 | let mut found_my_class = false; 291 | 292 | for line in lines { 293 | if let Ok(l) = &line { 294 | if l.is_empty() || l.starts_with("#") { 295 | continue; 296 | } else if l.starts_with("C") && l.contains(&encoded_class) { 297 | found_my_class = true; 298 | } else if l.starts_with("\t") && l.contains(&encoded_subclass) && found_my_class 299 | { 300 | self.subclass_name = l.replace(&encoded_subclass, "").trim().to_owned(); 301 | return; 302 | } 303 | } 304 | } 305 | } 306 | } 307 | 308 | fn set_vendor_name(&mut self) { 309 | if self.vendor_id.is_empty() { 310 | return; 311 | } 312 | 313 | if let Ok(lines) = read_lines(PATH_TO_PCI_IDS) { 314 | let ven = hex::encode(self.vendor_id.to_owned()); 315 | 316 | for line in lines { 317 | if let Ok(l) = &line { 318 | if l.len() == 0 || l.starts_with("#") || l.starts_with("C") { 319 | continue; 320 | } else if !l.starts_with("\t") && l.contains(&ven) { 321 | self.vendor_name = l.replace(&ven, "").trim().to_owned(); 322 | return; 323 | } 324 | } 325 | } 326 | } 327 | } 328 | 329 | fn set_device_name(&mut self) { 330 | if self.device_id.is_empty() { 331 | return; 332 | } 333 | 334 | if let Ok(lines) = read_lines(PATH_TO_PCI_IDS) { 335 | let dev = hex::encode(self.device_id.to_owned()); 336 | for line in lines { 337 | if let Ok(l) = &line { 338 | if l.len() == 0 || l.starts_with("#") || l.starts_with("C") { 339 | continue; 340 | } else if l.starts_with("\t") && l.contains(&dev) { 341 | self.device_name = l.replace(&dev, "").trim().to_owned(); 342 | return; 343 | } 344 | } 345 | } 346 | } 347 | } 348 | 349 | fn set_subsystem_name(&mut self) { 350 | if self.subsystem_device_id.is_empty() { 351 | return; 352 | } 353 | 354 | if let Ok(lines) = read_lines(PATH_TO_PCI_IDS) { 355 | let sub_dev = hex::encode(self.subsystem_device_id.to_owned()); 356 | let sub_ven = hex::encode(self.subsystem_vendor_id.to_owned()); 357 | 358 | for line in lines { 359 | if let Ok(l) = &line { 360 | if l.len() == 0 && l.starts_with("#") && l.starts_with("C") { 361 | continue; 362 | } else if l.starts_with("\t\t") && l.contains(&sub_dev) && l.contains(&sub_ven) 363 | { 364 | self.subsystem_name = l 365 | .replace(&sub_dev, "") 366 | .replace(&sub_ven, "") 367 | .trim() 368 | .to_owned(); 369 | return; 370 | } 371 | } 372 | } 373 | } 374 | } 375 | 376 | fn set_enabled(&mut self) { 377 | if let Ok(str) = std::fs::read_to_string(&self.path.join("enable")) { 378 | match &str[..] { 379 | "0\n" => self.enabled = false, 380 | _ => self.enabled = true, 381 | } 382 | } 383 | } 384 | 385 | fn set_d3cold_allowed(&mut self) { 386 | if let Ok(str) = std::fs::read_to_string(&self.path.join("d3cold_allowed")) { 387 | match &str[..] { 388 | "0\n" => self.d3cold_allowed = false, 389 | _ => self.d3cold_allowed = true, 390 | } 391 | } 392 | } 393 | } 394 | 395 | impl Default for LinuxPCIDevice { 396 | fn default() -> Self { 397 | LinuxPCIDevice { 398 | path: PathBuf::new(), 399 | address: String::new(), 400 | class_name: String::new(), 401 | subclass_name: String::new(), 402 | vendor_name: String::new(), 403 | device_name: String::new(), 404 | subsystem_name: String::new(), 405 | class_id: vec![], 406 | subsystem_vendor_id: vec![], 407 | subsystem_device_id: vec![], 408 | device_id: vec![], 409 | revision: vec![], 410 | vendor_id: vec![], 411 | numa_node: -1, 412 | d3cold_allowed: false, 413 | enabled: false, 414 | } 415 | } 416 | } 417 | 418 | impl Fetch for LinuxPCIDevice { 419 | fn fetch(maximum_devices: Option) -> Vec { 420 | let mut devices = Vec::new(); 421 | let entries = list_dir_entries(PATH_TO_PCI_DEVICES); 422 | let mut i = 0u8; 423 | for dir in entries { 424 | if let Some(d) = dir.to_str() { 425 | if let Some(m) = maximum_devices { 426 | i = i + 1; 427 | if i > m { 428 | continue; 429 | } 430 | } 431 | 432 | let device = LinuxPCIDevice::new(d); 433 | devices.push(device); 434 | } 435 | } 436 | return devices; 437 | } 438 | 439 | fn fetch_by_class(class: DeviceClass, maximum_devices: Option) -> Vec { 440 | let mut devices = Vec::new(); 441 | let dir_entries = list_dir_entries(PATH_TO_PCI_DEVICES); 442 | let mut i = 0u8; 443 | 444 | for dir in dir_entries { 445 | if let Some(d) = dir.to_str() { 446 | if let Some(m) = maximum_devices { 447 | i = i + 1; 448 | if i > m { 449 | continue; 450 | } 451 | } 452 | 453 | // We're using `PCIDevice::reserved_new()` to initialize a PCIDevice 454 | // with as little data as possible to avoid performance issues. 455 | let mut device = LinuxPCIDevice::reserved_new(d); 456 | if device.class_name() == class.to_string() { 457 | // We can now proceed to get and set the rest of the data 458 | // after having confirmed that the current PCIDevice's class matches 459 | // that provided by the user through a variant of the `DeviceClass` enum. 460 | device.set_address(); 461 | device.set_vendor_id(); 462 | device.set_device_id(); 463 | device.set_numa_node(); 464 | device.set_enabled(); 465 | device.set_d3cold_allowed(); 466 | device.set_revision(); 467 | device.set_subsystem_device_id(); 468 | device.set_subsystem_vendor_id(); 469 | device.set_device_name(); 470 | device.set_vendor_name(); 471 | device.set_subsystem_name(); 472 | device.set_subclass_name(); 473 | devices.push(device); 474 | } 475 | } 476 | } 477 | 478 | return devices; 479 | } 480 | 481 | fn fetch_gpus(maximum_devices: Option) -> Vec { 482 | let mut gpus: Vec = vec![]; 483 | let devices: Vec = Vec::new(); 484 | let dir_entries = list_dir_entries(PATH_TO_PCI_DEVICES); 485 | let mut i = 0u8; 486 | 487 | for dir in dir_entries { 488 | if let Some(d) = dir.to_str() { 489 | if let Some(m) = maximum_devices { 490 | i = i + 1; 491 | if i > m { 492 | continue; 493 | } 494 | } 495 | 496 | // We're using `PCIDevice::reserved_new()` to initialize a PCIDevice 497 | // with as little data as possible to avoid performance issues. 498 | let mut device = LinuxPCIDevice::reserved_new(d); 499 | if device.class_name() == DeviceClass::DisplayController.to_string() { 500 | // We can now proceed to get and set the rest of the data 501 | // after having confirmed that the current PCIDevice's class matches 502 | // that provided by the user through a variant of the `DeviceClass` enum. 503 | device.set_enabled(); 504 | // We're only going to return enabled gpus. 505 | if device.enabled { 506 | device.set_vendor_id(); 507 | device.set_device_id(); 508 | device.set_device_name(); 509 | device.set_vendor_name(); 510 | 511 | let whole_name = device.device_name(); 512 | // Extracting text within brackets from device_name. 513 | if let Some(start_bytes) = whole_name.find("[") { 514 | if let Some(end_bytes) = whole_name.rfind("]") { 515 | device.device_name = 516 | whole_name[start_bytes + 1..end_bytes].to_owned(); 517 | } 518 | } 519 | 520 | if device.vendor_name().contains("Corporation") { 521 | device.vendor_name = device.vendor_name().replace(" Corporation", ""); 522 | } 523 | 524 | let str = String::from(device.vendor_name + " " + &device.device_name); 525 | gpus.push(str); 526 | } 527 | } 528 | } 529 | } 530 | 531 | return gpus; 532 | } 533 | } 534 | 535 | #[cfg(test)] 536 | mod tests { 537 | use super::*; 538 | 539 | const PLACEHOLDER_PCI_DEVICE: &str = "00:00.0"; 540 | 541 | #[test] 542 | fn test_path() { 543 | let device = LinuxPCIDevice::new(PLACEHOLDER_PCI_DEVICE); 544 | assert_ne!(device.path(), PathBuf::new()); 545 | } 546 | 547 | #[test] 548 | fn test_address() { 549 | let device = LinuxPCIDevice::new(PLACEHOLDER_PCI_DEVICE); 550 | assert_ne!(device.address(), ""); 551 | } 552 | 553 | #[test] 554 | fn test_class_id() { 555 | let device = LinuxPCIDevice::new(PLACEHOLDER_PCI_DEVICE); 556 | assert_ne!(device.device_id(), Vec::new()); 557 | } 558 | 559 | #[test] 560 | fn test_vendor_id() { 561 | let device = LinuxPCIDevice::new(PLACEHOLDER_PCI_DEVICE); 562 | assert_ne!(device.vendor_id(), Vec::new()); 563 | } 564 | 565 | #[test] 566 | fn test_device_id() { 567 | let device = LinuxPCIDevice::new(PLACEHOLDER_PCI_DEVICE); 568 | assert_ne!(device.device_id(), Vec::new()); 569 | } 570 | 571 | #[test] 572 | fn test_numa_node() { 573 | let device = LinuxPCIDevice::new(PLACEHOLDER_PCI_DEVICE); 574 | assert_ne!(device.numa_node().to_string(), ""); 575 | } 576 | 577 | #[test] 578 | fn test_revision() { 579 | let device = LinuxPCIDevice::new(PLACEHOLDER_PCI_DEVICE); 580 | assert_ne!(device.revision(), Vec::new()); 581 | } 582 | 583 | #[test] 584 | fn test_subsystem_vendor_id() { 585 | let device = LinuxPCIDevice::new(PLACEHOLDER_PCI_DEVICE); 586 | assert_ne!(device.subsystem_vendor_id(), Vec::new()); 587 | } 588 | 589 | #[test] 590 | fn test_subsystem_device_id() { 591 | let device = LinuxPCIDevice::new(PLACEHOLDER_PCI_DEVICE); 592 | assert_ne!(device.subsystem_device_id(), Vec::new()); 593 | } 594 | 595 | #[test] 596 | fn test_class_name() { 597 | let device = LinuxPCIDevice::new(PLACEHOLDER_PCI_DEVICE); 598 | assert_ne!(device.class_name(), ""); 599 | } 600 | } 601 | --------------------------------------------------------------------------------