summaryrefslogtreecommitdiffstats
path: root/vendor/sysinfo/src/apple/macos/component/x86.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sysinfo/src/apple/macos/component/x86.rs')
-rw-r--r--vendor/sysinfo/src/apple/macos/component/x86.rs326
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);
+ }
+ }
+}