1
2
3
4
5 package oshi.util.platform.mac;
6
7 import java.nio.ByteBuffer;
8 import java.nio.ByteOrder;
9 import java.util.Arrays;
10 import java.util.Locale;
11 import java.util.Map;
12 import java.util.concurrent.ConcurrentHashMap;
13
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 import com.sun.jna.NativeLong;
18 import com.sun.jna.platform.mac.IOKit.IOConnect;
19 import com.sun.jna.platform.mac.IOKit.IOService;
20 import com.sun.jna.platform.mac.IOKitUtil;
21
22 import oshi.annotation.concurrent.ThreadSafe;
23 import oshi.jna.ByRef.CloseableNativeLongByReference;
24 import oshi.jna.ByRef.CloseablePointerByReference;
25 import oshi.jna.platform.mac.IOKit;
26 import oshi.jna.platform.mac.IOKit.SMCKeyData;
27 import oshi.jna.platform.mac.IOKit.SMCKeyDataKeyInfo;
28 import oshi.jna.platform.mac.IOKit.SMCVal;
29 import oshi.jna.platform.mac.SystemB;
30 import oshi.util.ParseUtil;
31
32
33
34
35 @ThreadSafe
36 public final class SmcUtil {
37
38 private static final Logger LOG = LoggerFactory.getLogger(SmcUtil.class);
39
40 private static final IOKit IO = IOKit.INSTANCE;
41
42
43
44
45 private static Map<Integer, SMCKeyDataKeyInfo> keyInfoCache = new ConcurrentHashMap<>();
46
47
48
49
50 private static final byte[] DATATYPE_SP78 = ParseUtil.asciiStringToByteArray("sp78", 5);
51 private static final byte[] DATATYPE_FPE2 = ParseUtil.asciiStringToByteArray("fpe2", 5);
52 private static final byte[] DATATYPE_FLT = ParseUtil.asciiStringToByteArray("flt ", 5);
53
54 public static final String SMC_KEY_FAN_NUM = "FNum";
55 public static final String SMC_KEY_FAN_SPEED = "F%dAc";
56 public static final String SMC_KEY_CPU_TEMP = "TC0P";
57 public static final String SMC_KEY_CPU_VOLTAGE = "VC0C";
58
59 public static final byte SMC_CMD_READ_BYTES = 5;
60 public static final byte SMC_CMD_READ_KEYINFO = 9;
61 public static final int KERNEL_INDEX_SMC = 2;
62
63 private SmcUtil() {
64 }
65
66
67
68
69
70
71 public static IOConnect smcOpen() {
72 IOService smcService = IOKitUtil.getMatchingService("AppleSMC");
73 if (smcService != null) {
74 try (CloseablePointerByReference connPtr = new CloseablePointerByReference()) {
75 int result = IO.IOServiceOpen(smcService, SystemB.INSTANCE.mach_task_self(), 0, connPtr);
76 if (result == 0) {
77 return new IOConnect(connPtr.getValue());
78 } else if (LOG.isErrorEnabled()) {
79 LOG.error(String.format(Locale.ROOT, "Unable to open connection to AppleSMC service. Error: 0x%08x",
80 result));
81 }
82 } finally {
83 smcService.release();
84 }
85 } else {
86 LOG.error("Unable to locate AppleSMC service");
87 }
88 return null;
89 }
90
91
92
93
94
95
96
97
98 public static int smcClose(IOConnect conn) {
99 return IO.IOServiceClose(conn);
100 }
101
102
103
104
105
106
107
108
109 public static double smcGetFloat(IOConnect conn, String key) {
110 try (SMCVal val = new SMCVal()) {
111 int result = smcReadKey(conn, key, val);
112 if (result == 0 && val.dataSize > 0) {
113 if (Arrays.equals(val.dataType, DATATYPE_SP78) && val.dataSize == 2) {
114
115
116 return val.bytes[0] + val.bytes[1] / 256d;
117 } else if (Arrays.equals(val.dataType, DATATYPE_FPE2) && val.dataSize == 2) {
118
119 return ParseUtil.byteArrayToFloat(val.bytes, val.dataSize, 2);
120 } else if (Arrays.equals(val.dataType, DATATYPE_FLT) && val.dataSize == 4) {
121
122 return ByteBuffer.wrap(val.bytes).order(ByteOrder.LITTLE_ENDIAN).getFloat();
123 }
124 }
125 }
126
127 return 0d;
128 }
129
130
131
132
133
134
135
136
137 public static long smcGetLong(IOConnect conn, String key) {
138 try (SMCVal val = new SMCVal()) {
139 int result = smcReadKey(conn, key, val);
140 if (result == 0) {
141 return ParseUtil.byteArrayToLong(val.bytes, val.dataSize);
142 }
143 }
144
145 return 0;
146 }
147
148
149
150
151
152
153
154
155
156 public static int smcGetKeyInfo(IOConnect conn, SMCKeyData inputStructure, SMCKeyData outputStructure) {
157 if (keyInfoCache.containsKey(inputStructure.key)) {
158 SMCKeyDataKeyInfo keyInfo = keyInfoCache.get(inputStructure.key);
159 outputStructure.keyInfo.dataSize = keyInfo.dataSize;
160 outputStructure.keyInfo.dataType = keyInfo.dataType;
161 outputStructure.keyInfo.dataAttributes = keyInfo.dataAttributes;
162 } else {
163 inputStructure.data8 = SMC_CMD_READ_KEYINFO;
164 int result = smcCall(conn, KERNEL_INDEX_SMC, inputStructure, outputStructure);
165 if (result != 0) {
166 return result;
167 }
168 SMCKeyDataKeyInfo keyInfo = new SMCKeyDataKeyInfo();
169 keyInfo.dataSize = outputStructure.keyInfo.dataSize;
170 keyInfo.dataType = outputStructure.keyInfo.dataType;
171 keyInfo.dataAttributes = outputStructure.keyInfo.dataAttributes;
172 keyInfoCache.put(inputStructure.key, keyInfo);
173 }
174 return 0;
175 }
176
177
178
179
180
181
182
183
184
185 public static int smcReadKey(IOConnect conn, String key, SMCVal val) {
186 try (SMCKeyData inputStructure = new SMCKeyData(); SMCKeyData outputStructure = new SMCKeyData()) {
187 inputStructure.key = (int) ParseUtil.strToLong(key, 4);
188 int result = smcGetKeyInfo(conn, inputStructure, outputStructure);
189 if (result == 0) {
190 val.dataSize = outputStructure.keyInfo.dataSize;
191 val.dataType = ParseUtil.longToByteArray(outputStructure.keyInfo.dataType, 4, 5);
192
193 inputStructure.keyInfo.dataSize = val.dataSize;
194 inputStructure.data8 = SMC_CMD_READ_BYTES;
195
196 result = smcCall(conn, KERNEL_INDEX_SMC, inputStructure, outputStructure);
197 if (result == 0) {
198 System.arraycopy(outputStructure.bytes, 0, val.bytes, 0, val.bytes.length);
199 return 0;
200 }
201 }
202 return result;
203 }
204 }
205
206
207
208
209
210
211
212
213
214
215 public static int smcCall(IOConnect conn, int index, SMCKeyData inputStructure, SMCKeyData outputStructure) {
216 try (CloseableNativeLongByReference size = new CloseableNativeLongByReference(
217 new NativeLong(outputStructure.size()))) {
218 return IO.IOConnectCallStructMethod(conn, index, inputStructure, new NativeLong(inputStructure.size()),
219 outputStructure, size);
220 }
221 }
222 }