hardware_query/
arm.rs

1use crate::Result;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5#[cfg(target_arch = "aarch64")]
6use std::process::Command;
7
8/// ARM-based system type
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub enum ARMSystemType {
11    RaspberryPi,
12    NVIDIAJetson,
13    AppleSilicon,
14    QualcommSnapdragon,
15    MediaTekDimensity,
16    SamsungExynos,
17    HiSiliconKirin,
18    AmazonGraviton,
19    AmpereAltra,
20    Unknown(String),
21}
22
23impl std::fmt::Display for ARMSystemType {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        match self {
26            ARMSystemType::RaspberryPi => write!(f, "Raspberry Pi"),
27            ARMSystemType::NVIDIAJetson => write!(f, "NVIDIA Jetson"),
28            ARMSystemType::AppleSilicon => write!(f, "Apple Silicon"),
29            ARMSystemType::QualcommSnapdragon => write!(f, "Qualcomm Snapdragon"),
30            ARMSystemType::MediaTekDimensity => write!(f, "MediaTek Dimensity"),
31            ARMSystemType::SamsungExynos => write!(f, "Samsung Exynos"),
32            ARMSystemType::HiSiliconKirin => write!(f, "HiSilicon Kirin"),
33            ARMSystemType::AmazonGraviton => write!(f, "Amazon Graviton"),
34            ARMSystemType::AmpereAltra => write!(f, "Ampere Altra"),
35            ARMSystemType::Unknown(name) => write!(f, "{name}"),
36        }
37    }
38}
39
40/// ARM hardware information
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ARMHardwareInfo {
43    /// System type
44    pub system_type: ARMSystemType,
45    
46    /// Board model
47    pub board_model: String,
48    
49    /// Board revision
50    pub board_revision: Option<String>,
51    
52    /// Serial number
53    pub serial_number: Option<String>,
54    
55    /// CPU architecture
56    pub cpu_architecture: String,
57    
58    /// CPU cores
59    pub cpu_cores: u32,
60    
61    /// GPU information (if available)
62    pub gpu_info: Option<String>,
63    
64    /// Available acceleration features
65    pub acceleration_features: Vec<String>,
66    
67    /// AI/ML capabilities
68    pub ml_capabilities: HashMap<String, String>,
69    
70    /// Memory configuration
71    pub memory_mb: Option<u64>,
72    
73    /// Available interfaces
74    pub interfaces: Vec<String>,
75    
76    /// Power information
77    pub power_info: Option<PowerInfo>,
78}
79
80/// Power consumption and thermal information
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct PowerInfo {
83    /// Current power consumption in watts
84    pub power_consumption: Option<f32>,
85    
86    /// CPU temperature in Celsius
87    pub cpu_temperature: Option<f32>,
88    
89    /// GPU temperature in Celsius (if available)
90    pub gpu_temperature: Option<f32>,
91    
92    /// Throttling status
93    pub throttling: bool,
94    
95    /// Power supply voltage
96    pub voltage: Option<f32>,
97}
98
99impl ARMHardwareInfo {
100    /// Detect ARM-based hardware information
101    pub fn detect() -> Result<Option<ARMHardwareInfo>> {
102        #[cfg(target_arch = "aarch64")]
103        {
104            // Only detect on ARM64 systems
105            if let Some(hardware_info) = Self::detect_raspberry_pi()? {
106                return Ok(Some(hardware_info));
107            }
108            
109            if let Some(hardware_info) = Self::detect_nvidia_jetson()? {
110                return Ok(Some(hardware_info));
111            }
112            
113            if let Some(hardware_info) = Self::detect_apple_silicon()? {
114                return Ok(Some(hardware_info));
115            }
116            
117            if let Some(hardware_info) = Self::detect_qualcomm_snapdragon()? {
118                return Ok(Some(hardware_info));
119            }
120            
121            // Generic ARM detection
122            if let Some(hardware_info) = Self::detect_generic_arm()? {
123                return Ok(Some(hardware_info));
124            }
125        }
126        
127        #[cfg(not(target_arch = "aarch64"))]
128        {
129            // On non-ARM systems, still try to detect if we're in an emulated environment
130            // or if there's ARM hardware information available
131        }
132        
133        Ok(None)
134    }
135    
136    #[cfg(target_arch = "aarch64")]
137    fn detect_raspberry_pi() -> Result<Option<ARMHardwareInfo>> {
138        // Check for Raspberry Pi specific files
139        if let Ok(model) = std::fs::read_to_string("/proc/device-tree/model") {
140            if model.to_lowercase().contains("raspberry pi") {
141                let mut hardware_info = ARMHardwareInfo {
142                    system_type: ARMSystemType::RaspberryPi,
143                    board_model: model.trim_end_matches('\0').to_string(),
144                    board_revision: Self::get_pi_revision(),
145                    serial_number: Self::get_pi_serial(),
146                    cpu_architecture: Self::get_cpu_architecture(),
147                    cpu_cores: Self::get_cpu_cores(),
148                    gpu_info: Some("VideoCore GPU".to_string()),
149                    acceleration_features: Self::get_pi_acceleration_features(),
150                    ml_capabilities: Self::get_pi_ml_capabilities(),
151                    memory_mb: Self::get_memory_size(),
152                    interfaces: Self::get_pi_interfaces(),
153                    power_info: Self::get_pi_power_info(),
154                };
155                
156                // Determine specific Pi model for better capabilities
157                if model.contains("Pi 5") {
158                    hardware_info.acceleration_features.push("VideoCore VII GPU".to_string());
159                    hardware_info.ml_capabilities.insert(
160                        "inference_performance".to_string(),
161                        "High (ARM Cortex-A76)".to_string(),
162                    );
163                } else if model.contains("Pi 4") {
164                    hardware_info.acceleration_features.push("VideoCore VI GPU".to_string());
165                    hardware_info.ml_capabilities.insert(
166                        "inference_performance".to_string(),
167                        "Medium (ARM Cortex-A72)".to_string(),
168                    );
169                }
170                
171                return Ok(Some(hardware_info));
172            }
173        }
174        
175        // Alternative detection via /proc/cpuinfo
176        if let Ok(cpuinfo) = std::fs::read_to_string("/proc/cpuinfo") {
177            if cpuinfo.contains("BCM") && cpuinfo.contains("Raspberry Pi") {
178                // Extract model from hardware line
179                for line in cpuinfo.lines() {
180                    if line.starts_with("Model") {
181                        return Ok(Some(ARMHardwareInfo {
182                            system_type: ARMSystemType::RaspberryPi,
183                            board_model: line.split(':').nth(1).unwrap_or("Unknown").trim().to_string(),
184                            board_revision: Self::get_pi_revision(),
185                            serial_number: Self::get_pi_serial(),
186                            cpu_architecture: Self::get_cpu_architecture(),
187                            cpu_cores: Self::get_cpu_cores(),
188                            gpu_info: Some("VideoCore GPU".to_string()),
189                            acceleration_features: Self::get_pi_acceleration_features(),
190                            ml_capabilities: Self::get_pi_ml_capabilities(),
191                            memory_mb: Self::get_memory_size(),
192                            interfaces: Self::get_pi_interfaces(),
193                            power_info: Self::get_pi_power_info(),
194                        }));
195                    }
196                }
197            }
198        }
199        
200        Ok(None)
201    }
202    
203    #[cfg(target_arch = "aarch64")]
204    fn detect_nvidia_jetson() -> Result<Option<ARMHardwareInfo>> {
205        // Check for Jetson-specific files
206        if let Ok(model) = std::fs::read_to_string("/proc/device-tree/model") {
207            let model_lower = model.to_lowercase();
208            if model_lower.contains("jetson") || model_lower.contains("tegra") {
209                let jetson_model = if model_lower.contains("nano") {
210                    "NVIDIA Jetson Nano"
211                } else if model_lower.contains("xavier") {
212                    if model_lower.contains("nx") {
213                        "NVIDIA Jetson Xavier NX"
214                    } else {
215                        "NVIDIA Jetson AGX Xavier"
216                    }
217                } else if model_lower.contains("orin") {
218                    if model_lower.contains("nx") {
219                        "NVIDIA Jetson Orin NX"
220                    } else if model_lower.contains("nano") {
221                        "NVIDIA Jetson Orin Nano"
222                    } else {
223                        "NVIDIA Jetson AGX Orin"
224                    }
225                } else {
226                    "NVIDIA Jetson"
227                };
228                
229                return Ok(Some(ARMHardwareInfo {
230                    system_type: ARMSystemType::NVIDIAJetson,
231                    board_model: jetson_model.to_string(),
232                    board_revision: Self::get_jetson_revision(),
233                    serial_number: Self::get_jetson_serial(),
234                    cpu_architecture: Self::get_cpu_architecture(),
235                    cpu_cores: Self::get_cpu_cores(),
236                    gpu_info: Some("NVIDIA GPU with CUDA support".to_string()),
237                    acceleration_features: Self::get_jetson_acceleration_features(jetson_model),
238                    ml_capabilities: Self::get_jetson_ml_capabilities(jetson_model),
239                    memory_mb: Self::get_memory_size(),
240                    interfaces: Self::get_jetson_interfaces(),
241                    power_info: Self::get_jetson_power_info(),
242                }));
243            }
244        }
245        
246        // Check via lshw or nvidia-smi if available
247        if let Ok(output) = Command::new("nvidia-smi").arg("-L").output() {
248            if output.status.success() {
249                let output_str = String::from_utf8_lossy(&output.stdout);
250                if output_str.contains("Tegra") || output_str.contains("Jetson") {
251                    return Ok(Some(ARMHardwareInfo {
252                        system_type: ARMSystemType::NVIDIAJetson,
253                        board_model: "NVIDIA Jetson (detected via nvidia-smi)".to_string(),
254                        board_revision: None,
255                        serial_number: None,
256                        cpu_architecture: Self::get_cpu_architecture(),
257                        cpu_cores: Self::get_cpu_cores(),
258                        gpu_info: Some(output_str.trim().to_string()),
259                        acceleration_features: vec!["CUDA".to_string(), "TensorRT".to_string()],
260                        ml_capabilities: HashMap::from([
261                            ("cuda_support".to_string(), "true".to_string()),
262                            ("tensorrt_support".to_string(), "true".to_string()),
263                        ]),
264                        memory_mb: Self::get_memory_size(),
265                        interfaces: vec!["USB".to_string(), "Ethernet".to_string(), "WiFi".to_string()],
266                        power_info: None,
267                    }));
268                }
269            }
270        }
271        
272        Ok(None)
273    }
274    
275    #[cfg(target_arch = "aarch64")]
276    fn detect_apple_silicon() -> Result<Option<ARMHardwareInfo>> {
277        #[cfg(target_os = "macos")]
278        {
279            if let Ok(output) = Command::new("sysctl")
280                .args(["-n", "machdep.cpu.brand_string"])
281                .output()
282            {
283                let cpu_brand = String::from_utf8_lossy(&output.stdout);
284                if cpu_brand.contains("Apple M") {
285                    let chip_name = cpu_brand.trim().to_string();
286                    return Ok(Some(ARMHardwareInfo {
287                        system_type: ARMSystemType::AppleSilicon,
288                        board_model: format!("Apple Silicon ({})", chip_name),
289                        board_revision: None,
290                        serial_number: None,
291                        cpu_architecture: "ARM64".to_string(),
292                        cpu_cores: Self::get_cpu_cores(),
293                        gpu_info: Some("Apple GPU".to_string()),
294                        acceleration_features: vec![
295                            "Apple Neural Engine".to_string(),
296                            "Metal".to_string(),
297                            "AMX".to_string(),
298                        ],
299                        ml_capabilities: HashMap::from([
300                            ("neural_engine".to_string(), "true".to_string()),
301                            ("core_ml".to_string(), "true".to_string()),
302                            ("metal_performance_shaders".to_string(), "true".to_string()),
303                        ]),
304                        memory_mb: Self::get_memory_size(),
305                        interfaces: vec!["Thunderbolt".to_string(), "USB-C".to_string(), "WiFi".to_string()],
306                        power_info: None,
307                    }));
308                }
309            }
310        }
311        Ok(None)
312    }
313    
314    // Helper functions for hardware detection
315    #[cfg(target_arch = "aarch64")]
316    fn get_pi_revision() -> Option<String> {
317        std::fs::read_to_string("/proc/cpuinfo")
318            .ok()?
319            .lines()
320            .find(|line| line.starts_with("Revision"))
321            .and_then(|line| line.split(':').nth(1))
322            .map(|s| s.trim().to_string())
323    }
324    
325    #[cfg(target_arch = "aarch64")]
326    fn get_pi_serial() -> Option<String> {
327        std::fs::read_to_string("/proc/cpuinfo")
328            .ok()?
329            .lines()
330            .find(|line| line.starts_with("Serial"))
331            .and_then(|line| line.split(':').nth(1))
332            .map(|s| s.trim().to_string())
333    }
334    
335    #[cfg(target_arch = "aarch64")]
336    fn get_cpu_architecture() -> String {
337        std::env::consts::ARCH.to_string()
338    }
339    
340    #[cfg(target_arch = "aarch64")]
341    fn get_cpu_cores() -> u32 {
342        num_cpus::get() as u32
343    }
344    
345    #[cfg(target_arch = "aarch64")]
346    fn get_memory_size() -> Option<u64> {
347        if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
348            for line in meminfo.lines() {
349                if line.starts_with("MemTotal:") {
350                    if let Some(kb_str) = line.split_whitespace().nth(1) {
351                        if let Ok(kb) = kb_str.parse::<u64>() {
352                            return Some(kb / 1024); // Convert KB to MB
353                        }
354                    }
355                }
356            }
357        }
358        None
359    }
360    
361    // Placeholder implementations - would be expanded with real detection
362    #[cfg(target_arch = "aarch64")]
363    fn detect_qualcomm_snapdragon() -> Result<Option<ARMHardwareInfo>> {
364        Ok(None)
365    }
366    
367    #[cfg(target_arch = "aarch64")]
368    fn detect_generic_arm() -> Result<Option<ARMHardwareInfo>> {
369        Ok(None)
370    }
371    
372    #[cfg(target_arch = "aarch64")]
373    fn get_pi_acceleration_features() -> Vec<String> {
374        vec!["VideoCore GPU".to_string(), "Hardware Video Decode".to_string()]
375    }
376    
377    #[cfg(target_arch = "aarch64")]
378    fn get_pi_ml_capabilities() -> HashMap<String, String> {
379        HashMap::from([
380            ("cpu_inference".to_string(), "true".to_string()),
381            ("frameworks".to_string(), "TensorFlow Lite, PyTorch".to_string()),
382        ])
383    }
384    
385    #[cfg(target_arch = "aarch64")]
386    fn get_pi_interfaces() -> Vec<String> {
387        vec!["GPIO".to_string(), "I2C".to_string(), "SPI".to_string(), "UART".to_string()]
388    }
389    
390    #[cfg(target_arch = "aarch64")]
391    fn get_pi_power_info() -> Option<PowerInfo> {
392        // Try to read Pi-specific thermal info
393        let cpu_temp = std::fs::read_to_string("/sys/class/thermal/thermal_zone0/temp")
394            .ok()
395            .and_then(|temp_str| temp_str.trim().parse::<f32>().ok())
396            .map(|temp| temp / 1000.0); // Convert millidegrees to degrees
397        
398        Some(PowerInfo {
399            power_consumption: None, // Pi doesn't expose this easily
400            cpu_temperature: cpu_temp,
401            gpu_temperature: None,
402            throttling: false, // Would need to check throttling flags
403            voltage: None,
404        })
405    }
406    
407    // Jetson-specific helper functions
408    #[cfg(target_arch = "aarch64")]
409    fn get_jetson_revision() -> Option<String> {
410        None // Placeholder
411    }
412    
413    #[cfg(target_arch = "aarch64")]
414    fn get_jetson_serial() -> Option<String> {
415        None // Placeholder
416    }
417    
418    #[cfg(target_arch = "aarch64")]
419    fn get_jetson_acceleration_features(model: &str) -> Vec<String> {
420        let mut features = vec!["CUDA".to_string(), "TensorRT".to_string()];
421        
422        if model.contains("Orin") {
423            features.push("Ampere GPU".to_string());
424            features.push("NVENC/NVDEC".to_string());
425        } else if model.contains("Xavier") {
426            features.push("Volta GPU".to_string());
427            features.push("Deep Learning Accelerator".to_string());
428        }
429        
430        features
431    }
432    
433    #[cfg(target_arch = "aarch64")]
434    fn get_jetson_ml_capabilities(model: &str) -> HashMap<String, String> {
435        let mut capabilities = HashMap::from([
436            ("cuda_support".to_string(), "true".to_string()),
437            ("tensorrt_support".to_string(), "true".to_string()),
438            ("deep_learning_accelerator".to_string(), "true".to_string()),
439        ]);
440        
441        if model.contains("Orin") {
442            capabilities.insert("inference_performance".to_string(), "Very High".to_string());
443            capabilities.insert("training_support".to_string(), "Yes".to_string());
444        } else if model.contains("Xavier") {
445            capabilities.insert("inference_performance".to_string(), "High".to_string());
446            capabilities.insert("training_support".to_string(), "Limited".to_string());
447        }
448        
449        capabilities
450    }
451    
452    #[cfg(target_arch = "aarch64")]
453    fn get_jetson_interfaces() -> Vec<String> {
454        vec![
455            "USB".to_string(),
456            "Ethernet".to_string(),
457            "WiFi".to_string(),
458            "GPIO".to_string(),
459            "I2C".to_string(),
460            "SPI".to_string(),
461            "UART".to_string(),
462            "CSI Camera".to_string(),
463            "HDMI".to_string(),
464        ]
465    }
466    
467    #[cfg(target_arch = "aarch64")]
468    fn get_jetson_power_info() -> Option<PowerInfo> {
469        None // Placeholder - would read from Jetson power monitoring
470    }
471}