summaryrefslogtreecommitdiffstats
path: root/devices
diff options
context:
space:
mode:
authorSean E. Russell <ser@ser1.net>2020-02-27 18:51:28 -0600
committerSean E. Russell <ser@ser1.net>2020-02-28 07:06:51 -0600
commit7a89225d729d4c54c5bc95b21979f48f9cd046b1 (patch)
tree10bf143324cf553765d7002d0eb82abc21c72bb1 /devices
parent4fce1654c5c263797205997e7635c7d06c393a85 (diff)
Abstracts CPU & mem devices.
Finishes device refactoring: temps Refactoring to allow updating maps. Simpler, and optimizable.
Diffstat (limited to 'devices')
-rw-r--r--devices/cpu.go50
-rw-r--r--devices/cpu_cpu.go25
-rw-r--r--devices/include/smc.c700
-rw-r--r--devices/include/smc.h254
-rw-r--r--devices/mem.go26
-rw-r--r--devices/mem_mem.go21
-rw-r--r--devices/mem_swap_freebsd.go44
-rw-r--r--devices/mem_swap_other.go23
-rw-r--r--devices/temp.go22
-rw-r--r--devices/temp_darwin.go46
-rw-r--r--devices/temp_freebsd.go45
-rw-r--r--devices/temp_linux.go29
-rw-r--r--devices/temp_openbsd.go72
-rw-r--r--devices/temp_windows.go24
14 files changed, 1349 insertions, 32 deletions
diff --git a/devices/cpu.go b/devices/cpu.go
index b35bdd3..21d21b6 100644
--- a/devices/cpu.go
+++ b/devices/cpu.go
@@ -1,44 +1,34 @@
package devices
import (
+ "log"
"time"
)
-var deviceCounts []func(bool) (int, error)
-var devicePercents []func(time.Duration, bool) ([]float64, error)
-var numDevices int
+var cpuFuncs []func(map[string]float64, time.Duration, bool) map[string]error
-// Counts returns the number of CPUs registered.
+// RegisterCPU adds a new CPU device to the CPU widget. labels returns the
+// names of the devices; they should be as short as possible, and the indexes
+// of the returned slice should align with the values returned by the percents
+// function. The percents function should return the percent CPU usage of the
+// device(s), sliced over the time duration supplied. If the bool argument to
+// percents is true, it is expected that the return slice
//
-// logical tells Counts to count the logical cores; this may be ignored for
-// some devices.
-func Counts(logical bool) (int, error) {
- var rv int
- var re error
- for _, d := range deviceCounts {
- r, err := d(logical)
- if err != nil {
- return rv, re
- }
- rv += r
- }
- return rv, re
+// labels may be called once and the value cached. This means the number of
+// cores should not change dynamically.
+func RegisterCPU(f func(map[string]float64, time.Duration, bool) map[string]error) {
+ cpuFuncs = append(cpuFuncs, f)
}
-// Percent calculates the percentage of cpu used either per CPU or combined.
+// CPUPercent calculates the percentage of cpu used either per CPU or combined.
// Returns one value per cpu, or a single value if percpu is set to false.
-func Percent(interval time.Duration, combined bool) ([]float64, error) {
- var rvs []float64
- rvs = make([]float64, 0, numDevices)
- for _, f := range devicePercents {
- vs, err := f(interval, combined)
- if err != nil {
- return rvs, err
- }
- for _, v := range vs {
- rvs = append(rvs, v)
+func UpdateCPU(cpus map[string]float64, interval time.Duration, logical bool) {
+ for _, f := range cpuFuncs {
+ errs := f(cpus, interval, logical)
+ if errs != nil {
+ for k, e := range errs {
+ log.Printf("%s: %s", k, e)
+ }
}
}
- numDevices = len(rvs)
- return rvs, nil
}
diff --git a/devices/cpu_cpu.go b/devices/cpu_cpu.go
index f5e6956..e18b51d 100644
--- a/devices/cpu_cpu.go
+++ b/devices/cpu_cpu.go
@@ -1,10 +1,31 @@
package devices
import (
+ "fmt"
+ "time"
+
psCpu "github.com/shirou/gopsutil/cpu"
)
func init() {
- deviceCounts = append(deviceCounts, psCpu.Counts)
- devicePercents = append(devicePercents, psCpu.Percent)
+ f := func(cpus map[string]float64, iv time.Duration, l bool) map[string]error {
+ cpuCount, err := psCpu.Counts(l)
+ if err != nil {
+ return nil
+ }
+ formatString := "CPU%1d"
+ if cpuCount > 10 {
+ formatString = "CPU%02d"
+ }
+ vals, err := psCpu.Percent(iv, l)
+ if err != nil {
+ return map[string]error{"gopsutil": err}
+ }
+ for i := 0; i < len(vals); i++ {
+ key := fmt.Sprintf(formatString, i)
+ cpus[key] = vals[i]
+ }
+ return nil
+ }
+ RegisterCPU(f)
}
diff --git a/devices/include/smc.c b/devices/include/smc.c
new file mode 100644
index 0000000..0c200d6
--- /dev/null
+++ b/devices/include/smc.c
@@ -0,0 +1,700 @@
+/*
+ * Apple System Management Controller (SMC) API from user space for Intel based
+ * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver
+ * for the SMC.
+ *
+ * smc.c
+ * libsmc
+ *
+ * Copyright (C) 2014 beltex <https://github.com/beltex>
+ *
+ * Based off of fork from:
+ * osx-cpu-temp <https://github.com/lavoiesl/osx-cpu-temp>
+ *
+ * With credits to:
+ *
+ * Copyright (C) 2006 devnull
+ * Apple System Management Control (SMC) Tool
+ *
+ * Copyright (C) 2006 Hendrik Holtmann
+ * smcFanControl <https://github.com/hholtmann/smcFanControl>
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "smc.h"
+
+
+//------------------------------------------------------------------------------
+// MARK: MACROS
+//------------------------------------------------------------------------------
+
+
+/**
+Name of the SMC IOService as seen in the IORegistry. You can view it either via
+command line with ioreg or through the IORegistryExplorer app (found on Apple's
+developer site - Hardware IO Tools for Xcode)
+*/
+#define IOSERVICE_SMC "AppleSMC"
+
+
+/**
+IOService for getting machine model name
+*/
+#define IOSERVICE_MODEL "IOPlatformExpertDevice"
+
+
+/**
+SMC data types - 4 byte multi-character constants
+
+Sources: See TMP SMC keys in smc.h
+
+http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types
+*/
+#define DATA_TYPE_UINT8 "ui8 "
+#define DATA_TYPE_UINT16 "ui16"
+#define DATA_TYPE_UINT32 "ui32"
+#define DATA_TYPE_FLAG "flag"
+#define DATA_TYPE_FPE2 "fpe2"
+#define DATA_TYPE_SFDS "{fds"
+#define DATA_TYPE_SP78 "sp78"
+
+
+//------------------------------------------------------------------------------
+// MARK: GLOBAL VARS
+//------------------------------------------------------------------------------
+
+
+/**
+Our connection to the SMC
+*/
+static io_connect_t conn;
+
+
+/**
+Number of characters in an SMC key
+*/
+static const int SMC_KEY_SIZE = 4;
+
+
+/**
+Number of characters in a data type "key" returned from the SMC. See data type
+macros.
+*/
+static const int DATA_TYPE_SIZE = 4;
+
+
+//------------------------------------------------------------------------------
+// MARK: ENUMS
+//------------------------------------------------------------------------------
+
+
+/**
+Defined by AppleSMC.kext. See SMCParamStruct.
+
+These are SMC specific return codes
+*/
+typedef enum {
+ kSMCSuccess = 0,
+ kSMCError = 1,
+ kSMCKeyNotFound = 0x84
+} kSMC_t;
+
+
+/**
+Defined by AppleSMC.kext. See SMCParamStruct.
+
+Function selectors. Used to tell the SMC which function inside it to call.
+*/
+typedef enum {
+ kSMCUserClientOpen = 0,
+ kSMCUserClientClose = 1,
+ kSMCHandleYPCEvent = 2,
+ kSMCReadKey = 5,
+ kSMCWriteKey = 6,
+ kSMCGetKeyCount = 7,
+ kSMCGetKeyFromIndex = 8,
+ kSMCGetKeyInfo = 9
+} selector_t;
+
+
+//------------------------------------------------------------------------------
+// MARK: STRUCTS
+//------------------------------------------------------------------------------
+
+
+/**
+Defined by AppleSMC.kext. See SMCParamStruct.
+*/
+typedef struct {
+ unsigned char major;
+ unsigned char minor;
+ unsigned char build;
+ unsigned char reserved;
+ unsigned short release;
+} SMCVersion;
+
+
+/**
+Defined by AppleSMC.kext. See SMCParamStruct.
+*/
+typedef struct {
+ uint16_t version;
+ uint16_t length;
+ uint32_t cpuPLimit;
+ uint32_t gpuPLimit;
+ uint32_t memPLimit;
+} SMCPLimitData;
+
+
+/**
+Defined by AppleSMC.kext. See SMCParamStruct.
+
+- dataSize : How many values written to SMCParamStruct.bytes
+- dataType : Type of data written to SMCParamStruct.bytes. This lets us know how
+ to interpret it (translate it to human readable)
+*/
+typedef struct {
+ IOByteCount dataSize;
+ uint32_t dataType;
+ uint8_t dataColors;
+} SMCKeyInfoData;
+
+
+/**
+Defined by AppleSMC.kext.
+
+This is the predefined struct that must be passed to communicate with the
+AppleSMC driver. While the driver is closed source, the definition of this
+struct happened to appear in the Apple PowerManagement project at around
+version 211, and soon after disappeared. It can be seen in the PrivateLib.c
+file under pmconfigd.
+
+https://www.opensource.apple.com/source/PowerManagement/PowerManagement-211/
+*/
+typedef struct {
+ uint32_t key;
+ SMCVersion vers;
+ SMCPLimitData pLimitData;
+ SMCKeyInfoData keyInfo;
+ uint8_t result;
+ uint8_t status;
+ uint8_t data8;
+ uint32_t data32;
+ uint8_t bytes[32];
+} SMCParamStruct;
+
+
+/**
+Used for returning data from the SMC.
+*/
+typedef struct {
+ uint8_t data[32];
+ uint32_t dataType;
+ uint32_t dataSize;
+ kSMC_t kSMC;
+} smc_return_t;
+
+
+//------------------------------------------------------------------------------
+// MARK: HELPERS - TYPE CONVERSION
+//------------------------------------------------------------------------------
+
+
+/**
+Convert data from SMC of fpe2 type to human readable.
+
+:param: data Data from the SMC to be converted. Assumed data size of 2.
+:returns: Converted data
+*/
+static unsigned int from_fpe2(uint8_t data[32])
+{
+ unsigned int ans = 0;
+
+ // Data type for fan calls - fpe2
+ // This is assumend to mean floating point, with 2 exponent bits
+ // http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types
+ ans += data[0] << 6;
+ ans += data[1] << 2;
+
+ return ans;
+}
+
+
+/**
+Convert to fpe2 data type to be passed to SMC.
+
+:param: val Value to convert
+:param: data Pointer to data array to place result
+*/
+static void to_fpe2(unsigned int val, uint8_t *data)
+{
+ data[0] = val >> 6;
+ data[1] = (val << 2) ^ (data[0] << 8);
+}
+
+
+/**
+Convert SMC key to uint32_t. This must be done to pass it to the SMC.
+
+:param: key The SMC key to convert
+:returns: uint32_t translation.
+ Returns zero if key is not 4 characters in length.
+*/
+static uint32_t to_uint32_t(char *key)
+{
+ uint32_t ans = 0;
+ uint32_t shift = 24;
+
+ // SMC key is expected to be 4 bytes - thus 4 chars
+ if (strlen(key) != SMC_KEY_SIZE) {
+ return 0;
+ }
+
+ for (int i = 0; i < SMC_KEY_SIZE; i++) {
+ ans += key[i] << shift;
+ shift -= 8;
+ }
+
+ return ans;
+}
+
+
+/**
+For converting the dataType return from the SMC to human readable 4 byte
+multi-character constant.
+*/
+static void to_string(uint32_t val, char *dataType)
+{
+ int shift = 24;
+
+ for (int i = 0; i < DATA_TYPE_SIZE; i++) {
+ // To get each char, we shift it into the lower 8 bits, and then & by
+ // 255 to insolate it
+ dataType[i] = (val >> shift) & 0xff;
+ shift -= 8;
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// MARK: HELPERS - TMP CONVERSION
+//------------------------------------------------------------------------------
+
+
+/**
+Celsius to Fahrenheit
+*/
+static double to_fahrenheit(double tmp)
+{
+ // http://en.wikipedia.org/wiki/Fahrenheit#Definition_and_conversions
+ return (tmp * 1.8) + 32;
+}
+
+
+/**
+Celsius to Kelvin
+*/
+static double to_kelvin(double tmp)
+{
+ // http://en.wikipedia.org/wiki/Kelvin
+ return tmp + 273.15;
+}
+
+
+//------------------------------------------------------------------------------
+// MARK: "PRIVATE" FUNCTIONS
+//------------------------------------------------------------------------------
+
+
+/**
+Make a call to the SMC
+
+:param: inputStruct Struct that holds data telling the SMC what you want
+:param: outputStruct Struct holding the SMC's response
+:returns: I/O Kit return code
+*/
+static kern_return_t call_smc(SMCParamStruct *inputStruct,
+ SMCParamStruct *outputStruct)
+{
+ kern_return_t result;
+ size_t inputStructCnt = sizeof(SMCParamStruct);
+ size_t outputStructCnt = sizeof(SMCParamStruct);
+
+ result = IOConnectCallStructMethod(conn, kSMCHandleYPCEvent,
+ inputStruct,
+ inputStructCnt,
+ outputStruct,
+ &outputStructCnt);
+
+ if (result != kIOReturnSuccess) {
+ // IOReturn error code lookup. See "Accessing Hardware From Applications
+ // -> Handling Errors" Apple doc
+ result = err_get_code(result);
+ }
+
+ return result;
+}
+
+
+/**
+Read data from the SMC
+
+:param: key The SMC key
+*/
+static kern_return_t read_smc(char *key, smc_return_t *result_smc)
+{
+ kern_return_t result;
+ SMCParamStruct inputStruct;
+ SMCParamStruct outputStruct;
+
+ memset(&inputStruct, 0, sizeof(SMCParamStruct));
+ memset(&outputStruct, 0, sizeof(SMCParamStruct));
+ memset(result_smc, 0, sizeof(smc_return_t));
+
+ // First call to AppleSMC - get key info
+ inputStruct.key = to_uint32_t(key);
+ inputStruct.data8 = kSMCGetKeyInfo;
+
+ result = call_smc(&inputStruct, &outputStruct);
+ result_smc->kSMC = outputStruct.result;
+
+ if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) {
+ return result;
+ }
+
+ // Store data for return
+ result_smc->dataSize = outputStruct.keyInfo.dataSize;
+ result_smc->dataType = outputStruct.keyInfo.dataType;
+
+
+ // Second call to AppleSMC - now we can get the data
+ inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize;
+ inputStruct.data8 = kSMCReadKey;
+
+ result = call_smc(&inputStruct, &outputStruct);
+ result_smc->kSMC = outputStruct.result;
+
+ if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) {
+ return result;
+ }
+
+ memcpy(result_smc->data, outputStruct.bytes, sizeof(outputStruct.bytes));
+
+ return result;
+}
+
+
+/**
+Write data to the SMC.
+
+:returns: IOReturn IOKit return code
+*/
+static kern_return_t write_smc(char *key, smc_return_t *result_smc)
+{
+ kern_return_t result;
+ SMCParamStruct inputStruct;
+ SMCParamStruct outputStruct;
+
+ memset(&inputStruct, 0, sizeof(SMCParamStruct));
+ memset(&outputStruct, 0, sizeof(SMCParamStruct));
+
+ // First call to AppleSMC - get key info
+ inputStruct.key = to_uint32_t(key);
+ inputStruct.data8 = kSMCGetKeyInfo;
+
+ result = call_smc(&inputStruct, &outputStruct);
+ result_smc->kSMC = outputStruct.result;
+
+ if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) {
+ return result;
+ }
+
+ // Check data is correct
+ if (result_smc->dataSize != outputStruct.keyInfo.dataSize ||
+ result_smc->dataType != outputStruct.keyInfo.dataType) {
+ return kIOReturnBadArgument;
+ }
+
+ // Second call to AppleSMC - now we can write the data
+ inputStruct.data8 = kSMCWriteKey;
+ inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize;
+
+ // Set data to write
+ memcpy(inputStruct.bytes, result_smc->data, sizeof(result_smc->data));
+
+ result = call_smc(&inputStruct, &outputStruct);
+ result_smc->kSMC = outputStruct.result;
+
+ return result;
+}
+
+
+/**
+Get the model name of the machine.
+*/
+static kern_return_t get_machine_model(io_name_t model)
+{
+ io_service_t service;
+ kern_return_t result;
+
+ service = IOServiceGetMatchingService(kIOMasterPortDefault,
+ IOServiceMatching(IOSERVICE_MODEL));
+
+ if (service == 0) {
+ printf("ERROR: %s NOT FOUND\n", IOSERVICE_MODEL);
+ return kIOReturnError;
+ }
+
+ // Get the model name
+ result = IORegistryEntryGetName(service, model);
+ IOObjectRelease(service);
+
+ return result;
+}
+
+
+//------------------------------------------------------------------------------
+// MARK: "PUBLIC" FUNCTIONS
+//------------------------------------------------------------------------------
+
+
+kern_return_t open_smc(void)
+{
+ kern_return_t result;
+ io_service_t service;
+
+ service = IOServiceGetMatchingService(kIOMasterPortDefault,
+ IOServiceMatching(IOSERVICE_SMC));
+
+ if (service == 0) {
+ // NOTE: IOServiceMatching documents 0 on failure
+ printf("ERROR: %s NOT FOUND\n", IOSERVICE_SMC);
+ return kIOReturnError;
+ }
+
+ result = IOServiceOpen(service, mach_task_self(), 0, &conn);
+ IOObjectRelease(service);
+
+ return result;
+}
+
+
+kern_return_t close_smc(void)
+{
+ return IOServiceClose(conn);
+}
+
+
+bool is_key_valid(char *key)
+{
+ bool ans = false;
+ kern_return_t result;
+ smc_return_t result_smc;
+
+ if (strlen(key) != SMC_KEY_SIZE) {
+ printf("ERROR: Invalid key size - must be 4 chars\n");
+ return ans;
+ }
+
+ // Try a read and see if it succeeds
+ result = read_smc(key, &result_smc);
+
+ if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) {
+ ans = true;
+ }
+
+ return ans;
+}
+
+
+double get_tmp(char *key, tmp_unit_t unit)
+{
+ kern_return_t result;
+ smc_return_t result_smc;
+
+ result = read_smc(key, &result_smc);
+
+ if (!(result == kIOReturnSuccess &&
+ result_smc.dataSize == 2 &&
+ result_smc.dataType == to_uint32_t(DATA_TYPE_SP78))) {
+ // Error
+ return 0.0;
+ }
+
+ // TODO: Create from_sp78() convert function
+ double tmp = result_smc.data[0];
+
+ switch (unit) {
+ case CELSIUS:
+ break;
+ case FAHRENHEIT:
+ tmp = to_fahrenheit(tmp);
+ break;
+ case KELVIN:
+ tmp = to_kelvin(tmp);
+ break;
+ }
+
+ return tmp;
+}
+
+
+bool is_battery_powered(void)
+{
+ kern_return_t result;
+ smc_return_t result_smc;
+
+ result = read_smc(BATT_PWR, &result_smc);
+
+ if (!(result == kIOReturnSuccess &&
+ result_smc.dataSize == 1 &&
+ result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) {
+ // Error
+ return false;
+ }
+
+ return result_smc.data[0];
+}
+
+
+bool is_optical_disk_drive_full(void)
+{
+ kern_return_t result;
+ smc_return_t result_smc;
+
+ result = read_smc(ODD_FULL, &result_smc);
+
+ if (!(result == kIOReturnSuccess &&
+ result_smc.dataSize == 1 &&
+ result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) {
+ // Error
+ return false;
+ }
+
+ return result_smc.data[0];
+}
+
+
+//------------------------------------------------------------------------------
+// MARK: FAN FUNCTIONS
+//------------------------------------------------------------------------------
+
+
+bool get_fan_name(unsigned int fan_num, fan_name_t name)
+{
+ char key[5];
+ kern_return_t result;
+ smc_return_t result_smc;
+
+ sprintf(key, "F%dID", fan_num);
+ result = read_smc(key, &result_smc);
+
+ if (!(result == kIOReturnSuccess &&
+ result_smc.dataSize == 16 &&
+ result_smc.dataType == to_uint32_t(DATA_TYPE_SFDS))) {
+ return false;
+ }
+
+
+ /*
+ We know the data size is 16 bytes and the type is "{fds", a custom
+ struct defined by the AppleSMC.kext. See TMP enum sources for the
+ struct.
+
+ The last 12 bytes contain the name of the fan, an array of chars, hence
+ the loop range.
+ */
+ int index = 0;
+ for (int i = 4; i < 16; i++) {
+ // Check if at the end (name may not be full 12 bytes)
+ // Could check for 0 (null), but instead we check for 32 (space). This
+ // is a hack to remove whitespace. :)
+ if (result_smc.data[i] == 32) {
+ break;
+ }
+
+ name[index] = result_smc.data[i];
+ index++;
+ }
+
+ return true;
+}
+
+
+int get_num_fans(void)
+{
+ kern_return_t result;
+ smc_return_t result_smc;
+
+ result = read_smc(NUM_FANS, &result_smc);
+
+ if (!(result == kIOReturnSuccess &&
+ result_smc.dataSize == 1 &&
+ result_smc.dataType == to_uint32_t(DATA_TYPE_UINT8))) {
+ // Error
+ return -1;
+ }
+
+ return result_smc.data[0];
+}
+
+
+unsigned int get_fan_rpm(unsigned int fan_num)
+{
+ char key[5];
+ kern_return_t result;
+ smc_return_t result_smc;
+
+ sprintf(key, "F%dAc", fan_num);
+ result = read_smc(key, &result_smc);
+
+ if (!(result == kIOReturnSuccess &&
+ result_smc.dataSize == 2 &&
+ result_smc.dataType == to_uint32_t(DATA_TYPE_FPE2))) {
+ // Error
+ return 0;
+ }
+
+ return from_fpe2(result_smc.data);
+}
+
+
+bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth)
+{
+ // TODO: Add rpm val safety check
+ char key[5];
+ bool ans = false;
+ kern_return_t result;
+ smc_return_t result_smc;
+
+ memset(&result_smc, 0, sizeof(smc_return_t));
+
+ // TODO: Don't use magic number
+ result_smc.dataSize = 2;
+ result_smc.dataType = to_uint32_t(DATA_TYPE_FPE2);
+ to_fpe2(rpm, result_smc.data);
+
+ sprintf(key, "F%dMn", fan_num);
+ result = write_smc(key, &result_smc);
+
+ if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) {
+ ans = true;
+ }
+
+ return ans;
+}
diff --git a/devices/include/smc.h b/devices/include/smc.h
new file mode 100644
index 0000000..d837e11
--- /dev/null
+++ b/devices/include/smc.h
@@ -0,0 +1,254 @@
+/*
+ * Apple System Management Controller (SMC) API from user space for Intel based
+ * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver
+ * for the SMC.
+ *
+ * smc.h
+ * libsmc
+ *
+ * Copyright (C) 2014 beltex <https://github.com/beltex>
+ *
+ * Based off of fork from:
+ * osx-cpu-temp <https://github.com/lavoiesl/osx-cpu-temp>
+ *
+ * With credits to:
+ *
+ * Copyright (C) 2006 devnull
+ * Apple System Management Control (SMC) Tool
+ *
+ * Copyright (C) 2006 Hendrik Holtmann
+ * smcFanControl <https://github.com/hholtmann/smcFanControl>
+ *
+ * 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.
+ */
+
+#include <IOKit/IOKitLib.h>
+
+
+//------------------------------------------------------------------------------
+// MARK: MACROS
+//------------------------------------------------------------------------------
+
+
+/**
+SMC keys for temperature sensors - 4 byte multi-character constants
+
+Not applicable to all Mac's of course. In adition, the definition of the codes
+may not be 100% accurate necessarily. Finally, list is incomplete.
+
+Presumed letter translations:
+
+- T = Temperature (if first char)
+- C = CPU
+- G = GPU
+- P = Proximity
+- D = Diode
+- H = Heatsink
+
+Sources:
+
+- https://www.apple.com/downloads/dashboard/status/istatpro.html
+- https://github.com/hholtmann/smcFanControl
+- https://github.com/jedda/OSX-Monitoring-Tools
+- http://www.parhelia.ch/blog/statics/k3_keys.html
+*/
+#define AMBIENT_AIR_0 "TA0P"
+#define AMBIENT_AIR_1 "TA1P"
+#define CPU_0_DIODE "TC0D"
+#define CPU_0_HEATSINK "TC0H"
+#define CPU_0_PROXIMITY "TC0P"
+#define ENCLOSURE_BASE_0 "TB0T"
+#define ENCLOSURE_BASE_1 "TB1T"
+#define ENCLOSURE_BASE_2 "TB2T"
+#define ENCLOSURE_BASE_3 "TB3T"
+#define GPU_0_DIODE "TG0D"
+#define GPU_0_HEATSINK "TG0H"
+#define GPU_0_PROXIMITY "TG0P"
+#define HARD_DRIVE_BAY "TH0P"
+#define MEMORY_SLOT_0 "TM0S"
+#define MEMORY_SLOTS_PROXIMITY "TM0P"
+#define NORTHBRIDGE "TN0H"
+#define NORTHBRIDGE_DIODE "TN0D"
+#define NORTHBRIDGE_PROXIMITY "TN0P"
+#define THUNDERBOLT_0 "TI0P"
+#define THUNDERBOLT_1 "TI1P"
+#define WIRELESS_MODULE "TW0P"
+
+
+/**
+SMC keys for fans - 4 byte multi-character constants
+
+Number of fans on Macs vary of course, thus not all keys will be applicable.
+
+Presumed letter translations:
+
+- F = Fan
+- Ac = Acutal
+- Mn = Min
+- Mx = Max
+- Sf = Safe
+- Tg = Target
+
+Sources: See TMP SMC keys
+*/
+#define FAN_0 "F0Ac"
+#define FAN_0_MIN_RPM "F0Mn"
+#define FAN_0_MAX_RPM "F0Mx"
+#define FAN_0_SAFE_RPM "F0Sf"
+#define FAN_0_TARGET_RPM "F0Tg"
+#define FAN_1 "F1Ac"
+#define FAN_1_MIN_RPM "F1Mn"
+#define FAN_1_MAX_RPM "F1Mx"
+#define FAN_1_SAFE_RPM "F1Sf"
+#define FAN_1_TARGET_RPM "F1Tg"
+#define FAN_2 "F2Ac"
+#define FAN_2_MIN_RPM "F2Mn"
+#define FAN_2_MAX_RPM "F2Mx"
+#define FAN_2_SAFE_RPM "F2Sf"
+#define FAN_2_TARGET_RPM "F2Tg"
+#define NUM_FANS "FNum"
+#define FORCE_BITS "FS! "
+
+
+/**
+Misc SMC keys - 4 byte multi-character constants
+
+Sources: See TMP SMC keys
+*/
+#define BATT_PWR "BATP"
+#define NUM_KEYS "#KEY"
+#define ODD_FULL "MSDI"
+
+
+//------------------------------------------------------------------------------
+// MARK: TYPES
+//------------------------------------------------------------------------------
+
+
+typedef char fan_name_t[13];
+
+
+//------------------------------------------------------------------------------
+// MARK: ENUMS
+//------------------------------------------------------------------------------
+
+
+typedef enum {
+ CELSIUS,
+ FAHRENHEIT,
+ KELVIN
+} tmp_unit_t;
+
+
+//------------------------------------------------------------------------------
+// MARK: PROTOTYPES
+//------------------------------------------------------------------------------
+
+
+/**
+Open a connection to the SMC
+
+:returns: kIOReturnSuccess on successful connection to the SMC.
+*/
+kern_return_t open_smc(void);
+
+
+/**
+Close connection to the SMC
+
+:returns: kIOReturnSuccess on successful close of connection to the SMC.
+*/
+kern_return_t close_smc(void);
+
+
+/**
+Check if an SMC key is valid. Useful for determining if a certain machine has
+particular sensor or fan for example.
+
+:param: key The SMC key to check. 4 byte multi-character constant. Must be 4
+ characters in length.
+:returns: True if the key is found, false otherwise
+*/
+bool is_key_valid(char *key);
+
+
+/**
+Get the current temperature from a sensor
+
+:param: key The temperature sensor to read from
+:param: unit The unit for the temperature value.
+:returns: Temperature of sensor. If the sensor is not found, or an error
+ occurs, return will be zero
+*/
+double get_tmp(char *key, tmp_unit_t unit);
+
+
+/**
+Is the machine being powered by the battery?
+
+:returns: True if it is, false otherwise
+*/
+bool is_battery_powered(void);
+
+
+/**
+Is there a CD in the optical disk drive (ODD)?
+
+:returns: True if there is, false otherwise
+*/
+bool is_optical_disk_drive_full(void);
+
+
+/**
+Get the name of a fan.
+
+:param: fanNum The number of the fan to check
+:param: name The name of the fan. Return will be empty on error.
+:returns: True if successful, false otherwise.
+*/
+bool get_fan_name(unsigned int fan_num, fan_name_t name);
+
+
+/**
+Get the number of fans on this machine.
+
+:returns: The number of fans. If an error occurs, return will be -1.
+*/
+int get_num_fans(void);
+
+
+/**
+Get the current speed (RPM - revolutions per minute) of a fan.
+
+:param: fan_num The number of the fan to check
+:returns: The fan RPM. If the fan is not found, or an error occurs, return
+ will be zero
+*/
+UInt get_fan_rpm(UInt fan_num);
+
+
+/**
+Set the minimum speed (RPM - revolutions per minute) of a fan. This method
+requires root privileges. By minimum we mean that OS X can interject and
+raise the fan speed if needed, however it will not go below this.
+
+WARNING: You are playing with hardware here, BE CAREFUL.
+
+:param: fan_num The number of the fan to set
+:param: rpm The speed you would like to set the fan to.
+:param: auth Should the function do authentication?
+:return: True if successful, false otherwise
+*/
+bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth);
diff --git a/devices/mem.g