From 6c8e28d20a69aa770932a4a0f9e81f94911cb276 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sat, 24 Oct 2009 05:03:33 +0000 Subject: got a basic smc example working, including outputting all supported keys svn path=/trunk/externals/apple/; revision=12658 --- smc.c | 405 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 405 insertions(+) create mode 100644 smc.c (limited to 'smc.c') diff --git a/smc.c b/smc.c new file mode 100644 index 0000000..d5acf7a --- /dev/null +++ b/smc.c @@ -0,0 +1,405 @@ +/* --------------------------------------------------------------------------*/ +/* */ +/* read the System Management Controller on Apple Mac OS X */ +/* Written by Hans-Christoph Steiner */ +/* */ +/* Copyright (C) 2008-2009 Hans-Christoph Steiner */ +/* 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 3 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* 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, */ +/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +/* */ +/* --------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include + +#include "m_pd.h" +#include "smc.h" + +//#define DEBUG(x) +#define DEBUG(x) x + +/*------------------------------------------------------------------------------ + * CLASS DEF + */ + +static t_class *smc_class; + +typedef struct _smc { + t_object x_obj; + t_symbol* key; + t_outlet* data_outlet; + t_outlet* status_outlet; +} t_smc; + +static io_connect_t conn; + + +/*------------------------------------------------------------------------------ + * WEIRD SHIT TO GET RID OF (why not just use the standard functions?) + */ + +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); +} + +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; +} + +/*------------------------------------------------------------------------------ + * SUPPORT FUNCTIONS + */ + +kern_return_t SMCOpen(void) +{ + kern_return_t result; + mach_port_t masterPort; + io_iterator_t iterator; + io_object_t device; + + result = IOMasterPort(MACH_PORT_NULL, &masterPort); + + CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC"); + result = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator); + if (result != kIOReturnSuccess) + { + post("Error: IOServiceGetMatchingServices() = %08x\n", result); + return 1; + } + + device = IOIteratorNext(iterator); + IOObjectRelease(iterator); + if (device == 0) + { + post("Error: no SMC found\n"); + return 1; + } + + result = IOServiceOpen(device, mach_task_self(), 0, &conn); + IOObjectRelease(device); + if (result != kIOReturnSuccess) + { + post("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) +{ + IOItemCount structureInputSize; + IOByteCount structureOutputSize; + + structureInputSize = sizeof(SMCKeyData_t); + structureOutputSize = sizeof(SMCKeyData_t); + + return IOConnectMethodStructureIStructureO( + conn, + index, + structureInputSize, + &structureOutputSize, + inputStructure, + outputStructure + ); +} + +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); + sprintf(val->key, key); + 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; +} + +kern_return_t SMCWriteKey(SMCVal_t writeVal) +{ + kern_return_t result; + SMCKeyData_t inputStructure; + SMCKeyData_t outputStructure; + + SMCVal_t readVal; + + result = SMCReadKey(writeVal.key, &readVal); + if (result != kIOReturnSuccess) + return result; + + if (readVal.dataSize != writeVal.dataSize) + return kIOReturnError; + + memset(&inputStructure, 0, sizeof(SMCKeyData_t)); + memset(&outputStructure, 0, sizeof(SMCKeyData_t)); + + inputStructure.key = _strtoul(writeVal.key, 4, 16); + inputStructure.data8 = SMC_CMD_WRITE_BYTES; + inputStructure.keyInfo.dataSize = writeVal.dataSize; + memcpy(inputStructure.bytes, writeVal.bytes, sizeof(writeVal.bytes)); + + result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure); + if (result != kIOReturnSuccess) + return result; + + return kIOReturnSuccess; +} + +UInt32 SMCReadIndexCount(void) +{ + SMCVal_t val; + + SMCReadKey("#KEY", &val); + return _strtoul(val.bytes, val.dataSize, 10); +} + +double SMCGetTemperature(char *key) +{ + SMCVal_t val; + kern_return_t result; + + result = SMCReadKey(key, &val); + if (result == kIOReturnSuccess) { + // read succeeded - check returned value + if (val.dataSize > 0) { + if (strcmp(val.dataType, DATATYPE_SP78) == 0) { + // convert fp78 value to temperature + int intValue = (val.bytes[0] * 256 + val.bytes[1]) >> 2; + return intValue / 64.0; + } + } + } + // read failed + return 0.0; +} + +kern_return_t SMCSetFanRpm(char *key, int rpm) +{ + SMCVal_t val; + + strcpy(val.key, key); + val.bytes[0] = (rpm << 2) / 256; + val.bytes[1] = (rpm << 2) % 256; + val.dataSize = 2; + return SMCWriteKey(val); +} + +int SMCGetFanRpm(char *key) +{ + SMCVal_t val; + kern_return_t result; + + result = SMCReadKey(key, &val); + if (result == kIOReturnSuccess) { + // read succeeded - check returned value + if (val.dataSize > 0) { + if (strcmp(val.dataType, DATATYPE_FPE2) == 0) { + // convert FPE2 value to int value + return (int)_strtof(val.bytes, val.dataSize, 2); + } + } + } + // read failed + return -1; +} + + + +/*------------------------------------------------------------------------------ + * IMPLEMENTATION + */ + + +static void smc_symbol(t_smc* x, t_symbol* key) +{ + DEBUG(post("smc_symbol");); + kern_return_t result; + SMCVal_t val; + t_atom output_atom; + + SMCOpen(); + result = SMCReadKey(key->s_name, &val); + if (result == kIOReturnSuccess) { + // read succeeded - check returned value + x->key = key; + if (val.dataSize > 0) { + if (strcmp(val.dataType, DATATYPE_SP78) == 0) { + // convert sp78 value to temperature + int intValue = (val.bytes[0] * 256 + val.bytes[1]) >> 2; + SETFLOAT(&output_atom, intValue / 64.0); + } + outlet_anything(x->data_outlet, key, 1, &output_atom); + } +/* + if ((strcmp(val.dataType, DATATYPE_UINT8) == 0) || + (strcmp(val.dataType, DATATYPE_UINT16) == 0) || + (strcmp(val.dataType, DATATYPE_UINT32) == 0)) { + SETFLOAT(&output_atom, _strtoul(val.bytes, val.dataSize, 10)); + outlet_anything(x->status_outlet, gensym("sensor"), 1, &output_atom); + } else if (strcmp(val.dataType, DATATYPE_FPE2) == 0) { + SETFLOAT(&output_atom, _strtof(val.bytes, val.dataSize, 2)); + outlet_anything(x->status_outlet, gensym("sensor"), 1, &output_atom); + } else { + t_atom output_list[val.dataSize]; + for (i = 0; i < val.dataSize; i++) + SETFLOAT(&output_atom + i, (unsigned char) val.bytes[i]); + outlet_anything(x->status_outlet, gensym("sensor"), + val.dataSize, &output_list); + } +*/ + } + SMCClose(); +} + +static void smc_bang(t_smc* x) +{ + smc_symbol(x, x->key); +} + +static void smc_keys(t_smc* x) +{ + DEBUG(post("smc_keys");); + kern_return_t result; + SMCKeyData_t inputStructure; + SMCKeyData_t outputStructure; + + int totalKeys, i; + UInt32Char_t key; + SMCVal_t val; + + SMCOpen(); + totalKeys = SMCReadIndexCount(); + t_atom output_list[totalKeys]; + for (i = 0; i < totalKeys; i++) + { + memset(&inputStructure, 0, sizeof(SMCKeyData_t)); + memset(&outputStructure, 0, sizeof(SMCKeyData_t)); + memset(&val, 0, sizeof(SMCVal_t)); + + inputStructure.data8 = SMC_CMD_READ_INDEX; + inputStructure.data32 = i; + + result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure); + if (result != kIOReturnSuccess) + continue; + + _ultostr(key, outputStructure.key); + SETSYMBOL(output_list + i, gensym(key)); + } + SMCClose(); + outlet_anything(x->status_outlet, gensym("keys"), totalKeys, output_list); +} + +static void smc_info(t_smc* x) +{ + t_atom output_atom; + SETSYMBOL(&output_atom, x->key); + outlet_anything(x->status_outlet, gensym("key"), 1, &output_atom); + smc_keys(x); +} + +static void *smc_new(void) +{ + DEBUG(post("smc_new");); + t_smc *x = (t_smc *)pd_new(smc_class); + + x->key = &s_; + x->data_outlet = outlet_new(&x->x_obj, &s_list); + x->status_outlet = outlet_new(&x->x_obj, &s_anything); + + return (x); +} + +void smc_setup(void) +{ + smc_class = class_new(gensym("smc"), + (t_newmethod)smc_new, + NULL, + sizeof(t_smc), + CLASS_DEFAULT, + 0); + /* add inlet datatype methods */ + class_addbang(smc_class,(t_method) smc_bang); + class_addsymbol(smc_class,(t_method) smc_symbol); + class_addmethod(smc_class,(t_method) smc_info, gensym("info"), 0); + class_addmethod(smc_class,(t_method) smc_keys, gensym("keys"), 0); +} -- cgit v1.2.1