diff options
Diffstat (limited to 'vendor/sysinfo/src/apple/macos/component/x86.rs')
-rw-r--r-- | vendor/sysinfo/src/apple/macos/component/x86.rs | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/vendor/sysinfo/src/apple/macos/component/x86.rs b/vendor/sysinfo/src/apple/macos/component/x86.rs new file mode 100644 index 000000000..415f90455 --- /dev/null +++ b/vendor/sysinfo/src/apple/macos/component/x86.rs @@ -0,0 +1,326 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::sys::{ffi, macos::utils::IOReleaser}; +use crate::ComponentExt; + +use libc::{c_char, c_int, c_void}; + +use std::mem; + +const COMPONENTS_TEMPERATURE_IDS: &[(&str, &[i8])] = &[ + ("PECI CPU", &['T' as i8, 'C' as i8, 'X' as i8, 'C' as i8]), // PECI CPU "TCXC" + ("PECI CPU", &['T' as i8, 'C' as i8, 'X' as i8, 'c' as i8]), // PECI CPU "TCXc" + ( + "CPU Proximity", + &['T' as i8, 'C' as i8, '0' as i8, 'P' as i8], + ), // CPU Proximity (heat spreader) "TC0P" + ("GPU", &['T' as i8, 'G' as i8, '0' as i8, 'P' as i8]), // GPU "TG0P" + ("Battery", &['T' as i8, 'B' as i8, '0' as i8, 'T' as i8]), // Battery "TB0T" +]; + +pub(crate) struct ComponentFFI { + input_structure: ffi::KeyData_t, + val: ffi::Val_t, + /// It is the `System::connection`. We need it to not require an extra argument + /// in `ComponentExt::refresh`. + connection: ffi::io_connect_t, +} + +impl ComponentFFI { + fn new(key: &[i8], connection: ffi::io_connect_t) -> Option<ComponentFFI> { + unsafe { + get_key_size(connection, key) + .ok() + .map(|(input_structure, val)| ComponentFFI { + input_structure, + val, + connection, + }) + } + } + + fn temperature(&self) -> Option<f32> { + get_temperature_inner(self.connection, &self.input_structure, &self.val) + } +} + +/// Used to get CPU information, not supported on iOS, or inside the default macOS sandbox. +pub(crate) struct Components { + pub inner: Vec<Component>, + connection: Option<IoService>, +} + +impl Components { + pub(crate) fn new() -> Self { + Self { + inner: Vec::with_capacity(2), + connection: IoService::new_connection(), + } + } + + pub(crate) fn refresh(&mut self) { + if let Some(ref connection) = self.connection { + let connection = connection.inner(); + self.inner.clear(); + // getting CPU critical temperature + let critical_temp = + get_temperature(connection, &['T' as i8, 'C' as i8, '0' as i8, 'D' as i8, 0]); + + for (id, v) in COMPONENTS_TEMPERATURE_IDS.iter() { + if let Some(c) = + Component::new((*id).to_owned(), None, critical_temp, v, connection) + { + self.inner.push(c); + } + } + } + } +} + +#[doc = include_str!("../../../../md_doc/component.md")] +pub struct Component { + temperature: f32, + max: f32, + critical: Option<f32>, + label: String, + ffi_part: ComponentFFI, +} + +impl Component { + /// Creates a new `Component` with the given information. + pub(crate) fn new( + label: String, + max: Option<f32>, + critical: Option<f32>, + key: &[i8], + connection: ffi::io_connect_t, + ) -> Option<Component> { + let ffi_part = ComponentFFI::new(key, connection)?; + ffi_part.temperature().map(|temperature| Component { + temperature, + label, + max: max.unwrap_or(temperature), + critical, + ffi_part, + }) + } +} + +impl ComponentExt for Component { + fn temperature(&self) -> f32 { + self.temperature + } + + fn max(&self) -> f32 { + self.max + } + + fn critical(&self) -> Option<f32> { + self.critical + } + + fn label(&self) -> &str { + &self.label + } + + fn refresh(&mut self) { + if let Some(temp) = self.ffi_part.temperature() { + self.temperature = temp; + if self.temperature > self.max { + self.max = self.temperature; + } + } + } +} + +unsafe fn perform_call( + conn: ffi::io_connect_t, + index: c_int, + input_structure: *const ffi::KeyData_t, + output_structure: *mut ffi::KeyData_t, +) -> i32 { + let mut structure_output_size = mem::size_of::<ffi::KeyData_t>(); + + ffi::IOConnectCallStructMethod( + conn, + index as u32, + input_structure, + mem::size_of::<ffi::KeyData_t>(), + output_structure, + &mut structure_output_size, + ) +} + +// Adapted from https://github.com/lavoiesl/osx-cpu-temp/blob/master/smc.c#L28 +#[inline] +fn strtoul(s: &[i8]) -> u32 { + unsafe { + ((*s.get_unchecked(0) as u32) << (3u32 << 3)) + + ((*s.get_unchecked(1) as u32) << (2u32 << 3)) + + ((*s.get_unchecked(2) as u32) << (1u32 << 3)) + + (*s.get_unchecked(3) as u32) + } +} + +#[inline] +unsafe fn ultostr(s: *mut c_char, val: u32) { + *s.offset(0) = ((val >> 24) % 128) as i8; + *s.offset(1) = ((val >> 16) % 128) as i8; + *s.offset(2) = ((val >> 8) % 128) as i8; + *s.offset(3) = (val % 128) as i8; + *s.offset(4) = 0; +} + +unsafe fn get_key_size( + con: ffi::io_connect_t, + key: &[i8], +) -> Result<(ffi::KeyData_t, ffi::Val_t), i32> { + let mut input_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>(); + let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>(); + let mut val: ffi::Val_t = mem::zeroed::<ffi::Val_t>(); + + input_structure.key = strtoul(key); + input_structure.data8 = ffi::SMC_CMD_READ_KEYINFO; + + let result = perform_call( + con, + ffi::KERNEL_INDEX_SMC, + &input_structure, + &mut output_structure, + ); + if result != ffi::KIO_RETURN_SUCCESS { + return Err(result); + } + + val.data_size = output_structure.key_info.data_size; + ultostr( + val.data_type.as_mut_ptr(), + output_structure.key_info.data_type, + ); + input_structure.key_info.data_size = val.data_size; + input_structure.data8 = ffi::SMC_CMD_READ_BYTES; + Ok((input_structure, val)) +} + +unsafe fn read_key( + con: ffi::io_connect_t, + input_structure: &ffi::KeyData_t, + mut val: ffi::Val_t, +) -> Result<ffi::Val_t, i32> { + let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>(); + + match perform_call( + con, + ffi::KERNEL_INDEX_SMC, + input_structure, + &mut output_structure, + ) { + ffi::KIO_RETURN_SUCCESS => { + libc::memcpy( + val.bytes.as_mut_ptr() as *mut c_void, + output_structure.bytes.as_mut_ptr() as *mut c_void, + mem::size_of::<[u8; 32]>(), + ); + Ok(val) + } + result => Err(result), + } +} + +fn get_temperature_inner( + con: ffi::io_connect_t, + input_structure: &ffi::KeyData_t, + original_val: &ffi::Val_t, +) -> Option<f32> { + unsafe { + if let Ok(val) = read_key(con, input_structure, (*original_val).clone()) { + if val.data_size > 0 + && libc::strcmp(val.data_type.as_ptr(), b"sp78\0".as_ptr() as *const i8) == 0 + { + // convert sp78 value to temperature + let x = (i32::from(val.bytes[0]) << 6) + (i32::from(val.bytes[1]) >> 2); + return Some(x as f32 / 64f32); + } + } + } + None +} + +fn get_temperature(con: ffi::io_connect_t, key: &[i8]) -> Option<f32> { + unsafe { + let (input_structure, val) = get_key_size(con, key).ok()?; + get_temperature_inner(con, &input_structure, &val) + } +} + +pub(crate) struct IoService(ffi::io_connect_t); + +impl IoService { + fn new(obj: ffi::io_connect_t) -> Option<Self> { + if obj == 0 { + None + } else { + Some(Self(obj)) + } + } + + pub(crate) fn inner(&self) -> ffi::io_connect_t { + self.0 + } + + // code from https://github.com/Chris911/iStats + // Not supported on iOS, or in the default macOS + pub(crate) fn new_connection() -> Option<Self> { + let mut iterator: ffi::io_iterator_t = 0; + + unsafe { + let matching_dictionary = ffi::IOServiceMatching(b"AppleSMC\0".as_ptr() as *const i8); + let result = ffi::IOServiceGetMatchingServices( + ffi::kIOMasterPortDefault, + matching_dictionary, + &mut iterator, + ); + if result != ffi::KIO_RETURN_SUCCESS { + sysinfo_debug!("Error: IOServiceGetMatchingServices() = {}", result); + return None; + } + let iterator = match IOReleaser::new(iterator) { + Some(i) => i, + None => { + sysinfo_debug!("Error: IOServiceGetMatchingServices() succeeded but returned invalid descriptor"); + return None; + } + }; + + let device = match IOReleaser::new(ffi::IOIteratorNext(iterator.inner())) { + Some(d) => d, + None => { + sysinfo_debug!("Error: no SMC found"); + return None; + } + }; + + let mut conn = 0; + let result = ffi::IOServiceOpen(device.inner(), libc::mach_task_self(), 0, &mut conn); + if result != ffi::KIO_RETURN_SUCCESS { + sysinfo_debug!("Error: IOServiceOpen() = {}", result); + return None; + } + let conn = IoService::new(conn); + if conn.is_none() { + sysinfo_debug!( + "Error: IOServiceOpen() succeeded but returned invalid descriptor..." + ); + } + conn + } + } +} + +impl Drop for IoService { + fn drop(&mut self) { + unsafe { + ffi::IOServiceClose(self.0); + } + } +} |