1 #include <stdio.h>
2 #include <string.h>
3 #include "smc_darwin.h"
4 
5 #define IOSERVICE_SMC "AppleSMC"
6 #define IOSERVICE_MODEL "IOPlatformExpertDevice"
7 
8 #define DATA_TYPE_SP78 "sp78"
9 
10 typedef enum {
11   kSMCUserClientOpen = 0,
12   kSMCUserClientClose = 1,
13   kSMCHandleYPCEvent = 2,
14   kSMCReadKey = 5,
15   kSMCWriteKey = 6,
16   kSMCGetKeyCount = 7,
17   kSMCGetKeyFromIndex = 8,
18   kSMCGetKeyInfo = 9,
19 } selector_t;
20 
21 typedef struct {
22   unsigned char major;
23   unsigned char minor;
24   unsigned char build;
25   unsigned char reserved;
26   unsigned short release;
27 } SMCVersion;
28 
29 typedef struct {
30   uint16_t version;
31   uint16_t length;
32   uint32_t cpuPLimit;
33   uint32_t gpuPLimit;
34   uint32_t memPLimit;
35 } SMCPLimitData;
36 
37 typedef struct {
38   IOByteCount data_size;
39   uint32_t data_type;
40   uint8_t data_attributes;
41 } SMCKeyInfoData;
42 
43 typedef struct {
44   uint32_t key;
45   SMCVersion vers;
46   SMCPLimitData p_limit_data;
47   SMCKeyInfoData key_info;
48   uint8_t result;
49   uint8_t status;
50   uint8_t data8;
51   uint32_t data32;
52   uint8_t bytes[32];
53 } SMCParamStruct;
54 
55 typedef enum {
56   kSMCSuccess = 0,
57   kSMCError = 1,
58   kSMCKeyNotFound = 0x84,
59 } kSMC_t;
60 
61 typedef struct {
62   uint8_t data[32];
63   uint32_t data_type;
64   uint32_t data_size;
65   kSMC_t kSMC;
66 } smc_return_t;
67 
68 static const int SMC_KEY_SIZE = 4; // number of characters in an SMC key.
69 static io_connect_t conn;          // our connection to the SMC.
70 
open_smc(void)71 kern_return_t open_smc(void) {
72   kern_return_t result;
73   io_service_t service;
74 
75   service = IOServiceGetMatchingService(kIOMasterPortDefault,
76                                         IOServiceMatching(IOSERVICE_SMC));
77   if (service == 0) {
78     // Note: IOServiceMatching documents 0 on failure
79     printf("ERROR: %s NOT FOUND\n", IOSERVICE_SMC);
80     return kIOReturnError;
81   }
82 
83   result = IOServiceOpen(service, mach_task_self(), 0, &conn);
84   IOObjectRelease(service);
85 
86   return result;
87 }
88 
close_smc(void)89 kern_return_t close_smc(void) { return IOServiceClose(conn); }
90 
to_uint32(char * key)91 static uint32_t to_uint32(char *key) {
92   uint32_t ans = 0;
93   uint32_t shift = 24;
94 
95   if (strlen(key) != SMC_KEY_SIZE) {
96     return 0;
97   }
98 
99   for (int i = 0; i < SMC_KEY_SIZE; i++) {
100     ans += key[i] << shift;
101     shift -= 8;
102   }
103 
104   return ans;
105 }
106 
call_smc(SMCParamStruct * input,SMCParamStruct * output)107 static kern_return_t call_smc(SMCParamStruct *input, SMCParamStruct *output) {
108   kern_return_t result;
109   size_t input_cnt = sizeof(SMCParamStruct);
110   size_t output_cnt = sizeof(SMCParamStruct);
111 
112   result = IOConnectCallStructMethod(conn, kSMCHandleYPCEvent, input, input_cnt,
113                                      output, &output_cnt);
114 
115   if (result != kIOReturnSuccess) {
116     result = err_get_code(result);
117   }
118   return result;
119 }
120 
read_smc(char * key,smc_return_t * result_smc)121 static kern_return_t read_smc(char *key, smc_return_t *result_smc) {
122   kern_return_t result;
123   SMCParamStruct input;
124   SMCParamStruct output;
125 
126   memset(&input, 0, sizeof(SMCParamStruct));
127   memset(&output, 0, sizeof(SMCParamStruct));
128   memset(result_smc, 0, sizeof(smc_return_t));
129 
130   input.key = to_uint32(key);
131   input.data8 = kSMCGetKeyInfo;
132 
133   result = call_smc(&input, &output);
134   result_smc->kSMC = output.result;
135 
136   if (result != kIOReturnSuccess || output.result != kSMCSuccess) {
137     return result;
138   }
139 
140   result_smc->data_size = output.key_info.data_size;
141   result_smc->data_type = output.key_info.data_type;
142 
143   input.key_info.data_size = output.key_info.data_size;
144   input.data8 = kSMCReadKey;
145 
146   result = call_smc(&input, &output);
147   result_smc->kSMC = output.result;
148 
149   if (result != kIOReturnSuccess || output.result != kSMCSuccess) {
150     return result;
151   }
152 
153   memcpy(result_smc->data, output.bytes, sizeof(output.bytes));
154 
155   return result;
156 }
157 
get_temperature(char * key)158 double get_temperature(char *key) {
159   kern_return_t result;
160   smc_return_t result_smc;
161 
162   result = read_smc(key, &result_smc);
163 
164   if (!(result == kIOReturnSuccess) && result_smc.data_size == 2 &&
165       result_smc.data_type == to_uint32(DATA_TYPE_SP78)) {
166     return 0.0;
167   }
168 
169   return (double)result_smc.data[0];
170 }
171