mirror of
https://github.com/aristocratos/btop.git
synced 2024-09-28 22:21:35 +02:00
temperature sensors via IOKit
This commit is contained in:
parent
cef0f0a68d
commit
fbae907720
@ -50,7 +50,7 @@ tab-size = 4
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#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::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;
|
using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
|
||||||
@ -107,7 +107,7 @@ namespace Shared {
|
|||||||
|
|
||||||
fs::path passwd_path;
|
fs::path passwd_path;
|
||||||
uint64_t totalMem;
|
uint64_t totalMem;
|
||||||
long pageSize, clkTck, coreCount, physicalCoreCount;
|
long pageSize, clkTck, coreCount;
|
||||||
int totalMem_len;
|
int totalMem_len;
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
@ -119,11 +119,6 @@ namespace Shared {
|
|||||||
Logger::warning("Could not determine number of cores, defaulting to 1.");
|
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);
|
pageSize = sysconf(_SC_PAGE_SIZE);
|
||||||
if (pageSize <= 0) {
|
if (pageSize <= 0) {
|
||||||
pageSize = 4096;
|
pageSize = 4096;
|
||||||
@ -227,159 +222,31 @@ namespace Cpu {
|
|||||||
return name;
|
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() {
|
bool get_sensors() {
|
||||||
SMCConnection smcCon;
|
Logger::debug("get_sensors");
|
||||||
try {
|
got_sensors = false;
|
||||||
long long t = smcCon.getTemp(-1); // check if we have package T
|
ThermalSensors sensors;
|
||||||
if (t > -1) {
|
if (sensors.getSensors().size() > 0) {
|
||||||
got_sensors = true;
|
got_sensors = true;
|
||||||
} else {
|
|
||||||
got_sensors = false;
|
|
||||||
}
|
|
||||||
} catch(std::runtime_error &e) {
|
|
||||||
// ignore, we don't have temp
|
|
||||||
got_sensors = false;
|
|
||||||
}
|
}
|
||||||
|
Logger::debug("got sensors:" + std::to_string(got_sensors));
|
||||||
return got_sensors;
|
return got_sensors;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_sensors() {
|
void update_sensors() {
|
||||||
current_cpu.temp_max = 95; // we have no idea how to get the critical temp
|
current_cpu.temp_max = 95; // we have no idea how to get the critical temp
|
||||||
SMCConnection smcCon;
|
ThermalSensors sensors;
|
||||||
int threadsPerCore = Shared::coreCount / Shared::physicalCoreCount;
|
std::map<int, double> sensor = sensors.getSensors();
|
||||||
try {
|
try {
|
||||||
long long packageT = smcCon.getTemp(-1); // -1 returns package T
|
current_cpu.temp.at(0).push_back((long long)sensor[0]);
|
||||||
current_cpu.temp.at(0).push_back(packageT);
|
|
||||||
|
|
||||||
if (Config::getB("show_coretemp") and not cpu_temp_only) {
|
if (Config::getB("show_coretemp") and not cpu_temp_only) {
|
||||||
for (int core = 0; core < Shared::coreCount; core++) {
|
for (int core = 1; core <= Shared::coreCount; core++) {
|
||||||
long long temp = smcCon.getTemp(core / threadsPerCore); // same temp for all threads of same physical core
|
long long temp = (long long) sensor[core];
|
||||||
if (cmp_less(core + 1, current_cpu.temp.size())) {
|
if (cmp_less(core, current_cpu.temp.size())) {
|
||||||
current_cpu.temp.at(core + 1).push_back(temp);
|
current_cpu.temp.at(core).push_back(temp);
|
||||||
if (current_cpu.temp.at(core + 1).size() > 20)
|
if (current_cpu.temp.at(core).size() > 20)
|
||||||
current_cpu.temp.at(core + 1).pop_front();
|
current_cpu.temp.at(core).pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
123
src/osx/sensors.cpp
Normal file
123
src/osx/sensors.cpp
Normal file
@ -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;
|
||||||
|
}
|
8
src/osx/sensors.hpp
Normal file
8
src/osx/sensors.hpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace Cpu {
|
||||||
|
class ThermalSensors {
|
||||||
|
public:
|
||||||
|
std::map<int, double> getSensors();
|
||||||
|
};
|
||||||
|
} // namespace Cpu
|
@ -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;
|
|
Loading…
Reference in New Issue
Block a user