// smcdump.c: dump a bunch of smc sensor data on MAC mini.
// Used to dev the smcstats.c program.
// 20260511 ground/X

#include <IOKit/IOKitLib.h>
#include <stdio.h>
#include <string.h>

typedef struct { char major, minor, build, reserved[1]; UInt16 release; } SMCVersion;
typedef struct { UInt16 version, length; UInt32 cpuPLimit, gpuPLimit, memPLimit; } SMCPLimitData;
typedef struct { UInt32 dataSize, dataType; UInt8 dataAttributes; } SMCKeyInfoData;

typedef struct {
    UInt32            key;
    SMCVersion        vers;
    SMCPLimitData     pLimitData;
    SMCKeyInfoData    keyInfo;
    UInt8             result;
    UInt8             status;
    UInt8             data8;
    UInt32            data32;
    UInt8             bytes[32];
} SMCParamStruct;

enum {
    kSMCHandleYPCEvent   = 2,
    kSMCReadKey          = 5,
    kSMCGetKeyInfo       = 9,
    kSMCGetKeyFromIndex  = 8,
};

static UInt32 s2u(const char *s) {
    UInt32 v = 0;
    for (int i = 0; i < 4; i++) v = (v << 8) | (unsigned char)s[i];
    return v;
}

static void u2s(UInt32 k, char *out) {
    out[0] = (k >> 24) & 0xff;
    out[1] = (k >> 16) & 0xff;
    out[2] = (k >>  8) & 0xff;
    out[3] =  k        & 0xff;
    out[4] = 0;
}

// The working two-step read (same pattern as smcscan.c / getfan.c)
static int twoStepRead(io_connect_t conn, const char *key,
                       UInt8 *bytes, UInt32 *size, char *type) {
    SMCParamStruct p = {0};
    size_t z = sizeof(p);

    // 1. GetKeyInfo
    p.data8 = kSMCGetKeyInfo;
    p.key   = s2u(key);
    if (IOConnectCallStructMethod(conn, kSMCHandleYPCEvent,
                                   &p, sizeof(p), &p, &z) || p.result)
        return -1;

    UInt32 sz = p.keyInfo.dataSize;
    UInt32 tp = p.keyInfo.dataType;
    if (sz > 32) sz = 32;

    type[0] = (tp >> 24) & 0xff;
    type[1] = (tp >> 16) & 0xff;
    type[2] = (tp >>  8) & 0xff;
    type[3] =  tp        & 0xff;
    type[4] = 0;

    SMCKeyInfoData ki = p.keyInfo;

    // 2. ReadKey with saved keyInfo
    memset(&p, 0, sizeof(p));
    z = sizeof(p);
    p.data8   = kSMCReadKey;
    p.key     = s2u(key);
    p.keyInfo = ki;
    if (IOConnectCallStructMethod(conn, kSMCHandleYPCEvent,
                                   &p, sizeof(p), &p, &z) || p.result)
        return -2;

    memcpy(bytes, p.bytes, sz);
    *size = sz;
    return 0;
}

// Decode common SMC types into a double
static double decode(UInt8 *b, UInt32 sz, const char *t) {
    if (!strcmp(t, "flt ") && sz == 4) { float f; memcpy(&f, b, 4); return f; }
    if (!strcmp(t, "ui8 ") && sz == 1) return b[0];
    if (!strcmp(t, "ui16") && sz == 2) return (b[0] << 8) | b[1];
    if (!strcmp(t, "ui32") && sz == 4) return ((UInt32)b[0]<<24)|((UInt32)b[1]<<16)|((UInt32)b[2]<<8)|b[3];
    if (!strcmp(t, "si8 ") && sz == 1) return (signed char)b[0];
    if (!strcmp(t, "si16") && sz == 2) return (SInt16)((b[0] << 8) | b[1]);
    if (!strcmp(t, "sp78") && sz == 2) { SInt16 v = (b[0] << 8) | b[1]; return v / 256.0; }
    if (!strcmp(t, "fp2e") && sz == 2) { SInt16 v = (b[0] << 8) | b[1]; return v / 16384.0; }
    if (!strcmp(t, "fpe2") && sz == 2) { SInt16 v = (b[0] << 8) | b[1]; return v / 16384.0; }
    return 1e100;   // sentinel
}

int main(void) {
    io_service_t srv = IOServiceGetMatchingService(kIOMainPortDefault,
                                                   IOServiceMatching("AppleSMC"));
    if (!srv) { fprintf(stderr, "AppleSMC not found\n"); return 1; }
    io_connect_t conn;
    if (IOServiceOpen(srv, mach_task_self(), 0, &conn)) {
        fprintf(stderr, "SMC open failed\n"); return 1;
    }
    IOObjectRelease(srv);

    printf("%-5s %-5s %-6s %-15s %s\n", "Key", "Type", "Size", "Value", "Decoded (float)");
    printf("----- ----- ------ --------------- ------------------------\n");

    int count = 0;
    for (UInt32 idx = 0; ; idx++) {
        SMCParamStruct p = {0};
        size_t z = sizeof(p);
        p.data8  = kSMCGetKeyFromIndex;
        p.data32 = idx;

        if (IOConnectCallStructMethod(conn, kSMCHandleYPCEvent,
                                       &p, sizeof(p), &p, &z) || p.result) {
            break;              // no more keys
        }

        char name[5];
        u2s(p.key, name);

        UInt8 bytes[32] = {0};
        UInt32 sz = 0;
        char type[5] = {0};

        if (twoStepRead(conn, name, bytes, &sz, type) != 0) {
            printf("%-5s (read error)\n", name);
            continue;
        }

        double val = decode(bytes, sz, type);
        printf("%-5s %-5s %-6u ", name, type, sz);
        for (UInt32 j = 0; j < sz && j < 8; j++) printf("%02x", bytes[j]);
        for (UInt32 j = sz; j < 8; j++) printf("  ");
        if (val < 1e99) printf(" %15.3f", val);
        else            printf(" %15s", "(unknown)");
        printf("\n");

        count++;
    }

    printf("\nSuccessfully read %d keys.\n", count);
    IOServiceClose(conn);
    return 0;
}

