use crate::device::common::constants::google_tpu::GOOGLE_VENDOR_ID;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct SysfsTpuInfo {
pub index: u32,
#[allow(dead_code)]
pub path: PathBuf,
#[allow(dead_code)]
pub vendor_id: String,
pub device_id: String,
#[allow(dead_code)]
pub temperature: Option<f64>,
}
pub fn scan_sysfs_tpus() -> Vec<SysfsTpuInfo> {
let mut devices = Vec::new();
let accel_path = Path::new("/sys/class/accel");
if accel_path.exists()
&& let Ok(entries) = fs::read_dir(accel_path)
{
let mut accel_entries: Vec<_> = entries.flatten().map(|e| e.path()).collect();
accel_entries.sort();
for (idx, path) in accel_entries.iter().enumerate() {
if let Some(info) = parse_accel_device(path, idx as u32) {
devices.push(info);
}
}
}
if devices.is_empty() {
let pci_path = Path::new("/sys/bus/pci/devices");
if pci_path.exists()
&& let Ok(entries) = fs::read_dir(pci_path)
{
let mut pci_entries: Vec<_> = entries.flatten().map(|e| e.path()).collect();
pci_entries.sort();
let mut index = 0;
for path in pci_entries {
if let Some(info) = parse_pci_device(&path, index) {
devices.push(info);
index += 1;
}
}
}
}
devices
}
fn parse_pci_device(path: &Path, index: u32) -> Option<SysfsTpuInfo> {
let vendor_path = path.join("vendor");
let vendor = read_sysfs_string(&vendor_path)?;
let vendor_norm = vendor.trim().to_lowercase();
if !vendor_norm.ends_with("1ae0") {
return None;
}
let class_path = path.join("class");
if let Some(class_code) = read_sysfs_string(&class_path) {
let class_norm = class_code.trim().to_lowercase();
let is_accelerator = if class_norm.starts_with("0x") {
class_norm.starts_with("0x12")
} else {
class_norm.starts_with("12")
};
if !is_accelerator {
return None;
}
} else {
let device_id_path = path.join("device");
let device_id = read_sysfs_string(&device_id_path).unwrap_or_default();
if !is_known_tpu_device_id(&device_id) {
return None;
}
}
let device_id_path = path.join("device");
let device_id = read_sysfs_string(&device_id_path).unwrap_or_else(|| "unknown".to_string());
let temperature = read_temperature(path);
Some(SysfsTpuInfo {
index,
path: path.to_path_buf(),
vendor_id: vendor,
device_id,
temperature,
})
}
fn is_known_tpu_device_id(device_id: &str) -> bool {
let id = device_id.trim().to_lowercase().replace("0x", "");
match id.as_str() {
"0027" | "0028" | "0050" | "0051" | "0060" | "0061" | "0062" | "006f" | "0070" | "0071" | "0080" | "0081" => true,
_ => false,
}
}
fn parse_accel_device(path: &Path, index: u32) -> Option<SysfsTpuInfo> {
let device_dir = path.join("device");
let vendor_path = device_dir.join("vendor");
let vendor = read_sysfs_string(&vendor_path)?;
if vendor != GOOGLE_VENDOR_ID {
return None;
}
let device_id_path = device_dir.join("device");
let device_id = read_sysfs_string(&device_id_path).unwrap_or_else(|| "unknown".to_string());
let temperature = read_temperature(&device_dir);
Some(SysfsTpuInfo {
index,
path: path.to_path_buf(),
vendor_id: vendor,
device_id,
temperature,
})
}
fn read_temperature(device_dir: &Path) -> Option<f64> {
let hwmon_dir = device_dir.join("hwmon");
if let Ok(entries) = fs::read_dir(hwmon_dir) {
for entry in entries.flatten() {
let temp_input = entry.path().join("temp1_input");
if temp_input.exists()
&& let Some(val) = read_sysfs_int(&temp_input)
{
return Some(val as f64 / 1000.0);
}
}
}
None
}
fn read_sysfs_string(path: &Path) -> Option<String> {
fs::read_to_string(path).ok().map(|s| s.trim().to_string())
}
fn read_sysfs_int(path: &Path) -> Option<i64> {
fs::read_to_string(path)
.ok()
.and_then(|s| s.trim().parse::<i64>().ok())
}