diff --git a/cli/src/main.rs b/cli/src/main.rs index f23fb32..d14c8bb 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -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()); diff --git a/daemon/src/config.rs b/daemon/src/config.rs index f13a58b..35d6e12 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -24,8 +24,8 @@ impl From 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, + pub gpu_model: Option, pub path: PathBuf, } diff --git a/daemon/src/daemon_connection.rs b/daemon/src/daemon_connection.rs index e6cfad6..30039ce 100644 --- a/daemon/src/daemon_connection.rs +++ b/daemon/src/daemon_connection.rs @@ -261,7 +261,7 @@ impl DaemonConnection { } } - pub fn get_gpus(&self) -> Result, DaemonError> { + pub fn get_gpus(&self) -> Result>, DaemonError> { log::trace!("sending request"); let mut s = UnixStream::connect(SOCK_PATH).unwrap(); s.write_all(&bincode::serialize(&Action::GetGpus).unwrap()) diff --git a/daemon/src/gpu_controller.rs b/daemon/src/gpu_controller.rs index ca9cf43..f3dd82a 100644 --- a/daemon/src/gpu_controller.rs +++ b/daemon/src/gpu_controller.rs @@ -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::>() - .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(), @@ -315,12 +267,16 @@ impl GpuController { Ok(t) => Some(t), 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, diff --git a/daemon/src/gpu_controller/vendor_data.rs b/daemon/src/gpu_controller/vendor_data.rs new file mode 100644 index 0000000..9e678d8 --- /dev/null +++ b/daemon/src/gpu_controller/vendor_data.rs @@ -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, + pub gpu_model: Option, + pub card_vendor: Option, + pub card_model: Option, +} + +impl VendorData { + pub fn from_ids( + vendor_id: &str, + model_id: &str, + subsys_vendor_id: &str, + subsys_model_id: &str, + ) -> Result { + 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, +} + +impl PciDatabase { + pub fn read<'a>() -> Result { + 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 = HashMap::new(); + + let mut lines = pci_ids.split("\n").into_iter(); + + let mut current_vendor_id: Option = None; + let mut current_device_id: Option = 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::>().join(" "); + + if let Some(current_vendor_id) = ¤t_vendor_id { + if let Some(current_device_id) = ¤t_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::>().join(" "); + + let device = PciDevice::new(name); + + current_device_id = Some(id.clone()); + + if let Some(current_vendor_id) = ¤t_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::>().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 { + 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, +} + +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); + } +} diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 5ab4f2a..42b8899 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -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 = match action { Action::CheckAlive => Ok(DaemonResponse::OK), Action::GetGpus => { - let mut gpus: HashMap = HashMap::new(); + let mut gpus: HashMap> = 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), + Gpus(HashMap>), PowerCap((i64, i64)), FanControlInfo(gpu_controller::FanControlInfo), } diff --git a/gui/src/main.rs b/gui/src/main.rs index 18b6083..72a6062 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -105,8 +105,8 @@ fn build_ui(application: >k::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));