|
/*- |
|
* Copyright (c) 2009 Andriy Gapon <[email protected]> |
|
* Copyright (c) 2009 Gabriel Craciunescu <[email protected]> |
|
* ( Added some Linux support ) |
|
* Copyright (c) 2011 KINTA-JAPAN <[email protected]> |
|
* ( Added Intel MEI Driver support ) |
|
* Copyright (c) 2014 KINTA-JAPAN <[email protected]> |
|
* ( Added Kernel 3.17 support ) |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
* SUCH DAMAGE. |
|
*/ |
|
|
|
/* HECI : gcc -o heci-qst heci-qst.c */ |
|
/* MEI : gcc -D MEI -o heci-qst heci-qst.c */ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <fcntl.h> |
|
#include <unistd.h> |
|
#include <linux/ioctl.h> |
|
#include <inttypes.h> |
|
#ifdef debug |
|
#include <time.h> |
|
#endif |
|
|
|
#ifdef MEI |
|
#define DEV_NAME "/dev/mei" |
|
#else |
|
#define DEV_NAME "/dev/heci" |
|
#endif |
|
|
|
/* heci.h in linux is broken ... what a surprise :P */ |
|
|
|
struct guid |
|
{ |
|
uint32_t data1; |
|
uint16_t data2; |
|
uint16_t data3; |
|
uint8_t data4[8]; |
|
}; |
|
|
|
struct heci_message_data |
|
{ |
|
#ifdef MEI |
|
union { |
|
struct guid guid; |
|
struct { |
|
uint32_t size; |
|
char protocol_version; |
|
char reserved[3]; |
|
} md; |
|
} msg_data; |
|
#else |
|
uint32_t size; |
|
char *data; |
|
#endif |
|
} __attribute__((packed)); |
|
|
|
#undef IOCTL_HECI_CONNECT_CLIENT |
|
#define IOCTL_HECI_CONNECT_CLIENT _IOWR(0x48, 0x01, struct heci_message_data) |
|
|
|
struct therm_sensor |
|
{ |
|
int8_t valid; |
|
int32_t value; |
|
} __attribute__((packed)); |
|
|
|
struct volt_sensor |
|
{ |
|
int8_t valid; |
|
int32_t value; |
|
} __attribute__((packed)); |
|
|
|
struct fan_sensor |
|
{ |
|
int8_t valid; |
|
int16_t value; |
|
} __attribute__((packed)); |
|
|
|
#define THERM_SENSOR_COUNT 12 |
|
struct therm_data |
|
{ |
|
int8_t status; |
|
struct therm_sensor data[THERM_SENSOR_COUNT]; |
|
} __attribute__((packed)); |
|
|
|
#define VOLT_SENSOR_COUNT 8 |
|
struct volt_data |
|
{ |
|
int8_t status; |
|
struct volt_sensor data[VOLT_SENSOR_COUNT]; |
|
} __attribute__((packed)); |
|
|
|
#define FAN_SENSOR_COUNT 8 |
|
struct fan_data |
|
{ |
|
int8_t status; |
|
struct fan_sensor data[FAN_SENSOR_COUNT]; |
|
} __attribute__((packed)); |
|
|
|
struct qst_cmd |
|
{ |
|
uint8_t cmd; |
|
uint16_t in_len; |
|
uint16_t out_len; |
|
} __attribute__((packed)); |
|
|
|
|
|
#define QST_THERMAL_CMD 0x12 |
|
static const struct qst_cmd therm_cmd = { QST_THERMAL_CMD, 0, sizeof(struct therm_data) }; |
|
|
|
#define QST_VOLT_CMD 0x58 |
|
static const struct qst_cmd volt_cmd = { QST_VOLT_CMD, 0, sizeof(struct volt_data) }; |
|
|
|
#define QST_FAN_CMD 0x37 |
|
static const struct qst_cmd fan_cmd = { QST_FAN_CMD, 0, sizeof(struct fan_data) }; |
|
|
|
#define DEBUG_LOG_PATH "/tmp/heci-qst.log" |
|
/* Hmmmm seems the output is different depeinding on chipset / ICH version */ |
|
/* On my DQ45CB ( ICH10DO / Q45 ) mobo this is right |
|
also is the same output Intel Desktop Utils is giving me */ |
|
|
|
const char * const temp_names[THERM_SENSOR_COUNT] = { |
|
"CPU_Temp", |
|
NULL, |
|
NULL, |
|
"MB_Temp", |
|
"ICH_Temp", |
|
"MCH_Temp", |
|
NULL, |
|
NULL, |
|
NULL, |
|
NULL, |
|
NULL, |
|
NULL, |
|
}; |
|
|
|
const int const temp_margin[THERM_SENSOR_COUNT] = { |
|
10000, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
}; |
|
|
|
const char * const volt_names[VOLT_SENSOR_COUNT] = { |
|
"+12_Volts", |
|
"+5_Volts", |
|
"+3.3_Volts", |
|
"MCH_Vccp", |
|
"CPU1_Vccp", |
|
NULL, |
|
NULL, |
|
NULL, |
|
}; |
|
|
|
const char * const fan_names[FAN_SENSOR_COUNT] = { |
|
"CPU_Fan", |
|
"INL_Fan", |
|
"OUTL_Fan", |
|
"AUX_Fan", |
|
NULL, |
|
NULL, |
|
NULL, |
|
NULL, |
|
}; |
|
|
|
static const struct guid _hwm_guid = { |
|
0x6B5205B9, |
|
0x8185, |
|
0x4519, |
|
{0xB8, 0x89, 0xD9, 0x87, 0x24, 0xB5, 0x86, 0x07} |
|
}; |
|
|
|
int main(int argc, char *argv[]) |
|
{ |
|
int loopCount=1, waitTime=1; |
|
int fd; |
|
int rc; |
|
int i,j; |
|
char dev_name[sizeof(DEV_NAME)+1]; |
|
|
|
if (argc==3){ |
|
sscanf(argv[1], "%d", &loopCount); |
|
sscanf(argv[2], "%d", &waitTime); |
|
} |
|
|
|
#ifdef debug |
|
FILE *fp=fopen(DEBUG_LOG_PATH, "a+"); |
|
if (fp==NULL){ |
|
perror(DEBUG_LOG_PATH); |
|
return 1; |
|
} |
|
#endif |
|
|
|
#ifdef MEI |
|
printf("**heci-qst(Intel MEI Driver Version)**\n\n"); |
|
#else |
|
printf("**heci-qst(Intel HECI Driver Version)**\n\n"); |
|
#endif |
|
|
|
struct heci_message_data hwm_guid; |
|
|
|
for (i=-1; i<10; i++){ |
|
if (i<0) |
|
sprintf(dev_name, "%s", DEV_NAME); |
|
else |
|
sprintf(dev_name, "%s%d", DEV_NAME, i); |
|
|
|
fprintf(stderr, "Try device name [%s]\n", dev_name); |
|
|
|
fd = open(dev_name, O_RDWR); |
|
if (fd >=0) |
|
break; |
|
} |
|
|
|
if (fd < 0) { |
|
fprintf(stderr, "Please load the Kernel module first!\n"); |
|
return 1; |
|
} |
|
|
|
#ifdef MEI |
|
hwm_guid.msg_data.guid = _hwm_guid; |
|
#else |
|
hwm_guid.size = sizeof(struct guid); |
|
hwm_guid.data = (char *)malloc(hwm_guid.size); |
|
|
|
if(!hwm_guid.data) { |
|
fprintf(stderr, "malloc failure.\n"); |
|
return 1; |
|
} |
|
memcpy(hwm_guid.data, &_hwm_guid, sizeof(_hwm_guid)); |
|
#endif |
|
|
|
rc = ioctl(fd, IOCTL_HECI_CONNECT_CLIENT, &hwm_guid); |
|
|
|
if (rc < 0) { |
|
perror("ioctl IOCTL_HECI_CONNECT_CLIENT"); |
|
return 1; |
|
} |
|
|
|
int32_t arvTherm[THERM_SENSOR_COUNT]; |
|
memset(arvTherm, '\0', sizeof(arvTherm)); |
|
|
|
int32_t arvFan[FAN_SENSOR_COUNT]; |
|
memset(arvFan, '\0', sizeof(arvFan)); |
|
|
|
int32_t arvVolt[VOLT_SENSOR_COUNT]; |
|
memset(arvVolt, '\0', sizeof(arvVolt)); |
|
|
|
struct therm_data therm_data; |
|
struct fan_data fan_data; |
|
struct volt_data volt_data; |
|
|
|
|
|
for (i = 0; i < loopCount ; i++){ |
|
#ifdef debug |
|
fprintf(fp, "--------------------------------------------\n"); |
|
time_t timer = time(NULL); |
|
struct tm *date = localtime(&timer); |
|
fprintf(fp, "%s", asctime(date)); |
|
fprintf(fp, "<Count = %d>\n", i+1); |
|
fprintf(fp, "--------------------------------------------\n"); |
|
#endif |
|
/* Request temp. */ |
|
rc = write(fd, &therm_cmd, sizeof(therm_cmd)); |
|
if (rc < 0) { |
|
perror("therm write"); |
|
return 1; |
|
} |
|
|
|
rc = read(fd, &therm_data, sizeof(therm_data)); |
|
if (rc < 0) { |
|
perror("therm read"); |
|
return 1; |
|
} |
|
|
|
for (j = 0; j < THERM_SENSOR_COUNT; j++) { |
|
if (therm_data.data[j].valid) { |
|
int32_t value = therm_data.data[j].value; |
|
#ifdef debug |
|
if (temp_names[j]){ |
|
fprintf(fp,"%d %s ", i+1, temp_names[j]); |
|
}else{ |
|
fprintf(fp,"%d %02d ", i+1, j); |
|
} |
|
#endif |
|
arvTherm[j]+=abs(value); |
|
#ifdef debug |
|
fprintf(fp,"%d.%02d\n", abs(value) / 100, abs(value) % 100); |
|
#endif |
|
} |
|
} |
|
|
|
/* Request fans */ |
|
rc = write(fd, &fan_cmd, sizeof(fan_cmd)); |
|
if (rc < 0) { |
|
perror("fan write"); |
|
return 1; |
|
} |
|
|
|
rc = read(fd, &fan_data, sizeof(fan_data)); |
|
if (rc < 0) { |
|
perror("fan read"); |
|
return 1; |
|
} |
|
for (j = 0; j < FAN_SENSOR_COUNT; j++) { |
|
if (fan_data.data[j].valid) { |
|
int32_t value = fan_data.data[j].value; |
|
|
|
#ifdef debug |
|
if (fan_names[j]){ |
|
fprintf(fp,"%d %s ", i+1, fan_names[j]); |
|
}else{ |
|
fprintf(fp,"%d %02d ", i+1, j); |
|
} |
|
#endif |
|
arvFan[j]+=abs(value); |
|
#ifdef debug |
|
fprintf(fp, "%d RPM\n", value); |
|
#endif |
|
} |
|
} |
|
|
|
/* Request volts. */ |
|
rc = write(fd, &volt_cmd, sizeof(volt_cmd)); |
|
if (rc < 0) { |
|
perror("volts write"); |
|
return 1; |
|
} |
|
|
|
rc = read(fd, &volt_data, sizeof(volt_data)); |
|
if (rc < 0) { |
|
perror("volts read"); |
|
return 1; |
|
} |
|
for (j = 0; j < VOLT_SENSOR_COUNT; j++) { |
|
if (volt_data.data[j].valid) { |
|
int32_t value = volt_data.data[j].value; |
|
|
|
#ifdef debug |
|
if (volt_names[j]){ |
|
fprintf(fp,"%d %s ", i+1, volt_names[j]); |
|
}else{ |
|
fprintf(fp,"%d %02d ", i+1, j); |
|
} |
|
#endif |
|
arvVolt[j]+=abs(value); |
|
#ifdef debug |
|
fprintf(fp,"%d.%03d V\n", abs(value) / 1000, abs(value) % 1000); |
|
#endif |
|
} |
|
} |
|
|
|
if(i != loopCount-1){ |
|
for(j = 0; j < waitTime; j++){ |
|
sleep(1); |
|
} |
|
} |
|
} |
|
close(fd); |
|
|
|
for(i=0; i<THERM_SENSOR_COUNT; i++) { |
|
if (therm_data.data[i].valid) { |
|
if (temp_names[i]){ |
|
printf("%s ", temp_names[i]); |
|
}else{ |
|
printf("%02d ", i); |
|
} |
|
int32_t value = arvTherm[i]/loopCount; |
|
if(temp_margin[i] != 0){ |
|
value = temp_margin[i] - value; |
|
} |
|
printf("%d.%02d\n", value / 100, value % 100); |
|
} |
|
} |
|
for(i=0; i<FAN_SENSOR_COUNT; i++) { |
|
if (fan_data.data[i].valid) { |
|
if (fan_names[i]){ |
|
printf("%s ", fan_names[i]); |
|
}else{ |
|
printf("%02d ", i); |
|
} |
|
int32_t value = arvFan[i]/loopCount; |
|
printf("%d RPM\n", value); |
|
} |
|
} |
|
for(i=0; i<VOLT_SENSOR_COUNT; i++) { |
|
if (volt_data.data[i].valid) { |
|
if (volt_names[i]){ |
|
printf("%s ", volt_names[i]); |
|
}else{ |
|
printf("%02d ", i); |
|
} |
|
int32_t value = arvVolt[i]/loopCount; |
|
printf("%d.%03d V\n", value / 1000, value % 1000); |
|
} |
|
} |
|
|
|
#ifdef debug |
|
fclose(fp); |
|
#endif |
|
|
|
return 0; |
|
} |