Compare commits

..

3 Commits

Author SHA1 Message Date
4cb9ef42a6 Added watch function 2025-02-01 08:22:52 -08:00
34bbcf7883 Reduced number of syscalls 2025-02-01 08:04:41 -08:00
afdddabc0c Only read Nvidia i2c devices 2025-02-01 00:29:48 -08:00
6 changed files with 115 additions and 105 deletions

View File

@ -39,6 +39,7 @@ Available options:
--reset : Reset all fans to their default mode --reset : Reset all fans to their default mode
--sensors : Print sensor readings even if setting a fan speed --sensors : Print sensor readings even if setting a fan speed
--compact : Print sensor reading in a compact one-line per card format --compact : Print sensor reading in a compact one-line per card format
--watch N : Keep printing output every N seconds
``` ```
### Examples: ### Examples:

View File

@ -13,6 +13,7 @@ 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]; char dev_file[NAME_MAX];
char *pci_addr;
FILE *test_fd; FILE *test_fd;
DIR *dir; DIR *dir;
@ -39,6 +40,11 @@ int find_evga_gpu_i2cs(struct card_info *infos, int max_gpus)
strcpy(device_path, i2c_devices_path); strcpy(device_path, i2c_devices_path);
strcat(device_path, ent->d_name); strcat(device_path, ent->d_name);
/* Only check Nvidia devices */
pci_addr = read_nvidia_pci_address(device_path);
if (pci_addr == NULL)
continue;
/* Read the PCI info for the underlying device */ /* Read the PCI info for the underlying device */
pci_vendor = read_pci_id(device_path, "/device/vendor"); pci_vendor = read_pci_id(device_path, "/device/vendor");
pci_device = read_pci_id(device_path, "/device/device"); pci_device = read_pci_id(device_path, "/device/device");
@ -56,10 +62,11 @@ int find_evga_gpu_i2cs(struct card_info *infos, int max_gpus)
/* Matched all PCI IDs, check for good firmware read */ /* Matched all PCI IDs, check for good firmware read */
strcpy(dev_file, "/dev/"); strcpy(dev_file, "/dev/");
strcat(dev_file, ent->d_name); strcat(dev_file, ent->d_name);
if (check_for_icx3(dev_file)) { infos[num_gpus].i2c_dev_path = dev_file;
if (icx3_init(&infos[num_gpus]) > 0) {
/* Write our card info into the provided struct array */ /* Write our card info into the provided struct array */
infos[num_gpus].card_name = evga_pci_ids[i].card_name; infos[num_gpus].card_name = evga_pci_ids[i].card_name;
infos[num_gpus].pci_id = read_nvidia_pci_address(device_path); infos[num_gpus].pci_id = pci_addr;
infos[num_gpus].i2c_dev_path = calloc(strlen(dev_file) + 1, sizeof(char)); infos[num_gpus].i2c_dev_path = calloc(strlen(dev_file) + 1, sizeof(char));
strcpy(infos[num_gpus].i2c_dev_path, dev_file); strcpy(infos[num_gpus].i2c_dev_path, dev_file);
num_gpus++; num_gpus++;
@ -111,12 +118,18 @@ char *read_nvidia_pci_address(char *device_path)
FILE *fp = fopen(file_path, "r"); FILE *fp = fopen(file_path, "r");
if (fp == NULL) if (fp == NULL) {
free(ret);
return NULL; return NULL;
}
fscanf(fp, "NVIDIA i2c adapter %*u at %16s", ret); fscanf(fp, "NVIDIA i2c adapter %*u at %16s", ret);
fclose(fp); fclose(fp);
if (strlen(ret) == 0) {
free(ret);
return NULL;
}
return ret; return ret;
} }

View File

@ -1,3 +1,6 @@
#ifndef EVGA_CARD_H
#define EVGA_CARD_H
#define NVIDIA_VEN 0x10DE #define NVIDIA_VEN 0x10DE
#define NVIDIA_RTX3060_DEV 0x2503 #define NVIDIA_RTX3060_DEV 0x2503
@ -84,7 +87,8 @@ struct card_info {
char *card_name; char *card_name;
char *pci_id; char *pci_id;
char *i2c_dev_path; char *i2c_dev_path;
int fd; int i2c_fd;
int product_id;
}; };
struct gpu_pci_info { struct gpu_pci_info {
@ -159,3 +163,5 @@ static struct gpu_pci_info evga_pci_ids[] =
int find_evga_gpu_i2cs(struct card_info *infos, int max_gpus); int find_evga_gpu_i2cs(struct card_info *infos, int max_gpus);
unsigned short read_pci_id(char *device_path, char *field); unsigned short read_pci_id(char *device_path, char *field);
char *read_nvidia_pci_address(char *device_path); char *read_nvidia_pci_address(char *device_path);
#endif

View File

@ -20,7 +20,8 @@ static const char helpstring[] = "Available options:\n"
" [+/-]N to set that fan to an RPM offset from the GPU-controlled speed\n" " [+/-]N to set that fan to an RPM offset from the GPU-controlled speed\n"
"--reset : Reset all fans to their default mode\n" "--reset : Reset all fans to their default mode\n"
"--sensors : Print sensor readings even if setting a fan speed \n" "--sensors : Print sensor readings even if setting a fan speed \n"
"--compact : Print sensor reading in a compact one-line per card format\n"; "--compact : Print sensor reading in a compact one-line per card format\n"
"--watch N : Keep printing output every N seconds\n";
void print_gpu_info(int gpu_num, struct card_info gpus[], int compact); void print_gpu_info(int gpu_num, struct card_info gpus[], int compact);
@ -31,6 +32,7 @@ int main (int argc, char **argv)
int print_info = 0; int print_info = 0;
int compact = 0; int compact = 0;
int gpu_num = -1; /* Card to control */ int gpu_num = -1; /* Card to control */
int watch = -1;
char *fan_speed[ICX3_MAX_FANS] = {NULL}; char *fan_speed[ICX3_MAX_FANS] = {NULL};
/* Input parsing */ /* Input parsing */
@ -69,6 +71,14 @@ int main (int argc, char **argv)
print_info = 1; print_info = 1;
} else if (strcmp(argv[i], "--compact") == 0) { } else if (strcmp(argv[i], "--compact") == 0) {
compact = 1; compact = 1;
} else if (strcmp(argv[i], "--watch") == 0) {
i++;
if (i < argc) {
watch = atoi(argv[i]);
} else {
printf(helpstring);
return -1;
}
} else { } else {
printf(helpstring); printf(helpstring);
return 0; return 0;
@ -105,19 +115,18 @@ int main (int argc, char **argv)
for (int i = 0; i < gpu_count; i++){ for (int i = 0; i < gpu_count; i++){
for (int j = 0; j < ICX3_MAX_FANS; j++) { for (int j = 0; j < ICX3_MAX_FANS; j++) {
if (fan_speed[j] != NULL) if (fan_speed[j] != NULL)
set_fan(j, fan_speed[j], gpus[i].i2c_dev_path); set_fan(j, fan_speed[j], &gpus[i]);
/* printf("gpu %d fan %d : %s\n", i, j, fan_speed[j]); */
} }
} }
} else if (gpu_num <= gpu_count - 1) { } else if (gpu_num <= gpu_count - 1) {
for (int j = 0; j < ICX3_MAX_FANS; j++) { for (int j = 0; j < ICX3_MAX_FANS; j++) {
if (fan_speed[j] != NULL) if (fan_speed[j] != NULL)
set_fan(gpu_num, fan_speed[j], gpus[gpu_num].i2c_dev_path); set_fan(gpu_num, fan_speed[j], &gpus[gpu_num]);
/* printf("gpu %d fan %d : %s\n", gpu_num, j, fan_speed[j]);*/
} }
} }
/* print sensor info */ /* print sensor info */
print:
if (print_info) { if (print_info) {
if (gpu_num == -1) { if (gpu_num == -1) {
for (int i = 0; i < gpu_count; i++){ for (int i = 0; i < gpu_count; i++){
@ -127,19 +136,22 @@ int main (int argc, char **argv)
print_gpu_info(gpu_num, gpus, compact); print_gpu_info(gpu_num, gpus, compact);
} }
} }
if (watch > 0) {
sleep(watch);
goto print;
}
} }
void print_gpu_info(int gpu_num, struct card_info gpus[], int compact) { void print_gpu_info(int gpu_num, struct card_info gpus[], int compact) {
if (compact) { if (compact) {
printf("#%d ", gpu_num); printf("#%d ", gpu_num);
print_icx3_fans_oneline(gpus[gpu_num].i2c_dev_path); print_icx3_fans_oneline(&gpus[gpu_num]);
print_icx3_temps_oneline(gpus[gpu_num].i2c_dev_path); print_icx3_temps_oneline(&gpus[gpu_num]);
printf("\n"); printf("\n");
} else { } else {
printf("#%d: %s (%s) @ %s\n", gpu_num, gpus[gpu_num].card_name, gpus[gpu_num].i2c_dev_path, gpus[gpu_num].pci_id); printf("#%d: %s (%s) @ %s\n", gpu_num, gpus[gpu_num].card_name, gpus[gpu_num].i2c_dev_path, gpus[gpu_num].pci_id);
print_icx3_fans(gpus[gpu_num].i2c_dev_path); print_icx3_fans(&gpus[gpu_num]);
print_icx3_temps(gpus[gpu_num].i2c_dev_path); print_icx3_temps(&gpus[gpu_num]);
printf("\n"); printf("\n");
} }

128
icx3.c
View File

@ -9,32 +9,42 @@
#include "icx3.h" #include "icx3.h"
/* Check an I2C device file for an ICX3 controller, returns the product id, or ICX3_PRODUCT_NONE on bad read. */ int icx3_init(struct card_info *card)
enum icx3_product_id check_for_icx3(char *i2c_dev_path)
{ {
char data[I2C_SMBUS_BLOCK_MAX] = {}; char data[I2C_SMBUS_BLOCK_MAX] = {};
int fd, read_result; int read_result;
struct icx3_info *temp_info; struct icx3_info *temp_info;
fd = open_i2c_dev(i2c_dev_path); int fd = open(card->i2c_dev_path, O_RDONLY);
if (fd == -1)
return ICX3_PRODUCT_NONE;
read_result = i2c_smbus_read_i2c_block_data(fd, ICX3_REG_READINFORMATION, ICX3_READINFORMATION_SIZE, data); if (fd == -1)
close(fd); return -1;
if (read_result == ICX3_READINFORMATION_SIZE) {
temp_info = (struct icx3_info*)&data; if (ioctl(fd, I2C_SLAVE, ICX3_I2C_ADDR) < 0) {
if (temp_info->slave_address == ICX3_I2C_ADDR) close(fd);
return temp_info->product_id; return -1;
} }
return ICX3_PRODUCT_NONE; read_result = i2c_smbus_read_i2c_block_data(fd, ICX3_REG_READINFORMATION, ICX3_READINFORMATION_SIZE, data);
if (read_result == ICX3_READINFORMATION_SIZE) {
temp_info = (struct icx3_info*)&data;
if (temp_info->slave_address == ICX3_I2C_ADDR) {
card->product_id = temp_info->product_id;
card->i2c_fd = fd;
return fd;
} else {
close(fd);
return -1;
}
}
return -1;
} }
void print_icx3_fans(char *i2c_dev_path) void print_icx3_fans(struct card_info *card)
{ {
struct icx3_fan_control fans[ICX3_MAX_FANS]; struct icx3_fan_control fans[ICX3_MAX_FANS];
get_fan_status(fans, i2c_dev_path); get_fan_status(fans, card);
for (int i=0; i < ICX3_MAX_FANS; i++) { for (int i=0; i < ICX3_MAX_FANS; i++) {
printf("%s: %d RPM (%d/%d%%, %s)\n", printf("%s: %d RPM (%d/%d%%, %s)\n",
@ -47,10 +57,10 @@ void print_icx3_fans(char *i2c_dev_path)
} }
} }
void print_icx3_fans_oneline(char *i2c_dev_path) void print_icx3_fans_oneline(struct card_info *card)
{ {
struct icx3_fan_control fans[ICX3_MAX_FANS]; struct icx3_fan_control fans[ICX3_MAX_FANS];
get_fan_status(fans, i2c_dev_path); get_fan_status(fans, card);
printf("FAN"); printf("FAN");
for (int i=0; i < ICX3_MAX_FANS; i++) { for (int i=0; i < ICX3_MAX_FANS; i++) {
@ -59,11 +69,11 @@ void print_icx3_fans_oneline(char *i2c_dev_path)
printf("%%"); printf("%%");
} }
void print_icx3_temps(char *i2c_dev_path) void print_icx3_temps(struct card_info *card)
{ {
float temps[ICX3_NUM_TEMP_SENSORS]; float temps[ICX3_NUM_TEMP_SENSORS];
get_temp_sensors(temps, i2c_dev_path); get_temp_sensors(temps, card);
for (int i=0; i<ICX3_NUM_TEMP_SENSORS; i++) { for (int i=0; i<ICX3_NUM_TEMP_SENSORS; i++) {
printf("%s: %+.1f°C\n", printf("%s: %+.1f°C\n",
@ -72,11 +82,11 @@ void print_icx3_temps(char *i2c_dev_path)
} }
} }
void print_icx3_temps_oneline(char *i2c_dev_path) void print_icx3_temps_oneline(struct card_info *card)
{ {
float temps[ICX3_NUM_TEMP_SENSORS]; float temps[ICX3_NUM_TEMP_SENSORS];
get_temp_sensors(temps, i2c_dev_path); get_temp_sensors(temps, card);
for (int i=0; i<ICX3_NUM_TEMP_SENSORS; i++) { for (int i=0; i<ICX3_NUM_TEMP_SENSORS; i++) {
if (i == 0 || strncmp(icx3_temp_sensor_names[i], icx3_temp_sensor_names[i-1], 3)) if (i == 0 || strncmp(icx3_temp_sensor_names[i], icx3_temp_sensor_names[i-1], 3))
@ -86,9 +96,9 @@ void print_icx3_temps_oneline(char *i2c_dev_path)
printf("°C"); printf("°C");
} }
void get_available_fans(char *i2c_dev_path, char *fans_avail) void get_available_fans(char *fans_avail, struct card_info *card)
{ {
int product_id = check_for_icx3(i2c_dev_path); int product_id = card->product_id;
/* From ICX3TotalFanCtrl.cs */ /* From ICX3TotalFanCtrl.cs */
switch(product_id) { switch(product_id) {
@ -142,47 +152,34 @@ void get_available_fans(char *i2c_dev_path, char *fans_avail)
} }
} }
void get_fan_status(struct icx3_fan_control *fans, char *i2c_dev_path) void get_fan_status(struct icx3_fan_control *fans, struct card_info *card)
{ {
char data[I2C_SMBUS_BLOCK_MAX] = {}; char data[I2C_SMBUS_BLOCK_MAX] = {};
int fd, read_result; int read_result;
/* First thing is to check for the product ID to figure out how many fans we have */
char fans_avail[ICX3_MAX_FANS] = {0}; char fans_avail[ICX3_MAX_FANS] = {0};
get_available_fans(i2c_dev_path, fans_avail); get_available_fans(fans_avail, card);
fd = open_i2c_dev(i2c_dev_path);
if (fd == -1)
return;
for (int i=0; i < ICX3_MAX_FANS; i++) { for (int i=0; i < ICX3_MAX_FANS; i++) {
if (!fans_avail[i]) { if (!fans_avail[i]) {
continue; continue;
} }
read_result = i2c_smbus_read_i2c_block_data(fd, ICX3_REG_FANCONTROL + i, ICX3_FANCONTROL_SIZE, data); read_result = i2c_smbus_read_i2c_block_data(card->i2c_fd, ICX3_REG_FANCONTROL + i, ICX3_FANCONTROL_SIZE, data);
if (read_result != ICX3_FANCONTROL_SIZE) { if (read_result != ICX3_FANCONTROL_SIZE) {
close(fd);
return; return;
} }
memcpy(&fans[i], &data, sizeof(struct icx3_fan_control)); memcpy(&fans[i], &data, sizeof(struct icx3_fan_control));
} }
close(fd);
} }
void get_temp_sensors(float *temps, char *i2c_dev_path) void get_temp_sensors(float *temps, struct card_info *card)
{ {
char data[I2C_SMBUS_BLOCK_MAX] = {}; char data[I2C_SMBUS_BLOCK_MAX] = {};
int fd, read_result; int read_result;
struct icx3_temp_sensors *temp_sensors; struct icx3_temp_sensors *temp_sensors;
fd = open_i2c_dev(i2c_dev_path); read_result = i2c_smbus_read_i2c_block_data(card->i2c_fd, ICX3_REG_TEMPSENSOR, ICX3_TEMPSENSOR_SIZE, data);
if (fd == -1)
return;
read_result = i2c_smbus_read_i2c_block_data(fd, ICX3_REG_TEMPSENSOR, ICX3_TEMPSENSOR_SIZE, data);
close(fd);
if (read_result != ICX3_TEMPSENSOR_SIZE) if (read_result != ICX3_TEMPSENSOR_SIZE)
return; return;
temp_sensors = (struct icx3_temp_sensors*) &data; temp_sensors = (struct icx3_temp_sensors*) &data;
@ -198,54 +195,34 @@ void get_temp_sensors(float *temps, char *i2c_dev_path)
} }
} }
int open_i2c_dev(char *i2c_dev_path) void enable_write(int enable, struct card_info *card)
{ {
int 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 == -1)
return -1;
if (ioctl(fd, I2C_SLAVE, ICX3_I2C_ADDR) < 0) {
close(fd);
return -1;
}
return fd;
}
void enable_write(int enable, char *i2c_dev_path)
{
int fd = open_i2c_dev(i2c_dev_path);
if (fd == -1)
return;
unsigned char *data; unsigned char *data;
if (enable) if (enable)
data = icx3_write_enable; data = icx3_write_enable;
else else
data = icx3_write_disable; data = icx3_write_disable;
/* Enable or disable write */ /* Enable or disable write */
i2c_smbus_write_i2c_block_data(fd, ICX3_REG_ENABLEWRITE, ICX3_ENABLEWRITE_SIZE, data); i2c_smbus_write_i2c_block_data(card->i2c_fd, ICX3_REG_ENABLEWRITE, ICX3_ENABLEWRITE_SIZE, data);
/* Read back the result to verify */ /* Read back the result to verify */
unsigned char read_result[ICX3_ENABLEWRITE_SIZE]; unsigned char read_result[ICX3_ENABLEWRITE_SIZE];
int write_ok = 0; int write_ok = 0;
i2c_smbus_read_i2c_block_data(fd, ICX3_REG_ENABLEWRITE, ICX3_ENABLEWRITE_SIZE, read_result); i2c_smbus_read_i2c_block_data(card->i2c_fd, ICX3_REG_ENABLEWRITE, ICX3_ENABLEWRITE_SIZE, read_result);
if (enable) if (enable)
write_ok = (read_result[1] == 0xFC); write_ok = (read_result[1] == 0xFC);
else else
write_ok = (read_result[1] == 0xFE); write_ok = (read_result[1] == 0xFE);
if (!write_ok) if (!write_ok)
printf("Unable to enable/disable write on %s\n", i2c_dev_path); printf("Unable to enable/disable write on %s\n", card->i2c_dev_path);
} }
/* Sets a given fan on the GPU according to a string setting /* Sets a given fan on the GPU according to a string setting
'auto' resets the fan to default (fans 1-2 go to GPU control, 3-4 to +0 RPM offset from GPU control) 'auto' resets the fan to default (fans 1-2 go to GPU control, 3-4 to +0 RPM offset from GPU control)
A number without a sign (e.g. 50) manually sets the fan to a given % duty cycle A number without a sign (e.g. 50) manually sets the fan to a given % duty cycle
A number with a sign (e.g. +500) sets the fan to run at an RPM offset from GPU control */ A number with a sign (e.g. +500) sets the fan to run at an RPM offset from GPU control */
void set_fan(int fan, char *setting, char *i2c_dev_path) void set_fan(int fan, char *setting, struct card_info *card)
{ {
char fans_avail[ICX3_MAX_FANS] = {0}; char fans_avail[ICX3_MAX_FANS] = {0};
@ -256,16 +233,12 @@ void set_fan(int fan, char *setting, char *i2c_dev_path)
int write_result; int write_result;
/* Check to make sure we're setting a valid fan */ /* Check to make sure we're setting a valid fan */
get_available_fans(i2c_dev_path, fans_avail); get_available_fans(fans_avail, card);
if (fans_avail[fan] == 0) { if (fans_avail[fan] == 0) {
printf("Fan %d does not exist on this card \n", fan); printf("Fan %d does not exist on this card \n", fan);
return; return;
} }
int fp = open_i2c_dev(i2c_dev_path);
if (fp == -1)
return;
fan_control.length = ICX3_FANCONTROL_SIZE - 1; fan_control.length = ICX3_FANCONTROL_SIZE - 1;
if (strcmp(setting, "auto") == 0) { if (strcmp(setting, "auto") == 0) {
/* auto setting */ /* auto setting */
@ -283,16 +256,15 @@ void set_fan(int fan, char *setting, char *i2c_dev_path)
fan_control.duty = atoi(setting); fan_control.duty = atoi(setting);
} }
enable_write(1, i2c_dev_path); enable_write(1, card);
i2c_smbus_write_i2c_block_data(fp, reg, ICX3_FANCONTROL_SIZE, (char *)&fan_control); i2c_smbus_write_i2c_block_data(card->i2c_fd, reg, ICX3_FANCONTROL_SIZE, (char *)&fan_control);
/* Read back data and verify we set the fan properly */ /* Read back data and verify we set the fan properly */
i2c_smbus_read_i2c_block_data(fp, reg, ICX3_FANCONTROL_SIZE, (char *)&fan_readback); i2c_smbus_read_i2c_block_data(card->i2c_fd, reg, ICX3_FANCONTROL_SIZE, (char *)&fan_readback);
if (fan_readback.fanmode != fan_control.fanmode || if (fan_readback.fanmode != fan_control.fanmode ||
fan_readback.rpm_offset != fan_control.rpm_offset || fan_readback.rpm_offset != fan_control.rpm_offset ||
fan_readback.duty != fan_control.duty) fan_readback.duty != fan_control.duty)
printf("Error setting fan %d on %s\n", fan, i2c_dev_path); printf("Error setting fan %d on %s\n", fan, card->i2c_fd);
close(fp);
} }

28
icx3.h
View File

@ -1,3 +1,8 @@
#ifndef ICX3_H
#define ICX3_H
#include "evga-card.h"
#define ICX3_I2C_ADDR 0x2D #define ICX3_I2C_ADDR 0x2D
#define ICX3_REG_FANCONTROL 80 #define ICX3_REG_FANCONTROL 80
@ -111,14 +116,15 @@ static char *icx3_temp_sensor_names[] = {
"PWR5", "PWR5",
}; };
enum icx3_product_id check_for_icx3(char *i2c_dev_path); int icx3_init(struct card_info *card);
void print_icx3_fans(char *i2c_dev_path); void print_icx3_fans(struct card_info *card);
void print_icx3_fans_oneline(char *i2c_dev_path); void print_icx3_fans_oneline(struct card_info *card);
void print_icx3_temps(char *i2c_dev_path); void print_icx3_temps(struct card_info *card);
void print_icx3_temps_oneline(char *i2c_dev_path); void print_icx3_temps_oneline(struct card_info *card);
void get_available_fans(char *i2c_dev_path, char *fans_avail); void get_available_fans(char *fans_avail, struct card_info *card);
void get_fan_status(struct icx3_fan_control *fans, char *i2c_dev_path); void get_fan_status(struct icx3_fan_control *fans, struct card_info *card);
void get_temp_sensors(float *temps, char *i2c_dev_path); void get_temp_sensors(float *temps, struct card_info *card);
int open_i2c_dev(char *i2c_dev_path); void enable_write(int enable, struct card_info *card);
void enable_write(int enable, char *i2c_dev_path); void set_fan(int fan, char *setting, struct card_info *card);
void set_fan(int fan, char *setting, char *i2c_dev_path);
#endif