summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJos Dehaes <jos.dehaes@gmail.com>2021-10-15 23:41:37 +0200
committerJos Dehaes <jos.dehaes@gmail.com>2021-10-15 23:41:46 +0200
commitfbae907720afbae47162666b6b0aea974be80c07 (patch)
tree9f8c389b4848f30a4bb1440a8d5d9fffd8a07291
parentcef0f0a68daa88a380000ce200a364e4701ba93a (diff)
temperature sensors via IOKit
-rw-r--r--src/osx/btop_collect.cpp167
-rw-r--r--src/osx/sensors.cpp123
-rw-r--r--src/osx/sensors.hpp8
-rw-r--r--src/osx/smc.hpp69
4 files changed, 148 insertions, 219 deletions
diff --git a/src/osx/btop_collect.cpp b/src/osx/btop_collect.cpp
index c3da4e7..8ddf52e 100644
--- a/src/osx/btop_collect.cpp
+++ b/src/osx/btop_collect.cpp
@@ -50,7 +50,7 @@ tab-size = 4
#include <regex>
#include <string>
-#include "smc.hpp"
+#include "sensors.hpp"
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
@@ -107,7 +107,7 @@ namespace Shared {
fs::path passwd_path;
uint64_t totalMem;
- long pageSize, clkTck, coreCount, physicalCoreCount;
+ long pageSize, clkTck, coreCount;
int totalMem_len;
void init() {
@@ -119,11 +119,6 @@ namespace Shared {
Logger::warning("Could not determine number of cores, defaulting to 1.");
}
- size_t physicalCoreCountSize = sizeof(physicalCoreCount);
- if (sysctlbyname("hw.physicalcpu", &physicalCoreCount, &physicalCoreCountSize, NULL, 0) < 0) {
- Logger::error("Could not get physical core count");
- }
-
pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize <= 0) {
pageSize = 4096;
@@ -227,159 +222,31 @@ namespace Cpu {
return name;
}
- class SMCConnection {
- io_connect_t conn;
- kern_return_t result;
- mach_port_t masterPort;
- io_iterator_t iterator;
- io_object_t device;
-
- public:
- SMCConnection() {
- IOMasterPort(MACH_PORT_NULL, &masterPort);
-
- CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
- result = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
- if (result != kIOReturnSuccess) {
- throw std::runtime_error("failed to get AppleSMC");
- }
-
- device = IOIteratorNext(iterator);
- IOObjectRelease(iterator);
- if (device == 0) {
- throw std::runtime_error("failed to get SMC device");
- }
-
- result = IOServiceOpen(device, mach_task_self(), 0, &conn);
- IOObjectRelease(device);
- if (result != kIOReturnSuccess) {
- throw std::runtime_error("failed to get SMC connection");
- }
- }
- // core means physical core in SMC, while in core map it's cpu threads :-/ Only an issue on hackintosh?
- // this means we can only get the T per physical core
- // another issue with the SMC API is that the key is always 4 chars -> what with systems with more than 9 physical cores?
- // no Mac models with more than 18 threads are released, so no problem so far
- // according to VirtualSMC docs (hackintosh fake SMC) the enumeration follows with alphabetic chars - not implemented yet here (nor in VirtualSMC)
- long long getTemp(int core) {
- SMCVal_t val;
- kern_return_t result;
- char key[] = SMC_KEY_CPU_TEMP;
- if (core >= 0) {
- snprintf(key, 5, "TC%1dc", core);
- }
- result = SMCReadKey(key, &val);
- if (result == kIOReturnSuccess) {
- if (strcmp(val.dataType, DATATYPE_SP78) == 0) {
- // convert sp78 value to temperature
- int intValue = val.bytes[0] * 256 + (unsigned char)val.bytes[1];
- return static_cast<long long>(intValue / 256.0);
- }
- }
- return -1;
- }
- virtual ~SMCConnection() {
- IOServiceClose(conn);
- }
- private:
- UInt32 _strtoul(char *str, int size, int base) {
- UInt32 total = 0;
- int i;
-
- for (i = 0; i < size; i++) {
- if (base == 16) {
- total += str[i] << (size - 1 - i) * 8;
- } else {
- total += (unsigned char)(str[i] << (size - 1 - i) * 8);
- }
- }
- return total;
- }
- void _ultostr(char *str, UInt32 val) {
- str[0] = '\0';
- sprintf(str, "%c%c%c%c",
- (unsigned int)val >> 24,
- (unsigned int)val >> 16,
- (unsigned int)val >> 8,
- (unsigned int)val);
- }
-
- kern_return_t SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure) {
- size_t structureInputSize;
- size_t structureOutputSize;
-
- structureInputSize = sizeof(SMCKeyData_t);
- structureOutputSize = sizeof(SMCKeyData_t);
-
- return IOConnectCallStructMethod(conn, index,
- // inputStructure
- inputStructure, structureInputSize,
- // ouputStructure
- outputStructure, &structureOutputSize);
- }
-
- kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t *val) {
- kern_return_t result;
- SMCKeyData_t inputStructure;
- SMCKeyData_t outputStructure;
-
- memset(&inputStructure, 0, sizeof(SMCKeyData_t));
- memset(&outputStructure, 0, sizeof(SMCKeyData_t));
- memset(val, 0, sizeof(SMCVal_t));
-
- inputStructure.key = _strtoul(key, 4, 16);
- inputStructure.data8 = SMC_CMD_READ_KEYINFO;
-
- result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
- if (result != kIOReturnSuccess)
- return result;
-
- val->dataSize = outputStructure.keyInfo.dataSize;
- _ultostr(val->dataType, outputStructure.keyInfo.dataType);
- inputStructure.keyInfo.dataSize = val->dataSize;
- inputStructure.data8 = SMC_CMD_READ_BYTES;
-
- result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
- if (result != kIOReturnSuccess)
- return result;
-
- memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
-
- return kIOReturnSuccess;
- }
- };
-
bool get_sensors() {
- SMCConnection smcCon;
- try {
- long long t = smcCon.getTemp(-1); // check if we have package T
- if (t > -1) {
- got_sensors = true;
- } else {
- got_sensors = false;
- }
- } catch(std::runtime_error &e) {
- // ignore, we don't have temp
- got_sensors = false;
+ Logger::debug("get_sensors");
+ got_sensors = false;
+ ThermalSensors sensors;
+ if (sensors.getSensors().size() > 0) {
+ got_sensors = true;
}
+ Logger::debug("got sensors:" + std::to_string(got_sensors));
return got_sensors;
}
void update_sensors() {
current_cpu.temp_max = 95; // we have no idea how to get the critical temp
- SMCConnection smcCon;
- int threadsPerCore = Shared::coreCount / Shared::physicalCoreCount;
+ ThermalSensors sensors;
+ std::map<int, double> sensor = sensors.getSensors();
try {
- long long packageT = smcCon.getTemp(-1); // -1 returns package T
- current_cpu.temp.at(0).push_back(packageT);
+ current_cpu.temp.at(0).push_back((long long)sensor[0]);
if (Config::getB("show_coretemp") and not cpu_temp_only) {
- for (int core = 0; core < Shared::coreCount; core++) {
- long long temp = smcCon.getTemp(core / threadsPerCore); // same temp for all threads of same physical core
- if (cmp_less(core + 1, current_cpu.temp.size())) {
- current_cpu.temp.at(core + 1).push_back(temp);
- if (current_cpu.temp.at(core + 1).size() > 20)
- current_cpu.temp.at(core + 1).pop_front();
+ for (int core = 1; core <= Shared::coreCount; core++) {
+ long long temp = (long long) sensor[core];
+ if (cmp_less(core, current_cpu.temp.size())) {
+ current_cpu.temp.at(core).push_back(temp);
+ if (current_cpu.temp.at(core).size() > 20)
+ current_cpu.temp.at(core).pop_front();
}
}
}
diff --git a/src/osx/sensors.cpp b/src/osx/sensors.cpp
new file mode 100644
index 0000000..1b65754
--- /dev/null
+++ b/src/osx/sensors.cpp
@@ -0,0 +1,123 @@
+#include "sensors.hpp"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hidsystem/IOHIDEventSystemClient.h>
+
+#include <iostream>
+#include <map>
+#include <string>
+
+extern "C" {
+typedef struct __IOHIDEvent *IOHIDEventRef;
+typedef struct __IOHIDServiceClient *IOHIDServiceClientRef;
+#ifdef __LP64__
+typedef double IOHIDFloat;
+#else
+typedef float IOHIDFloat;
+#endif
+
+#define IOHIDEventFieldBase(type) (type << 16)
+#define kIOHIDEventTypeTemperature 15
+
+IOHIDEventSystemClientRef IOHIDEventSystemClientCreate(CFAllocatorRef allocator);
+int IOHIDEventSystemClientSetMatching(IOHIDEventSystemClientRef client, CFDictionaryRef match);
+int IOHIDEventSystemClientSetMatchingMultiple(IOHIDEventSystemClientRef client, CFArrayRef match);
+IOHIDEventRef IOHIDServiceClientCopyEvent(IOHIDServiceClientRef, int64_t, int32_t, int64_t);
+CFStringRef IOHIDServiceClientCopyProperty(IOHIDServiceClientRef service, CFStringRef property);
+IOHIDFloat IOHIDEventGetFloatValue(IOHIDEventRef event, int32_t field);
+
+// create a dict ref, like for temperature sensor {"PrimaryUsagePage":0xff00, "PrimaryUsage":0x5}
+CFDictionaryRef matching(int page, int usage) {
+ CFNumberRef nums[2];
+ CFStringRef keys[2];
+
+ keys[0] = CFStringCreateWithCString(0, "PrimaryUsagePage", 0);
+ keys[1] = CFStringCreateWithCString(0, "PrimaryUsage", 0);
+ nums[0] = CFNumberCreate(0, kCFNumberSInt32Type, &page);
+ nums[1] = CFNumberCreate(0, kCFNumberSInt32Type, &usage);
+
+ CFDictionaryRef dict = CFDictionaryCreate(0, (const void **)keys, (const void **)nums, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ return dict;
+}
+
+CFArrayRef getProductNames(CFDictionaryRef sensors) {
+ IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault); // in CFBase.h = NULL
+ // ... this is the same as using kCFAllocatorDefault or the return value from CFAllocatorGetDefault()
+ IOHIDEventSystemClientSetMatching(system, sensors);
+ CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices(system); // matchingsrvs = matching services
+
+ long count = CFArrayGetCount(matchingsrvs);
+ CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+
+ for (int i = 0; i < count; i++) {
+ IOHIDServiceClientRef sc = (IOHIDServiceClientRef)CFArrayGetValueAtIndex(matchingsrvs, i);
+ CFStringRef name = IOHIDServiceClientCopyProperty(sc, CFSTR("Product")); // here we use ...CopyProperty
+ if (name) {
+ CFArrayAppendValue(array, name);
+ } else {
+ CFArrayAppendValue(array, CFSTR("noname")); // @ gives a Ref like in "CFStringRef name"
+ }
+ }
+ return array;
+}
+
+CFArrayRef getThermalValues(CFDictionaryRef sensors) {
+ IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault);
+ IOHIDEventSystemClientSetMatching(system, sensors);
+ CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices(system);
+
+ long count = CFArrayGetCount(matchingsrvs);
+ CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+
+ for (int i = 0; i < count; i++) {
+ IOHIDServiceClientRef sc = (IOHIDServiceClientRef)CFArrayGetValueAtIndex(matchingsrvs, i);
+ IOHIDEventRef event = IOHIDServiceClientCopyEvent(sc, kIOHIDEventTypeTemperature, 0, 0); // here we use ...CopyEvent
+
+ CFNumberRef value;
+ if (event != 0) {
+ double temp = IOHIDEventGetFloatValue(event, IOHIDEventFieldBase(kIOHIDEventTypeTemperature));
+ value = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &temp);
+ } else {
+ double temp = 0;
+ value = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &temp);
+ }
+ CFArrayAppendValue(array, value);
+ }
+ return array;
+}
+}
+std::map<int, double> Cpu::ThermalSensors::getSensors() {
+ std::map<int, double> cpuValues;
+ CFDictionaryRef thermalSensors = matching(0xff00, 5); // 65280_10 = FF00_16
+ // thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of 0xff05
+ // can be checked by ioreg -lfx
+ CFArrayRef thermalNames = getProductNames(thermalSensors);
+ CFArrayRef thermalValues = getThermalValues(thermalSensors);
+ long count = CFArrayGetCount(thermalNames);
+ for (int i = 0; i < count; i++) {
+ CFStringRef nameRef = (CFStringRef)CFArrayGetValueAtIndex(thermalNames, i);
+ char buf[200];
+ CFStringGetCString(nameRef, buf, 200, kCFStringEncodingASCII);
+ std::string n(buf);
+ CFNumberRef value = (CFNumberRef)CFArrayGetValueAtIndex(thermalValues, i);
+ double temp = 0.0;
+ CFNumberGetValue(value, kCFNumberDoubleType, &temp);
+ if (n.starts_with("PMU tdie")) {
+ // Apple Silicon
+ std::string indexString = n.substr(8, 1);
+ int index = stoi(indexString);
+ cpuValues[index - 1] = temp;
+ } else if (n.starts_with("TC") && n[3] == 'c') {
+ // intel mac
+ std::string indexString = n.substr(2, 1);
+ int index = stoi(indexString);
+ cpuValues[index] = temp;
+ } else if (n == "TCAD") {
+ cpuValues[0] = temp; // package T for intel
+ } else if (n == "SOC MTR Temp Sensor0") {
+ cpuValues[0] = temp; // package T for Apple Silicon
+ }
+ }
+ CFRelease(thermalValues);
+ return cpuValues;
+}
diff --git a/src/osx/sensors.hpp b/src/osx/sensors.hpp
new file mode 100644
index 0000000..0cf7159
--- /dev/null
+++ b/src/osx/sensors.hpp
@@ -0,0 +1,8 @@
+#include <map>
+
+namespace Cpu {
+ class ThermalSensors {
+ public:
+ std::map<int, double> getSensors();
+ };
+} // namespace Cpu
diff --git a/src/osx/smc.hpp b/src/osx/smc.hpp
deleted file mode 100644
index 0b851ad..0000000
--- a/src/osx/smc.hpp
+++ /dev/null
@@ -1,69 +0,0 @@
-#pragma once
-
-#define VERSION "0.01"
-
-#define KERNEL_INDEX_SMC 2
-
-#define SMC_CMD_READ_BYTES 5
-#define SMC_CMD_WRITE_BYTES 6
-#define SMC_CMD_READ_INDEX 8
-#define SMC_CMD_READ_KEYINFO 9
-#define SMC_CMD_READ_PLIMIT 11
-#define SMC_CMD_READ_VERS 12
-
-#define DATATYPE_FPE2 "fpe2"
-#define DATATYPE_UINT8 "ui8 "
-#define DATATYPE_UINT16 "ui16"
-#define DATATYPE_UINT32 "ui32"
-#define DATATYPE_SP78 "sp78"
-
-// key values
-#define SMC_KEY_CPU_TEMP "TC0P"
-#define SMC_KEY_CPU1_TEMP "TC1C"
-#define SMC_KEY_CPU2_TEMP "TC2C" // etc
-#define SMC_KEY_FAN0_RPM_CUR "F0Ac"
-
-typedef struct {
- char major;
- char minor;
- char build;
- char reserved[1];
- UInt16 release;
-} SMCKeyData_vers_t;
-
-typedef struct {
- UInt16 version;
- UInt16 length;
- UInt32 cpuPLimit;
- UInt32 gpuPLimit;
- UInt32 memPLimit;
-} SMCKeyData_pLimitData_t;
-
-typedef struct {
- UInt32 dataSize;
- UInt32 dataType;
- char dataAttributes;
-} SMCKeyData_keyInfo_t;
-
-typedef char SMCBytes_t[32];
-
-typedef struct {
- UInt32 key;
- SMCKeyData_vers_t vers;
- SMCKeyData_pLimitData_t pLimitData;
- SMCKeyData_keyInfo_t keyInfo;
- char result;
- char status;
- char data8;
- UInt32 data32;
- SMCBytes_t bytes;
-} SMCKeyData_t;
-
-typedef char UInt32Char_t[5];
-
-typedef struct {
- UInt32Char_t key;
- UInt32 dataSize;
- UInt32Char_t dataType;
- SMCBytes_t bytes;
-} SMCVal_t; \ No newline at end of file