Added display of fan status and temperature sensors.
This commit is contained in:
parent
23a734cf0a
commit
bcd4457c2f
141
evga-card.c
141
evga-card.c
@ -1,18 +1,22 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "evga-card.h"
|
#include "evga-card.h"
|
||||||
#include "icx3.h"
|
#include "icx3.h"
|
||||||
|
|
||||||
/* Search all i2c device files for ones are on a PCI device of a supported GPU,
|
/* Search all i2c device files for ones are on a PCI device of a supported GPU,
|
||||||
and respond with the correct iCX3 version information */
|
and respond with the correct iCX3 version information */
|
||||||
int find_evga_gpu_i2cs(struct card_info **infos, int max_gpus)
|
int find_evga_gpu_i2cs(struct card_info *infos, int max_gpus)
|
||||||
{
|
{
|
||||||
char i2c_devices_path[NAME_MAX];
|
char i2c_devices_path[NAME_MAX];
|
||||||
char device_path[NAME_MAX];
|
char device_path[NAME_MAX];
|
||||||
|
char dev_file[NAME_MAX];
|
||||||
|
|
||||||
FILE *test_fd;
|
FILE *test_fd;
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *ent;
|
||||||
|
|
||||||
int num_gpus = 0;
|
int num_gpus = 0;
|
||||||
unsigned short pci_vendor, pci_device, pci_subsystem_vendor, pci_subsystem_device = 0;
|
unsigned short pci_vendor, pci_device, pci_subsystem_vendor, pci_subsystem_device = 0;
|
||||||
@ -38,114 +42,46 @@ int find_evga_gpu_i2cs(struct card_info **infos, int max_gpus)
|
|||||||
strcat(device_path, ent->d_name);
|
strcat(device_path, ent->d_name);
|
||||||
|
|
||||||
/* Read the PCI info for the underlying device */
|
/* Read the PCI info for the underlying device */
|
||||||
pci_vendor = read_pci_id(device_path, "/vendor");
|
pci_vendor = read_pci_id(device_path, "/device/vendor");
|
||||||
pci_device = read_pci_id(device_path, "/device");
|
pci_device = read_pci_id(device_path, "/device/device");
|
||||||
pci_subsystem_vendor = read_pci_id(device_path, "/subsystem_vendor");
|
pci_subsystem_vendor = read_pci_id(device_path, "/device/subsystem_vendor");
|
||||||
pci_subsystem_device = read_pci_id(device_path, "/subsystem_device");
|
pci_subsystem_device = read_pci_id(device_path, "/device/subsystem_device");
|
||||||
|
|
||||||
/* See if it's a matching device for a supported EVGA card */
|
/* See if it's a matching device for a supported EVGA card */
|
||||||
for (int i = 0, i < (sizeof(evga_pci_ids) / sizeof(struct gpu_pci_info)), i++) {
|
for (int i = 0; i < (sizeof(evga_pci_ids) / sizeof(struct gpu_pci_info)); i++) {
|
||||||
|
|
||||||
if (pci_vendor == evga_pci_ids[i]->vendor_id &&
|
if (pci_vendor == evga_pci_ids[i].vendor_id &&
|
||||||
pci_device == evga_pci_ids[i]->device_id &&
|
pci_device == evga_pci_ids[i].device_id &&
|
||||||
pci_subsystem_vendor == evga_pci_ids[i]->subvendor_id &&
|
pci_subsystem_vendor == evga_pci_ids[i].subvendor_id &&
|
||||||
pci_subsystem_device == evga_pci_ids[i]->subdevice_id) {
|
pci_subsystem_device == evga_pci_ids[i].subdevice_id) {
|
||||||
|
|
||||||
/* Matched all PCI IDs, check for good firmware read */
|
/* Matched all PCI IDs, check for good firmware read */
|
||||||
strcpy(device_path, "/dev/");
|
strcpy(dev_file, "/dev/");
|
||||||
strcat(device_path, ent->d_name);
|
strcat(dev_file, ent->d_name);
|
||||||
if (check_for_icx3(device_path)) {
|
if (check_for_icx3(dev_file)) {
|
||||||
|
|
||||||
/* Write our card info into the provided struct array */
|
/* Write our card info into the provided struct array */
|
||||||
card_info[num_gpus]->card_name = &evga_pci_ids[i]->card_name;
|
infos[num_gpus].card_name = evga_pci_ids[i].card_name;
|
||||||
card_info[num_gpus]->pci_id
|
infos[num_gpus].pci_id = read_nvidia_pci_address(device_path);
|
||||||
card_info[num_gpus]->i2c_dev_path = calloc(strlen(device_path) + 1, sizeof(char));
|
infos[num_gpus].i2c_dev_path = calloc(strlen(dev_file) + 1, sizeof(char));
|
||||||
strcpy(card_info[num_gpus]->i2c_dev_path, device_path);
|
strcpy(infos[num_gpus].i2c_dev_path, dev_file);
|
||||||
|
num_gpus++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
strcat(device_string, "/name");
|
if (num_gpus == max_gpus)
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
if(strncmp(ent->d_name, "i2c-", 4) == 0)
|
|
||||||
{
|
|
||||||
strcpy(device_string, i2c_devices_path);
|
|
||||||
strcat(device_string, ent->d_name);
|
|
||||||
strcat(device_string, "/name");
|
|
||||||
test_fd = fopen(device_string, O_RDONLY);
|
|
||||||
|
|
||||||
if(test_fd)
|
|
||||||
{
|
|
||||||
memset(device_string, 0x00, sizeof(device_string));
|
|
||||||
|
|
||||||
if(read(test_fd, device_string, sizeof(device_string)) < 0)
|
|
||||||
{
|
|
||||||
printf("Failed to read i2c device name\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
device_string[strlen(device_string) - 1] = 0x00;
|
|
||||||
|
|
||||||
close(test_fd);
|
|
||||||
|
|
||||||
// Clear PCI Information
|
|
||||||
pci_vendor = 0;
|
|
||||||
pci_device = 0;
|
|
||||||
pci_subsystem_vendor = 0;
|
|
||||||
pci_subsystem_device = 0;
|
|
||||||
port_id = 0;
|
|
||||||
|
|
||||||
// Get port ID for NVidia GPUs
|
|
||||||
sscanf(device_string, "NVIDIA i2c adapter %hu at", &port_id);
|
|
||||||
|
|
||||||
// Get device path
|
|
||||||
strcpy(path, i2c_devices_path);
|
|
||||||
strcat(path, ent->d_name);
|
|
||||||
if(ent->d_type == DT_LNK)
|
|
||||||
{
|
|
||||||
ptr = realpath(path, NULL);
|
|
||||||
if(ptr == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
strcpy(path, ptr);
|
|
||||||
strcat(path, "/..");
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strcat(path, "/device");
|
|
||||||
}
|
|
||||||
ptr = path + strlen(path);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (pci_vendor == NVIDIA_VEN && pci_subsystem_vendor == EVGA_SUB_VEN) {
|
|
||||||
|
|
||||||
strcpy(device_string, "/dev/");
|
|
||||||
strcat(device_string, ent->d_name);
|
|
||||||
test_fd = open(device_string, O_RDWR);
|
|
||||||
|
|
||||||
if (test_fd < 0)
|
|
||||||
{
|
|
||||||
printf("Failed to open device file %s, are you root or have permissions for i2c?\n", device_string);
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
} else {
|
|
||||||
printf("Bad FW version read %s\n", device_string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return num_gpus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read a 16-bit unsigned int from a pci id file */
|
||||||
unsigned short read_pci_id(char *device_path, char *field)
|
unsigned short read_pci_id(char *device_path, char *field)
|
||||||
{
|
{
|
||||||
char *buf[16];
|
char buf[16];
|
||||||
char file_path[NAME_MAX];
|
char file_path[NAME_MAX];
|
||||||
|
|
||||||
strcpy(file_path, device_path);
|
strcpy(file_path, device_path);
|
||||||
@ -166,4 +102,23 @@ unsigned short read_pci_id(char *device_path, char *field)
|
|||||||
return (unsigned short)strtoul(buf, NULL, 16);
|
return (unsigned short)strtoul(buf, NULL, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *read_nvidia_pci_address(char *device_path)
|
||||||
|
{
|
||||||
|
char file_path[NAME_MAX];
|
||||||
|
|
||||||
|
char *ret = calloc(16 + 1, sizeof(char)); /* assuming pci ids could look as large as 00000000:0C:00.0 */
|
||||||
|
|
||||||
|
strcpy(file_path, device_path);
|
||||||
|
strcat(file_path, "/name");
|
||||||
|
|
||||||
|
FILE *fp = fopen(file_path, "r");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fscanf(fp, "NVIDIA i2c adapter %*u at %16s", ret);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
21
evga-card.h
21
evga-card.h
@ -1,3 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#define NVIDIA_VEN 0x10DE
|
#define NVIDIA_VEN 0x10DE
|
||||||
|
|
||||||
#define NVIDIA_RTX3060_DEV 0x2503
|
#define NVIDIA_RTX3060_DEV 0x2503
|
||||||
@ -80,16 +82,11 @@
|
|||||||
#define EVGA_RTX3090TI_FTW3_GAMING_SUB_DEV 0x4983
|
#define EVGA_RTX3090TI_FTW3_GAMING_SUB_DEV 0x4983
|
||||||
#define EVGA_RTX3090TI_FTW3_ULTRA_GAMING_SUB_DEV 0x4985
|
#define EVGA_RTX3090TI_FTW3_ULTRA_GAMING_SUB_DEV 0x4985
|
||||||
|
|
||||||
#define MAX_GPUS 16
|
struct card_info {
|
||||||
|
|
||||||
int find_evga_gpu_i2cs(struct card_info **infos, int max_gpus);
|
|
||||||
unsigned short read_pci_id(char *path);
|
|
||||||
|
|
||||||
struct card_info (
|
|
||||||
char *card_name;
|
char *card_name;
|
||||||
char *pci_id;
|
char *pci_id;
|
||||||
char *i2c_dev_path;
|
char *i2c_dev_path;
|
||||||
);
|
};
|
||||||
|
|
||||||
struct gpu_pci_info {
|
struct gpu_pci_info {
|
||||||
char *card_name;
|
char *card_name;
|
||||||
@ -99,7 +96,7 @@ struct gpu_pci_info {
|
|||||||
unsigned short subdevice_id;
|
unsigned short subdevice_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gpu_pci_info evga_pci_ids[] =
|
static struct gpu_pci_info evga_pci_ids[] =
|
||||||
{
|
{
|
||||||
{"EVGA GeForce RTX 3060 Ti FTW3 Gaming" , NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, EVGA_SUB_VEN, EVGA_RTX3060TI_FTW3_GAMING_SUB_DEV },
|
{"EVGA GeForce RTX 3060 Ti FTW3 Gaming" , NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, EVGA_SUB_VEN, EVGA_RTX3060TI_FTW3_GAMING_SUB_DEV },
|
||||||
{"EVGA GeForce RTX 3060 Ti FTW3 Ultra" , NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, EVGA_SUB_VEN, EVGA_RTX3060TI_FTW3_ULTRA_SUB_DEV },
|
{"EVGA GeForce RTX 3060 Ti FTW3 Ultra" , NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, EVGA_SUB_VEN, EVGA_RTX3060TI_FTW3_ULTRA_SUB_DEV },
|
||||||
@ -157,5 +154,9 @@ struct gpu_pci_info evga_pci_ids[] =
|
|||||||
{"EVGA GeForce RTX 3090 K|NGP|N Hydro Copper" , NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_KINGPIN_HC_SUB_DEV },
|
{"EVGA GeForce RTX 3090 K|NGP|N Hydro Copper" , NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_KINGPIN_HC_SUB_DEV },
|
||||||
{"EVGA GeForce RTX 3090 Ti FTW3 Black Gaming" , NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, EVGA_SUB_VEN, EVGA_RTX3090TI_FTW3_BLACK_SUB_DEV },
|
{"EVGA GeForce RTX 3090 Ti FTW3 Black Gaming" , NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, EVGA_SUB_VEN, EVGA_RTX3090TI_FTW3_BLACK_SUB_DEV },
|
||||||
{"EVGA GeForce RTX 3090 Ti FTW3 Gaming" , NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, EVGA_SUB_VEN, EVGA_RTX3090TI_FTW3_GAMING_SUB_DEV },
|
{"EVGA GeForce RTX 3090 Ti FTW3 Gaming" , NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, EVGA_SUB_VEN, EVGA_RTX3090TI_FTW3_GAMING_SUB_DEV },
|
||||||
{"EVGA GeForce RTX 3090 Ti FTW3 Ultra Gaming" , NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, EVGA_SUB_VEN, EVGA_RTX3090TI_FTW3_ULTRA_GAMING_SUB_DEV },
|
{"EVGA GeForce RTX 3090 Ti FTW3 Ultra Gaming" , NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, EVGA_SUB_VEN, EVGA_RTX3090TI_FTW3_ULTRA_GAMING_SUB_DEV }
|
||||||
}
|
};
|
||||||
|
|
||||||
|
int find_evga_gpu_i2cs(struct card_info *infos, int max_gpus);
|
||||||
|
unsigned short read_pci_id(char *device_path, char *field);
|
||||||
|
char *read_nvidia_pci_address(char *device_path);
|
||||||
|
25
evga-icx.c
25
evga-icx.c
@ -5,17 +5,32 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
#include "icx3.h"
|
#include "icx3.h"
|
||||||
#include "nvidia_pci.h"
|
#include "evga-card.h"
|
||||||
|
|
||||||
|
#define MAX_GPUS 16
|
||||||
|
|
||||||
int main (int argc, char **argv)
|
int main (int argc, char **argv)
|
||||||
{
|
{
|
||||||
int *gpus[MAX_GPUS];
|
struct card_info gpus[MAX_GPUS];
|
||||||
if (find_evga_gpu_i2c(gpus) == -1) {
|
int num_gpus;
|
||||||
|
|
||||||
|
num_gpus = find_evga_gpu_i2cs(gpus, MAX_GPUS);
|
||||||
|
|
||||||
|
if (num_gpus == -1) {
|
||||||
|
printf("Error scanning I2C devices\n");
|
||||||
|
return -1;
|
||||||
|
} else if (num_gpus == 0) {
|
||||||
|
printf("No supported GPUs found.\nAre you root or do you have udev access to i2c devices?\nDo you need to run `modprobe i2c-dev`?\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < num_gpus; i++){
|
||||||
|
printf("#%d: %s (%s) @ %s\n", i, gpus[i].card_name, gpus[i].i2c_dev_path, gpus[i].pci_id);
|
||||||
|
print_icx3_fans(gpus[i].i2c_dev_path);
|
||||||
|
print_icx3_temps(gpus[i].i2c_dev_path);
|
||||||
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
183
icx3.c
183
icx3.c
@ -1,72 +1,163 @@
|
|||||||
#include <linux/i2c-dev.h>
|
#include <linux/i2c-dev.h>
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <i2c/smbus.h>
|
#include <i2c/smbus.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "icx3.h"
|
#include "icx3.h"
|
||||||
|
|
||||||
void *print_icx3_temp(int fp)
|
/* Check an I2C device file for an ICX3 controller, returns the product id, or ICX3_PRODUCT_NONE on bad read. */
|
||||||
|
enum icx3_product_id check_for_icx3(char *i2c_dev_path)
|
||||||
{
|
{
|
||||||
int read_result;
|
char data[I2C_SMBUS_BLOCK_MAX] = {};
|
||||||
char buff[1024];
|
int fd, read_result;
|
||||||
struct icx3_fancontrol *fanstatus;
|
struct icx3_info *temp_info;
|
||||||
|
|
||||||
|
fd = open(i2c_dev_path, O_RDONLY);
|
||||||
|
if (!fd)
|
||||||
|
return ICX3_PRODUCT_NONE;
|
||||||
|
|
||||||
|
if (ioctl(fd, I2C_SLAVE, ICX3_I2C_ADDR) < 0) {
|
||||||
|
close(fd);
|
||||||
|
return ICX3_PRODUCT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_result = i2c_smbus_read_i2c_block_data(fd, ICX3_REG_READINFORMATION, ICX3_READINFORMATION_SIZE, data);
|
||||||
|
close(fd);
|
||||||
|
if (read_result == ICX3_READINFORMATION_SIZE) {
|
||||||
|
temp_info = (struct icx3_info*)&data;
|
||||||
|
if (temp_info->slave_address == ICX3_I2C_ADDR)
|
||||||
|
return temp_info->product_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ICX3_PRODUCT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_icx3_fans(char *i2c_dev_path)
|
||||||
|
{
|
||||||
|
char data[I2C_SMBUS_BLOCK_MAX] = {};
|
||||||
|
int fd, read_result;
|
||||||
|
|
||||||
|
struct icx3_fan_control *fan_status;
|
||||||
|
|
||||||
|
/* First thing is to check for the product ID to figure out how many fans we have */
|
||||||
|
int fans_avail[ICX3_MAX_FANS] = {0};
|
||||||
|
int product_id = check_for_icx3(i2c_dev_path);
|
||||||
|
|
||||||
|
/* From ICX3TotalFanCtrl.cs */
|
||||||
|
switch(product_id) {
|
||||||
|
case ICX3_PRODUCT_E221_E222_XC3:
|
||||||
|
case ICX3_PRODUCT_E229_XC3:
|
||||||
|
case ICX3_PRODUCT_E222_XC3_LPC54113:
|
||||||
|
case ICX3_PRODUCT_E229_XC3_LPC54113:
|
||||||
|
case ICX3_PRODUCT_E221_XC3_STM32L431:
|
||||||
|
case ICX3_PRODUCT_E229_E237_XC3_STM32L431:
|
||||||
|
/* XC3, default plus fan 3 */
|
||||||
|
fans_avail[0] = 1;
|
||||||
|
fans_avail[1] = 1;
|
||||||
|
fans_avail[2] = 1;
|
||||||
|
break;
|
||||||
|
case ICX3_PRODUCT_E227_E228_FTW3:
|
||||||
|
case ICX3_PRODUCT_E230_FTW3:
|
||||||
|
case ICX3_PRODUCT_E227_E228_FTW3_LPC54113:
|
||||||
|
case ICX3_PRODUCT_E230_FTW3_LPC54113:
|
||||||
|
case ICX3_PRODUCT_E230_E238_FTW3_STM32L431:
|
||||||
|
case ICX3_PRODUCT_E250_FTW3:
|
||||||
|
/* FTW3, default plus fan 3 and external fan */
|
||||||
|
fans_avail[0] = 1;
|
||||||
|
fans_avail[1] = 1;
|
||||||
|
fans_avail[2] = 1;
|
||||||
|
fans_avail[3] = 1;
|
||||||
|
break;
|
||||||
|
case ICX3_PRODUCT_E223_KINGPIN_HYBRID:
|
||||||
|
case ICX3_PRODUCT_E227_E228_FTW3_HYBRID:
|
||||||
|
case ICX3_PRODUCT_E251_KINGPIN_HYBRID:
|
||||||
|
case ICX3_PRODUCT_E227_E228_FTW3_HYBRID_LPC54113:
|
||||||
|
case ICX3_PRODUCT_E250_FTW3_HYBRID:
|
||||||
|
/* KINGPIN, default and external */
|
||||||
|
fans_avail[0] = 1;
|
||||||
|
fans_avail[1] = 1;
|
||||||
|
fans_avail[3] = 1;
|
||||||
|
break;
|
||||||
|
case ICX3_PRODUCT_E227_E228_FTW3_HC:
|
||||||
|
case ICX3_PRODUCT_E223_KINGPIN_HC:
|
||||||
|
case ICX3_PRODUCT_E251_KINGPIN_HC:
|
||||||
|
/* External fan only */
|
||||||
|
fans_avail[3] = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fans_avail[0] = 1;
|
||||||
|
fans_avail[1] = 1;
|
||||||
|
break;
|
||||||
|
case ICX3_PRODUCT_E221_E222_XC3_HC:
|
||||||
|
/* Not sure what this case is for */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(i2c_dev_path, O_RDONLY);
|
||||||
|
|
||||||
|
/* Error silently here because we should have already checked we can read the i2c in check_for_icx3() */
|
||||||
|
if (!fd)
|
||||||
|
return;
|
||||||
|
if (ioctl(fd, I2C_SLAVE, ICX3_I2C_ADDR) < 0) {
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i=0; i < ICX3_MAX_FANS; i++) {
|
for (int i=0; i < ICX3_MAX_FANS; i++) {
|
||||||
read_result = i2c_smbus_read_i2c_block_data(fp, ICX3_REG_FANCONTROL + i, ICX3_FANCONTROL_SIZE, buff);
|
if (!fans_avail[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
read_result = i2c_smbus_read_i2c_block_data(fd, ICX3_REG_FANCONTROL + i, ICX3_FANCONTROL_SIZE, data);
|
||||||
if (read_result != ICX3_FANCONTROL_SIZE) {
|
if (read_result != ICX3_FANCONTROL_SIZE) {
|
||||||
return 0;
|
close(fd);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
fanstatus = (struct icx3_fancontrol*) &buff;
|
fan_status = (struct icx3_fan_control*) &data;
|
||||||
printf("%s: %d RPM (%d/%d%%, %s)\n",
|
printf("%s: %d RPM (%d/%d%%, %s)\n",
|
||||||
icx3_fan_names[i],
|
icx3_fan_names[i],
|
||||||
fanstatus->rpm_status,
|
fan_status->rpm_status,
|
||||||
fanstatus->duty_status,
|
fan_status->duty_status,
|
||||||
fanstatus->duty,
|
fan_status->duty,
|
||||||
icx3_fan_mode_names[fanstatus->fanmode]
|
icx3_fan_mode_names[fan_status->fanmode]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_icx3_temps(char *i2c_dev_path)
|
||||||
|
{
|
||||||
|
char data[I2C_SMBUS_BLOCK_MAX] = {};
|
||||||
|
int fd, read_result;
|
||||||
|
struct icx3_temp_sensors *temp_sensors;
|
||||||
|
|
||||||
struct icx3_tempsensors *tempsensors;
|
fd = open(i2c_dev_path, O_RDONLY);
|
||||||
read_result = i2c_smbus_read_i2c_block_data(fp, ICX3_REG_TEMPSENSOR, ICX3_TEMPSENSOR_SIZE, buff);
|
|
||||||
if (read_result != ICX3_TEMPSENSOR_SIZE) {
|
/* Error silently here because we should have already checked we can read the i2c in check_for_icx3() */
|
||||||
return 0;
|
if (!fd)
|
||||||
|
return;
|
||||||
|
if (ioctl(fd, I2C_SLAVE, ICX3_I2C_ADDR) < 0) {
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
tempsensors = (struct icx3_tempsensors*) &buff;
|
|
||||||
|
read_result = i2c_smbus_read_i2c_block_data(fd, ICX3_REG_TEMPSENSOR, ICX3_TEMPSENSOR_SIZE, data);
|
||||||
|
if (read_result != ICX3_TEMPSENSOR_SIZE) {
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
temp_sensors = (struct icx3_temp_sensors*) &data;
|
||||||
|
|
||||||
float cur_temp;
|
float cur_temp;
|
||||||
short cur_data;
|
short cur_data;
|
||||||
|
|
||||||
for (int i=0; i<ICX3_NUM_TEMP_SENSORS; i++) {
|
for (int i=0; i<ICX3_NUM_TEMP_SENSORS; i++) {
|
||||||
cur_data = (short)(tempsensors->data[2*i+1] << 8) | (short)(tempsensors->data[2*i]);
|
/* endian swap */
|
||||||
|
cur_data = (short)(temp_sensors->data[2*i+1] << 8) | (short)(temp_sensors->data[2*i]);
|
||||||
|
/* temp is reported in tenths of deg C */
|
||||||
cur_temp = (float)cur_data/10;
|
cur_temp = (float)cur_data/10;
|
||||||
printf("%s %.1f C\n",
|
printf("%s %.1f C\n",
|
||||||
icx3_temp_sensor_names[i],
|
icx3_temp_sensor_names[i],
|
||||||
cur_temp);
|
cur_temp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check an I2C device file for an ICX3 controller, returs the product id. */
|
|
||||||
enum icx3_product_id check_for_icx3(char *i2c_dev_path)
|
|
||||||
{
|
|
||||||
char data[I2C_SMBUS_BLOCK_MAX] = {};
|
|
||||||
int test_fd, read_result;
|
|
||||||
struct icx3_info *temp_info;
|
|
||||||
|
|
||||||
test_fd = fopen(i2c_dev_path, O_RDONLY);
|
|
||||||
if (!test_fd)
|
|
||||||
return ICX3_NONE;
|
|
||||||
|
|
||||||
if (ioctl(test_fd, I2C_SLAVE, ICX3_I2C_ADDR) < 0) {
|
|
||||||
close(test_fd);
|
|
||||||
return ICX3_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_result = i2c_smbus_read_i2c_block_data(test_fd, ICX3_REG_READINFORMATION, ICX3_READINFORMATION_SIZE, buff);
|
|
||||||
if (read_result == ICX3_READINFORMATION_SIZE) {
|
|
||||||
temp_info = (struct *icx3_info)&buff;
|
|
||||||
if (temp_info->slave_address == ICX3_I2C_ADDR)
|
|
||||||
return temp_info->product_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ICX3_NONE;
|
|
||||||
|
|
||||||
}
|
|
78
icx3.h
78
icx3.h
@ -1,3 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#define ICX3_I2C_ADDR 0x2D
|
#define ICX3_I2C_ADDR 0x2D
|
||||||
|
|
||||||
#define ICX3_REG_FANCONTROL 80
|
#define ICX3_REG_FANCONTROL 80
|
||||||
@ -11,8 +13,7 @@
|
|||||||
#define ICX3_MAX_FANS 4
|
#define ICX3_MAX_FANS 4
|
||||||
#define ICX3_NUM_TEMP_SENSORS 9
|
#define ICX3_NUM_TEMP_SENSORS 9
|
||||||
|
|
||||||
|
struct icx3_fan_control {
|
||||||
struct icx3_fancontrol {
|
|
||||||
unsigned char length;
|
unsigned char length;
|
||||||
unsigned char fanmode;
|
unsigned char fanmode;
|
||||||
unsigned short rpm_offset;
|
unsigned short rpm_offset;
|
||||||
@ -21,12 +22,13 @@ struct icx3_fancontrol {
|
|||||||
unsigned short rpm_status;
|
unsigned short rpm_status;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct icx3_tempsensors {
|
struct icx3_temp_sensors {
|
||||||
unsigned char length;
|
unsigned char length;
|
||||||
unsigned char data[18];
|
unsigned char data[18];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct icx3_info {
|
struct icx3_info {
|
||||||
|
unsigned char length;
|
||||||
unsigned char reserved;
|
unsigned char reserved;
|
||||||
unsigned char slave_address;
|
unsigned char slave_address;
|
||||||
unsigned char product_id;
|
unsigned char product_id;
|
||||||
@ -35,45 +37,45 @@ struct icx3_info {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum icx3_product_id {
|
enum icx3_product_id {
|
||||||
ICX3_IAP = 160,
|
ICX3_PRODUCT_IAP = 160,
|
||||||
ICX3_E227_E228_FTW3 = 2,
|
ICX3_PRODUCT_E227_E228_FTW3 = 2,
|
||||||
ICX3_E230_FTW3 = 4,
|
ICX3_PRODUCT_E230_FTW3 = 4,
|
||||||
ICX3_E227_E228_FTW3_HYBRID = 8,
|
ICX3_PRODUCT_E227_E228_FTW3_HYBRID = 8,
|
||||||
ICX3_E227_E228_FTW3_HC = 9,
|
ICX3_PRODUCT_E227_E228_FTW3_HC = 9,
|
||||||
ICX3_E221_E222_XC3 = 1,
|
ICX3_PRODUCT_E221_E222_XC3 = 1,
|
||||||
ICX3_E229_XC3 = 5,
|
ICX3_PRODUCT_E229_XC3 = 5,
|
||||||
ICX3_E221_E222_XC3_HYBRID = 6,
|
ICX3_PRODUCT_E221_E222_XC3_HYBRID = 6,
|
||||||
ICX3_E221_E222_XC3_HC = 7,
|
ICX3_PRODUCT_E221_E222_XC3_HC = 7,
|
||||||
ICX3_IAP_KINGPIN = 161,
|
ICX3_PRODUCT_IAP_KINGPIN = 161,
|
||||||
ICX3_E223_KINGPIN_HYBRID = 3,
|
ICX3_PRODUCT_E223_KINGPIN_HYBRID = 3,
|
||||||
ICX3_E223_KINGPIN_HC = 10,
|
ICX3_PRODUCT_E223_KINGPIN_HC = 10,
|
||||||
ICX3_IAP_KINGPIN_E251 = 165,
|
ICX3_PRODUCT_IAP_KINGPIN_E251 = 165,
|
||||||
ICX3_E251_KINGPIN_HYBRID = 19,
|
ICX3_PRODUCT_E251_KINGPIN_HYBRID = 19,
|
||||||
ICX3_E251_KINGPIN_HC = 26,
|
ICX3_PRODUCT_E251_KINGPIN_HC = 26,
|
||||||
ICX3_IAP_LPC5516 = 164,
|
ICX3_PRODUCT_IAP_LPC5516 = 164,
|
||||||
ICX3_E250_FTW3 = 50,
|
ICX3_PRODUCT_E250_FTW3 = 50,
|
||||||
ICX3_E250_FTW3_HYBRID = 56,
|
ICX3_PRODUCT_E250_FTW3_HYBRID = 56,
|
||||||
ICX3_IAP_LPC54113 = 162,
|
ICX3_PRODUCT_IAP_LPC54113 = 162,
|
||||||
ICX3_E227_E228_FTW3_LPC54113 = 18,
|
ICX3_PRODUCT_E227_E228_FTW3_LPC54113 = 18,
|
||||||
ICX3_E227_E228_FTW3_HYBRID_LPC54113 = 24,
|
ICX3_PRODUCT_E227_E228_FTW3_HYBRID_LPC54113 = 24,
|
||||||
ICX3_E230_FTW3_LPC54113 = 20,
|
ICX3_PRODUCT_E230_FTW3_LPC54113 = 20,
|
||||||
ICX3_E222_XC3_LPC54113 = 17,
|
ICX3_PRODUCT_E222_XC3_LPC54113 = 17,
|
||||||
ICX3_E229_XC3_LPC54113 = 21,
|
ICX3_PRODUCT_E229_XC3_LPC54113 = 21,
|
||||||
ICX3_IAP_STM32L431 = 163,
|
ICX3_PRODUCT_IAP_STM32L431 = 163,
|
||||||
ICX3_E230_E238_FTW3_STM32L431 = 36,
|
ICX3_PRODUCT_E230_E238_FTW3_STM32L431 = 36,
|
||||||
ICX3_E221_XC3_STM32L431 = 33,
|
ICX3_PRODUCT_E221_XC3_STM32L431 = 33,
|
||||||
ICX3_E229_E237_XC3_STM32L431 = 37,
|
ICX3_PRODUCT_E229_E237_XC3_STM32L431 = 37,
|
||||||
ICX3_NONE = 0
|
ICX3_PRODUCT_NONE = 0
|
||||||
}
|
};
|
||||||
|
|
||||||
const char *icx3_fan_names[] = {
|
static char *icx3_fan_names[] = {
|
||||||
"Fan 1",
|
"Fan 1",
|
||||||
"Fan 2",
|
"Fan 2",
|
||||||
"Fan 3",
|
"Fan 3",
|
||||||
"Ext. fan"
|
"Ext. fan"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *icx3_fan_mode_names[] = {
|
static char *icx3_fan_mode_names[] = {
|
||||||
"Default",
|
"Default",
|
||||||
"Auto",
|
"Auto",
|
||||||
"Manual",
|
"Manual",
|
||||||
@ -82,7 +84,7 @@ const char *icx3_fan_mode_names[] = {
|
|||||||
"Mode 3"
|
"Mode 3"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *icx3_temp_sensor_names[] = {
|
static char *icx3_temp_sensor_names[] = {
|
||||||
"GPU2",
|
"GPU2",
|
||||||
"MEM1",
|
"MEM1",
|
||||||
"MEM2",
|
"MEM2",
|
||||||
@ -94,4 +96,6 @@ const char *icx3_temp_sensor_names[] = {
|
|||||||
"PWR5",
|
"PWR5",
|
||||||
};
|
};
|
||||||
|
|
||||||
enum icx3_product_id check_for_icx3(char *i2c_dev_path);
|
enum icx3_product_id check_for_icx3(char *i2c_dev_path);
|
||||||
|
void print_icx3_fans(char *i2c_dev_path);
|
||||||
|
void print_icx3_temps(char *i2c_dev_path);
|
||||||
|
11
makefile
11
makefile
@ -1,13 +1,16 @@
|
|||||||
.PHONY : clean debug
|
.PHONY : clean debug
|
||||||
|
|
||||||
|
OBJS = evga-icx.o evga-card.o icx3.o
|
||||||
LDLIBS = -li2c
|
LDLIBS = -li2c
|
||||||
objects = evga-icx.o
|
CFLAGS = -MD
|
||||||
|
|
||||||
evga-icx : $(objects)
|
evga-icx : $(OBJS)
|
||||||
|
|
||||||
debug : CFLAGS += -g -O0
|
debug : CFLAGS += -g -O0
|
||||||
debug : evga-icx
|
debug : evga-icx
|
||||||
|
|
||||||
clean :
|
clean :
|
||||||
rm evga-icx $(objects)
|
rm evga-icx $(OBJS)
|
||||||
|
rm *.d
|
||||||
|
|
||||||
|
-include $(OBJS:.o=.d)
|
Loading…
x
Reference in New Issue
Block a user