libdrm_amdgpu_sys/pci/
bus_info.rs1use crate::AMDGPU;
2
3#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
5pub struct BUS_INFO {
6 pub domain: u16,
7 pub bus: u8,
8 pub dev: u8,
9 pub func: u8,
10}
11
12use super::{LINK, STATUS};
13
14use std::path::PathBuf;
15
16impl BUS_INFO {
17 pub fn get_sysfs_path(&self) -> PathBuf {
19 PathBuf::from("/sys/bus/pci/devices/").join(self.to_string())
20 }
21
22 pub fn get_hwmon_path(&self) -> Option<PathBuf> {
24 let base = self.get_sysfs_path().join("hwmon");
25 let entry = std::fs::read_dir(base).ok()?.next()?.ok()?;
26
27 Some(entry.path())
28 }
29
30 fn get_drm_path(&self, type_name: &str) -> std::io::Result<PathBuf> {
31 let base = PathBuf::from("/dev/dri");
32
33 let name = format!("by-path/pci-{}-{type_name}", self);
34 let pci_by_path = std::fs::canonicalize(base.join(name));
35
36 pci_by_path.or_else(|e| {
37 std::fs::read_dir(self.get_sysfs_path().join("drm"))?
38 .find_map(|v| {
39 let file_name = v.ok()?.file_name().into_string().ok()?;
40 if file_name.starts_with(type_name) {
41 Some(base.join(file_name))
42 } else {
43 None
44 }
45 })
46 .ok_or(e)
47 })
48 }
49
50 pub fn get_drm_render_path(&self) -> std::io::Result<PathBuf> {
52 self.get_drm_path("render")
53 }
54
55 pub fn get_drm_card_path(&self) -> std::io::Result<PathBuf> {
57 self.get_drm_path("card")
58 }
59
60 pub fn get_debug_dri_path(&self) -> std::io::Result<PathBuf> {
62 let s = format!("amdgpu dev={}", self);
63
64 std::fs::read_dir("/sys/kernel/debug/dri/")?
65 .filter_map(|entry| Some(entry.ok()?.path()))
66 .find(|path| {
67 let Ok(name) = std::fs::read_to_string(path.join("name")) else { return false };
68
69 name.starts_with(&s)
70 })
71 .ok_or(std::io::Error::from(std::io::ErrorKind::NotFound))
72 }
73
74 pub fn get_min_max_link_info_from_dpm(&self) -> Option<[LINK; 2]> {
76 LINK::get_min_max_link_info_from_dpm(self.get_sysfs_path())
77 }
78
79 pub fn get_current_link_info_from_dpm(&self) -> Option<LINK> {
81 LINK::get_current_link_info_from_dpm(self.get_sysfs_path())
82 }
83
84 pub fn get_max_gpu_link(&self) -> Option<LINK> {
86 let mut tmp = self.get_system_pcie_port_sysfs_path();
87
88 tmp.pop();
89
90 LINK::get_max_link(&tmp)
91 }
92
93 pub fn get_max_system_link(&self) -> Option<LINK> {
95 LINK::get_max_link(&self.get_system_pcie_port_sysfs_path())
96 }
97
98 fn from_pathbuf(path: PathBuf) -> Option<Self> {
99 path
100 .canonicalize().ok()?
101 .file_name()?
102 .to_str()?
103 .parse().ok()
104 }
105
106 pub fn get_gpu_pcie_port_bus(&self) -> Self {
110 let mut path = self.get_system_pcie_port_sysfs_path();
111
112 path.pop();
113
114 if let Some(pci) = Self::from_pathbuf(path) {
115 pci
116 } else {
117 *self
118 }
119 }
120
121 pub fn get_system_pcie_port_bus(&self) -> Self {
122 let path = self.get_system_pcie_port_sysfs_path();
123
124 if let Some(pci) = Self::from_pathbuf(path) {
125 pci
126 } else {
127 *self
128 }
129 }
130
131 fn get_system_pcie_port_sysfs_path(&self) -> PathBuf {
135 const VENDOR_ATI: &str = "0x1002\n";
136 const CLASS: &str = "0x060400\n";
138
139 let mut tmp = self.get_sysfs_path().join("../"); for _ in 0..2 {
142 let [Ok(vendor), Ok(class)] = [
143 std::fs::read_to_string(tmp.join("vendor")),
144 std::fs::read_to_string(tmp.join("class")),
145 ] else { break };
146
147 if vendor == VENDOR_ATI && class == CLASS {
148 tmp.push("../");
149 } else {
150 break;
151 }
152 }
153
154 tmp
155 }
156
157 pub fn get_current_link_info(&self) -> Option<LINK> {
159 LINK::get_from_sysfs_with_status(self.get_sysfs_path(), STATUS::Current)
160 }
161
162 pub fn get_max_link_info(&self) -> Option<LINK> {
164 LINK::get_from_sysfs_with_status(self.get_sysfs_path(), STATUS::Max)
165 }
166
167 fn parse_id(&self, file_name: &str) -> Option<u32> {
168 let sysfs_path = self.get_sysfs_path();
169 let id = std::fs::read_to_string(sysfs_path.join(file_name)).ok()?;
170
171 u32::from_str_radix(id.trim_start_matches("0x").trim_end(), 16).ok()
172 }
173
174 pub fn get_device_id(&self) -> Option<u32> {
176 self.parse_id("device")
177 }
178
179 pub fn get_revision_id(&self) -> Option<u32> {
181 self.parse_id("revision")
182 }
183
184 pub fn find_device_name(&self) -> Option<String> {
187 let device_id = self.get_device_id()?;
188 let revision_id = self.get_revision_id()?;
189
190 AMDGPU::find_device_name(device_id, revision_id)
191 }
192
193 pub fn find_device_name_or_default_name(&self) -> String {
194 self.find_device_name().unwrap_or(AMDGPU::DEFAULT_DEVICE_NAME.to_string())
195 }
196
197 pub fn check_if_device_is_active(&self) -> bool {
198 let path = self.get_sysfs_path().join("power/runtime_status");
199 let Ok(s) = std::fs::read_to_string(path) else { return false };
200
201 s.starts_with("active")
202 }
203}
204
205#[derive(Debug, PartialEq, Eq)]
206pub struct ParseBusInfoError;
207
208impl std::str::FromStr for BUS_INFO {
209 type Err = ParseBusInfoError;
210
211 fn from_str(s: &str) -> Result<Self, Self::Err> {
212 let mut split = s.split(&[':', '.']).take(4);
213 let [domain, bus, dev, func] = [split.next(), split.next(), split.next(), split.next()]
214 .map(|s| s.ok_or(ParseBusInfoError));
215 let domain = u16::from_str_radix(domain?, 16).map_err(|_| ParseBusInfoError);
216 let [bus, dev, func] = [bus, dev, func].map(|v| {
217 u8::from_str_radix(v?, 16).map_err(|_| ParseBusInfoError)
218 });
219
220 Ok(Self {
221 domain: domain?,
222 bus: bus?,
223 dev: dev?,
224 func: func?,
225 })
226 }
227}
228
229#[test]
230fn test_pci_bus_info_parse() {
231 let s = "0000:0d:00.0".parse();
232 let bus = BUS_INFO { domain: 0x0, bus: 0xd, dev: 0x0, func: 0x0 };
233
234 assert_eq!(s, Ok(bus));
235}
236
237use std::fmt;
238impl fmt::Display for BUS_INFO {
239 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
240 write!(f,
241 "{:04x}:{:02x}:{:02x}.{:01x}",
242 self.domain, self.bus, self.dev, self.func
243 )
244 }
245}