Reworked the pci.ids parser

This commit is contained in:
Ilya Zlobintsev 2021-01-21 09:40:47 +02:00
parent 2449cc9ee3
commit b7da2cbad5
7 changed files with 248 additions and 71 deletions

View File

@ -67,8 +67,8 @@ fn main() {
fn print_info(d: &DaemonConnection, gpu_id: u32) {
let gpu_info = d.get_gpu_info(gpu_id).unwrap();
println!("{} {}", "GPU Model:".blue(), gpu_info.card_model.bold());
println!("{} {}", "GPU Vendor:".blue(), gpu_info.gpu_vendor.bold());
println!("{} {}", "GPU Model:".blue(), gpu_info.vendor_data.card_model.unwrap_or_default().bold());
println!("{} {}", "GPU Vendor:".blue(), gpu_info.vendor_data.gpu_vendor.unwrap_or_default().bold());
println!("{} {}", "Driver in use:".blue(), gpu_info.driver.bold());
println!("{} {}", "VBIOS Version:".blue(), gpu_info.vbios_version.bold());
println!("{} {}", "VRAM Size:".blue(), gpu_info.vram_size.to_string().bold());

View File

@ -24,8 +24,8 @@ impl From<serde_json::Error> for ConfigError {
#[derive(Deserialize, Serialize, Debug, Clone, Hash, Eq)]
pub struct GpuIdentifier {
pub pci_id: String,
pub card_model: String,
pub gpu_model: String,
pub card_model: Option<String>,
pub gpu_model: Option<String>,
pub path: PathBuf,
}

View File

@ -261,7 +261,7 @@ impl DaemonConnection {
}
}
pub fn get_gpus(&self) -> Result<HashMap<u32, String>, DaemonError> {
pub fn get_gpus(&self) -> Result<HashMap<u32, Option<String>>, DaemonError> {
log::trace!("sending request");
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
s.write_all(&bincode::serialize(&Action::GetGpus).unwrap())

View File

@ -1,6 +1,7 @@
use crate::config::{GpuConfig, GpuIdentifier};
use crate::hw_mon::{HWMon, HWMonError};
use serde::{Deserialize, Serialize};
use vendor_data::VendorData;
use std::{collections::BTreeMap, fs};
use std::{
num::ParseIntError,
@ -8,6 +9,8 @@ use std::{
};
use vulkano::instance::{Instance, InstanceExtensions, PhysicalDevice};
pub mod vendor_data;
#[derive(Serialize, Deserialize, Debug)]
pub enum GpuControllerError {
NotSupported,
@ -123,10 +126,7 @@ pub struct VulkanInfo {
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct GpuInfo {
pub gpu_vendor: String,
pub gpu_model: String,
pub card_model: String,
pub card_vendor: String,
pub vendor_data: VendorData,
pub model_id: String,
pub vendor_id: String,
pub driver: String,
@ -190,8 +190,8 @@ impl GpuController {
let gpu_info = self.get_info();
GpuIdentifier {
pci_id: gpu_info.pci_slot.clone(),
card_model: gpu_info.card_model.clone(),
gpu_model: gpu_info.gpu_model.clone(),
card_model: gpu_info.vendor_data.card_model.clone(),
gpu_model: gpu_info.vendor_data.gpu_model.clone(),
path: self.hw_path.clone(),
}
}
@ -234,54 +234,6 @@ impl GpuController {
}
}
let vendor = "AMD".to_string();
let mut model = String::new();
let mut card_vendor = String::new();
let mut card_model = String::new();
let mut full_hwid_list = String::new();
if Path::exists(&PathBuf::from("/usr/share/hwdata/pci.ids")) {
full_hwid_list = fs::read_to_string("/usr/share/hwdata/pci.ids").unwrap();
} else if Path::exists(&PathBuf::from("/usr/share/misc/pci.ids")) {
full_hwid_list = fs::read_to_string("/usr/share/misc/pci.ids").unwrap();
}
if !full_hwid_list.is_empty() {
//some weird space character, don't touch
let pci_id_line = format!(" {}", model_id.to_lowercase());
let card_ids_line = format!(
" {} {}",
card_vendor_id.to_lowercase(),
card_model_id.to_lowercase()
);
log::trace!("identifying {} \n {}", pci_id_line, card_ids_line);
let lines: Vec<&str> = full_hwid_list.split('\n').collect();
//for line in full_hwid_list.split('\n') {
for i in 0..lines.len() {
let line = lines[i];
if line.len() > card_vendor_id.len() {
if line[0..card_vendor_id.len()] == card_vendor_id.to_lowercase() {
card_vendor = line
.splitn(2, ' ')
.collect::<Vec<&str>>()
.last()
.unwrap()
.trim_start()
.to_string();
}
}
if line.contains(&pci_id_line) {
model = line[pci_id_line.len()..].trim_start().to_string();
}
if line.contains(&card_ids_line) {
card_model = line[card_ids_line.len()..].trim_start().to_string();
}
}
}
let vbios_version = match fs::read_to_string(self.hw_path.join("vbios_version")) {
Ok(v) => v,
Err(_) => "".to_string(),
@ -316,11 +268,15 @@ impl GpuController {
Err(_) => None,
};
let vendor_data = match VendorData::from_ids(&vendor_id, &model_id, &card_vendor_id, &card_model_id) {
Ok(data) => data,
Err(e) => VendorData::default(),
};
log::info!("Vendor data: {:?}", vendor_data);
GpuInfo {
gpu_vendor: vendor,
gpu_model: model,
card_vendor,
card_model,
vendor_data,
model_id,
vendor_id,
driver,

View File

@ -0,0 +1,221 @@
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fs,
path::{Path, PathBuf},
};
#[derive(Debug)]
pub enum VendorDataError {
MissingIdsFile,
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct VendorData {
pub gpu_vendor: Option<String>,
pub gpu_model: Option<String>,
pub card_vendor: Option<String>,
pub card_model: Option<String>,
}
impl VendorData {
pub fn from_ids(
vendor_id: &str,
model_id: &str,
subsys_vendor_id: &str,
subsys_model_id: &str,
) -> Result<VendorData, VendorDataError> {
let vendor_id = vendor_id.to_lowercase();
let model_id = model_id.to_lowercase();
let subsys_vendor_id = subsys_vendor_id.to_lowercase();
let subsys_model_id = subsys_model_id.to_lowercase();
match PciDatabase::read() {
Ok(db) => {
let mut gpu_vendor = None;
let mut gpu_model = None;
let mut card_vendor = None;
let mut card_model = None;
log::trace!("Seacrhing vendor {}", vendor_id);
if let Some(vendor) = db.vendors.get(&vendor_id) {
log::trace!("Found vendor {}", vendor.name);
gpu_vendor = Some(vendor.name.clone());
log::trace!("Searching device {}", model_id);
if let Some(model) = vendor.devices.get(&model_id) {
log::trace!("Found device {}", model.name);
gpu_model = Some(model.name.clone());
log::trace!("Searching subdevice {} {}", subsys_vendor_id, subsys_model_id);
if let Some(subvendor) = db.vendors.get(&subsys_vendor_id) {
log::trace!("Found subvendor {}", subvendor.name);
card_vendor = Some(subvendor.name.clone());
}
if let Some(subdevice) = model.subdevices.get(&(subsys_vendor_id, subsys_model_id)) {
log::trace!("Found subdevice {}", subdevice);
card_model = Some(subdevice.to_owned());
}
}
}
Ok(VendorData { gpu_vendor, gpu_model, card_vendor, card_model })
},
Err(_) => Err(VendorDataError::MissingIdsFile)
}
}
}
#[derive(Debug)]
enum PciDatabaseError {
FileNotFound,
}
struct PciDatabase {
pub vendors: HashMap<String, PciVendor>,
}
impl PciDatabase {
pub fn read<'a>() -> Result<Self, PciDatabaseError> {
let _ = env_logger::builder().is_test(true).try_init();
match Self::read_pci_db() {
Some(pci_ids) => {
log::trace!("Parsing pci.ids");
let mut vendors: HashMap<String, PciVendor> = HashMap::new();
let mut lines = pci_ids.split("\n").into_iter();
let mut current_vendor_id: Option<String> = None;
let mut current_device_id: Option<String> = None;
while let Some(line) = lines.next() {
if line.starts_with("#") | line.is_empty() {
continue;
} else if line.starts_with("\t\t") {
let mut split = line.split_whitespace();
let vendor_id = split.next().unwrap().to_owned();
let device_id = split.next().unwrap().to_owned();
let name = split.collect::<Vec<&str>>().join(" ");
if let Some(current_vendor_id) = &current_vendor_id {
if let Some(current_device_id) = &current_device_id {
vendors
.get_mut(current_vendor_id)
.unwrap()
.devices
.get_mut(current_device_id)
.unwrap()
.subdevices
.insert((vendor_id, device_id), name);
}
}
} else if line.starts_with("\t") {
let mut split = line.split_whitespace();
let id = split.next().unwrap().to_owned();
let name = split.collect::<Vec<&str>>().join(" ");
let device = PciDevice::new(name);
current_device_id = Some(id.clone());
if let Some(current_vendor_id) = &current_vendor_id {
vendors
.get_mut(current_vendor_id)
.unwrap()
.devices
.insert(id, device);
}
} else {
let mut split = line.split_whitespace();
let id = split.next().unwrap().to_owned();
let name = split.collect::<Vec<&str>>().join(" ");
current_vendor_id = Some(id.clone());
let vendor = PciVendor::new(name);
vendors.insert(id, vendor);
}
}
Ok(PciDatabase { vendors })
}
None => Err(PciDatabaseError::FileNotFound),
}
}
fn read_pci_db() -> Option<String> {
let paths = ["/usr/share/hwdata/pci.ids", "/usr/share/misc/pci.ids"];
if let Some(path) = paths.iter().find(|path| Path::exists(&PathBuf::from(path))) {
let all_ids = fs::read_to_string(path).unwrap();
Some(all_ids)
} else {
None
}
}
}
#[derive(Debug)]
struct PciVendor {
pub name: String,
pub devices: HashMap<String, PciDevice>,
}
impl PciVendor {
pub fn new(name: String) -> Self {
PciVendor {
name,
devices: HashMap::new(),
}
}
}
#[derive(Debug, Clone)]
struct PciDevice {
pub name: String,
pub subdevices: HashMap<(String, String), String>, // <(vendor_id, device_id), name>
}
impl PciDevice {
pub fn new(name: String) -> Self {
PciDevice {
name,
subdevices: HashMap::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn init() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[test]
fn parse_polaris() {
init();
let data = VendorData::from_ids("1002", "67DF", "1DA2", "E387").unwrap();
assert_eq!(data.gpu_vendor, Some("Advanced Micro Devices, Inc. [AMD/ATI]".to_string()));
assert_eq!(data.gpu_model, Some("Ellesmere [Radeon RX 470/480/570/570X/580/580X/590]".to_string()));
assert_eq!(data.card_vendor, Some("Sapphire Technology Limited".to_string()));
assert_eq!(data.card_model, Some("Radeon RX 570 Pulse 4GB".to_string()));
}
#[test]
fn parse_vega() {
let data = VendorData::from_ids("1002", "687F", "1043", "0555").unwrap();
assert_eq!(data.gpu_vendor, Some("Advanced Micro Devices, Inc. [AMD/ATI]".to_string()));
assert_eq!(data.gpu_model, Some("Vega 10 XL/XT [Radeon RX Vega 56/64]".to_string()));
assert_eq!(data.card_vendor, Some("ASUSTeK Computer Inc.".to_string()));
assert_eq!(data.card_model, None);
}
}

View File

@ -95,7 +95,7 @@ impl Daemon {
let gpu_info = controller.get_info();
for (id, (gpu_identifier, gpu_config)) in &config.gpu_configs {
if gpu_info.pci_slot == gpu_identifier.pci_id && gpu_info.card_model == gpu_identifier.card_model && gpu_info.gpu_model == gpu_identifier.gpu_model {
if gpu_info.pci_slot == gpu_identifier.pci_id && gpu_info.vendor_data.card_model == gpu_identifier.card_model && gpu_info.vendor_data.gpu_model == gpu_identifier.gpu_model {
controller.load_config(gpu_config.clone());
gpu_controllers.insert(id.clone(), controller);
log::info!("already known");
@ -155,9 +155,9 @@ impl Daemon {
let response: Result<DaemonResponse, DaemonError> = match action {
Action::CheckAlive => Ok(DaemonResponse::OK),
Action::GetGpus => {
let mut gpus: HashMap<u32, String> = HashMap::new();
let mut gpus: HashMap<u32, Option<String>> = HashMap::new();
for (id, controller) in &self.gpu_controllers {
gpus.insert(*id, controller.get_info().gpu_model.clone());
gpus.insert(*id, controller.get_info().vendor_data.gpu_model.clone());
}
Ok(DaemonResponse::Gpus(gpus))
},
@ -336,7 +336,7 @@ pub enum DaemonResponse {
OK,
GpuInfo(gpu_controller::GpuInfo),
GpuStats(gpu_controller::GpuStats),
Gpus(HashMap<u32, String>),
Gpus(HashMap<u32, Option<String>>),
PowerCap((i64, i64)),
FanControlInfo(gpu_controller::FanControlInfo),
}

View File

@ -105,8 +105,8 @@ fn build_ui(application: &gtk::Application) {
let gpus = d.get_gpus().unwrap();
for gpu in &gpus {
gpu_select_comboboxtext.append(Some(&gpu.0.to_string()), &gpu.1);
for (id, gpu) in &gpus {
gpu_select_comboboxtext.append(Some(&id.to_string()), &gpu.clone().unwrap_or_default());
}
//limits the length of gpu names in combobox
@ -408,8 +408,8 @@ fn set_info(builder: &Builder, d: DaemonConnection, gpu_id: u32, gpu_power_level
let gpu_info = d.get_gpu_info(gpu_id).unwrap();
gpu_model_text_buffer.set_text(&gpu_info.card_model);
manufacturer_text_buffer.set_text(&gpu_info.card_vendor);
gpu_model_text_buffer.set_text(&gpu_info.vendor_data.card_model.unwrap_or_default());
manufacturer_text_buffer.set_text(&gpu_info.vendor_data.card_vendor.unwrap_or_default());
vbios_version_text_buffer.set_text(&gpu_info.vbios_version);
driver_text_buffer.set_text(&gpu_info.driver);
vram_size_text_buffer.set_text(&format!("{} MiB", &gpu_info.vram_size));