From 469bdce3f325758afc2180bfe3ccbacd47ae14bc Mon Sep 17 00:00:00 2001 From: Sebastian Hildebrandt Date: Sun, 15 Jan 2017 12:31:40 +0100 Subject: [PATCH] added cpuTemperature also for OSX --- README.md | 7 +- binding.gyp | 19 +++++ lib/OSX/smc.cc | 210 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/OSX/smc.h | 84 ++++++++++++++++++++ lib/cpu.js | 15 ++++ 5 files changed, 332 insertions(+), 3 deletions(-) create mode 100755 binding.gyp create mode 100755 lib/OSX/smc.cc create mode 100755 lib/OSX/smc.h diff --git a/README.md b/README.md index b32bb20..0b9a2fd 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ si.cpu() ### Latest Activity +- Version 3.15.0: added cpuTemperature also for OSX - Version 3.14.0: added currentLoad per cpu/core, cpu cache (L1, L2, L3) and cpu flags - Version 3.13.0: added shell (returns standard shell) - Version 3.12.0: refactoring and extended currentLoad (better OSX coverage and added irq load). @@ -136,9 +137,9 @@ This library is splitted in several sections: | - min | X | X | min CPU speed (all cores) | | - max | X | X | max CPU speed (all cores) | | si.cpuTemperature(cb) | X | | CPU temperature (if sensors is installed) | -| - main | X | | main temperature | -| - cores | X | | array of temperatures | -| - max | X | | max temperature | +| - main | X | X | main temperature | +| - cores | X | X | array of temperatures | +| - max | X | X | max temperature | | si.mem(cb) | X | X | Memory information| | - total | X | X | total memory | | - free | X | X | not used | diff --git a/binding.gyp b/binding.gyp new file mode 100755 index 0000000..97e2bbd --- /dev/null +++ b/binding.gyp @@ -0,0 +1,19 @@ +{ + 'targets': [ + ], + 'conditions': [ + ['OS=="mac"', { + 'targets': [ + { + "target_name": "smc", + "sources": [ "lib/OSX/smc.h", "lib/OSX/smc.cc" ], + "link_settings": { + 'libraries': [ + 'IOKit.framework' + ] + } + } + ], + }] + ] +} diff --git a/lib/OSX/smc.cc b/lib/OSX/smc.cc new file mode 100755 index 0000000..14d086a --- /dev/null +++ b/lib/OSX/smc.cc @@ -0,0 +1,210 @@ +/* + * Apple System Management Control (SMC) Tool + * Copyright (C) 2006 devnull + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef BUILDING_NODE_EXTENSION + #define BUILDING_NODE_EXTENSION +#endif + +#include +#include +#include +#include +#include + +#include "smc.h" + +using namespace v8; + +static io_connect_t conn; + +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; +} + +float _strtof(char *str, int size, int e) { + float total = 0; + int i; + + for (i = 0; i < size; i++) { + if (i == (size - 1)) + total += (str[i] & 0xff) >> e; + else + total += str[i] << (size - 1 - i) * (8 - e); + } + + 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 SMCOpen(void) { + kern_return_t result; + io_iterator_t iterator; + io_object_t device; + + CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC"); + result = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &iterator); + if (result != kIOReturnSuccess) { + printf("Error: IOServiceGetMatchingServices() = %08x\n", result); + return 1; + } + + device = IOIteratorNext(iterator); + IOObjectRelease(iterator); + if (device == 0) { + printf("Error: no SMC found\n"); + return 1; + } + + result = IOServiceOpen(device, mach_task_self(), 0, &conn); + IOObjectRelease(device); + if (result != kIOReturnSuccess) { + printf("Error: IOServiceOpen() = %08x\n", result); + return 1; + } + + return kIOReturnSuccess; +} + +kern_return_t SMCClose() { + return IOServiceClose(conn); +} + + +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); + + #if MAC_OS_X_VERSION_10_5 + return IOConnectCallStructMethod( conn, index, + // inputStructure + inputStructure, structureInputSize, + // ouputStructure + outputStructure, &structureOutputSize ); + #else + return IOConnectMethodStructureIStructureO( conn, index, + structureInputSize, /* structureInputSize */ + &structureOutputSize, /* structureOutputSize */ + inputStructure, /* inputStructure */ + outputStructure); /* ouputStructure */ + #endif + +} + +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; +} + +double SMCGet(UInt32Char_t key) { + SMCVal_t val; + kern_return_t result; + + result = SMCReadKey((char*) key, &val); + if (result == kIOReturnSuccess) { + // read succeeded - check returned value + if (val.dataSize > 0) { + if (strcmp(val.dataType, DATATYPE_SP78) == 0) { + // convert sp78 value to temperature + int intValue = val.bytes[0] * 256 + (unsigned char)val.bytes[1]; + return intValue / 256.0; + } + if (strcmp(val.dataType, DATATYPE_UINT8) == 0) { + int intValue = _strtoul((char *)val.bytes, val.dataSize, 10); + return intValue; + } + if (strcmp(val.dataType, DATATYPE_FPE2) == 0) { + int intValue = _strtof(val.bytes, val.dataSize, 2); + return intValue; + } + } + } + // read failed + return 0.0; +} + +void Get(const FunctionCallbackInfo& args) { + Isolate* isolate = Isolate::GetCurrent(); + HandleScope scope(isolate); + SMCOpen(); + if (args.Length() < 1) { + args.GetReturnValue().Set(Undefined(isolate)); + return; + } + if (!args[0]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8(isolate, "Expected string"))); + return; + } + v8::String::Utf8Value k(args[0]->ToString()); + char *key = *k; + double value = SMCGet(key); + SMCClose(); + args.GetReturnValue().Set(Number::New(isolate, value)); +} + +void Init(v8::Handle exports) { + NODE_SET_METHOD(exports, "get", Get); +} + +NODE_MODULE(smc, Init) diff --git a/lib/OSX/smc.h b/lib/OSX/smc.h new file mode 100755 index 0000000..21d2fcf --- /dev/null +++ b/lib/OSX/smc.h @@ -0,0 +1,84 @@ +/* + * Apple System Management Control (SMC) Tool + * Copyright (C) 2006 devnull + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SMC_H__ +#define __SMC_H__ +#endif + +#define VERSION "0.02" + +#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" + +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; diff --git a/lib/cpu.js b/lib/cpu.js index fb92741..b2f3ab3 100644 --- a/lib/cpu.js +++ b/lib/cpu.js @@ -252,6 +252,21 @@ function cpuTemperature(callback) { }); } if (_darwin) { + let smc = require('./build/Release/smc'); + let cores = ['TC1C', 'TC2C', 'TC3C', 'TC4C', 'TC5C', 'TC6C', 'TC7C', 'TC8C']; + let sum = 0; + cores.forEach(function(key) { + let value = smc.get(key); + if (value > 0) { + result.cores.push(value); + sum = sum + value; + if (value > result.max) result.max = value; + } + }); + if (result.cores.length) { + result.main = sum / result.cores.length; + } + if (callback) { callback(result) } resolve(result); }