Skip to content

Instantly share code, notes, and snippets.

@simnalamburt
Last active November 27, 2022 08:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simnalamburt/0a1e233fe967a48b66fba89bf480d036 to your computer and use it in GitHub Desktop.
Save simnalamburt/0a1e233fe967a48b66fba89bf480d036 to your computer and use it in GitHub Desktop.

KINTA's heci-qst.c with MEI kernel support

This is mirror of KINTA's heci-qst.c. KINTA's heci-qst.c is modified from Andriy Gapon's heci-qst.c to support Intel MEI.

# Make sure that your kernel have Intel MEI support. Those values should be 'y' or 'm'
$ gzip -dc /proc/config.gz | grep INTEL_MEI
CONFIG_INTEL_MEI=m
CONFIG_INTEL_MEI_ME=m
CONFIG_INTEL_MEI_TXE=m
CONFIG_INTEL_MEI_HDCP=m
CONFIG_INTEL_MEI_WDT=m

# To use MEI
$ gcc -DMEI heci-qst.c && ./a.out

# To use HECI
$ gcc heci-qst.c && ./a.out
References
See Also
/*-
* Copyright (c) 2009 Andriy Gapon <avg@icyb.net.ua>
* Copyright (c) 2009 Gabriel Craciunescu <crazy@frugalware.org>
* ( Added some Linux support )
* Copyright (c) 2011 KINTA-JAPAN <sanu@ruby.plala.or.jp>
* ( Added Intel MEI Driver support )
* Copyright (c) 2014 KINTA-JAPAN <sanu@ruby.plala.or.jp>
* ( 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment