summaryrefslogtreecommitdiffstats
path: root/src/osx/sensors.cpp
blob: 8451bd14b0029a2be109a6ea82b0fd857642249b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "sensors.hpp"

#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hidsystem/IOHIDEventSystemClient.h>

#include <iostream>
#include <map>
#include <string>

#include <btop_tools.hpp>

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);
	CFRelease(keys);
	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"
		}
		CFRelease(name);
	}
	CFRelease(system);
	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);
		CFRelease(value);
	}
	CFRelease(system);
	return array;
}
}
unordered_flat_map<int, double> Cpu::ThermalSensors::getSensors() {
	unordered_flat_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(thermalSensors);
	CFRelease(thermalNames);
	CFRelease(thermalValues);

	return cpuValues;
}