/* $NetBSD: emcfanctl.c,v 1.1 2025/03/11 13:56:48 brad Exp $ */
/*
* Copyright (c) 2025 Brad Spencer <
[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
#ifdef __RCSID
__RCSID("$NetBSD: emcfanctl.c,v 1.1 2025/03/11 13:56:48 brad Exp $");
#endif
/* Main userland program that can interfact with a EMC-210x and EMC-230x
* fan controller using emcfan(4).
*/
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <dev/i2c/emcfanreg.h>
#include <dev/i2c/emcfaninfo.h>
#define EXTERN extern
#include "emcfanctl.h"
#include "emcfanctlconst.h"
#include "emcfanctlutil.h"
#include "emcfanctloutputs.h"
int valid_cmd(const struct emcfanctlcmd[], long unsigned int, char *);
static void
usage(void)
{
const char *p = getprogname();
fprintf(stderr, "Usage: %s [-jdh] device cmd args\n\n",
p);
for(long unsigned int i = 0;i < __arraycount(emcfanctlcmds);i++) {
switch (emcfanctlcmds[i].id) {
case EMCFANCTL_REGISTER:
for(long unsigned int j = 0;j < __arraycount(emcfanctlregistercmds);j++) {
fprintf(stderr,"%s [-jdh] device %s %s\n",
p,
emcfanctlcmds[i].cmd,
emcfanctlregistercmds[j].helpargs);
}
break;
case EMCFANCTL_FAN:
for(long unsigned int j = 0;j < __arraycount(emcfanctlfancmds);j++) {
switch (emcfanctlfancmds[j].id) {
case EMCFANCTL_FAN_DRIVE:
case EMCFANCTL_FAN_DIVIDER:
for(long unsigned int k = 0;k < __arraycount(emcfanctlddcmds);k++) {
fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n",
p,
emcfanctlcmds[i].cmd,
emcfanctlfancmds[j].cmd,
emcfanctlddcmds[k].helpargs);
}
break;
case EMCFANCTL_FAN_MINEXPECTED_RPM:
for(long unsigned int k = 0;k < __arraycount(emcfanctlddcmds);k++) {
if (emcfanctlddcmds[k].id != EMCFANCTL_FAN_DD_WRITE) {
fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n",
p,
emcfanctlcmds[i].cmd,
emcfanctlfancmds[j].cmd,
emcfanctlddcmds[k].helpargs);
} else {
fprintf(stderr,"%s [-jdh] device %s <n> %s %s ",
p,
emcfanctlcmds[i].cmd,
emcfanctlfancmds[j].cmd,
emcfanctlddcmds[k].helpargs);
for(long unsigned int q = 0;q < __arraycount(fan_minexpectedrpm);q++) {
fprintf(stderr,"%d|",fan_minexpectedrpm[q].human_int);
}
fprintf(stderr,"\n");
}
}
break;
case EMCFANCTL_FAN_EDGES:
for(long unsigned int k = 0;k < __arraycount(emcfanctlddcmds);k++) {
if (emcfanctlddcmds[k].id != EMCFANCTL_FAN_DD_WRITE) {
fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n",
p,
emcfanctlcmds[i].cmd,
emcfanctlfancmds[j].cmd,
emcfanctlddcmds[k].helpargs);
} else {
fprintf(stderr,"%s [-jdh] device %s <n> %s %s ",
p,
emcfanctlcmds[i].cmd,
emcfanctlfancmds[j].cmd,
emcfanctlddcmds[k].helpargs);
for(long unsigned int q = 0;q < __arraycount(fan_numedges);q++) {
fprintf(stderr,"%d|",fan_numedges[q].human_int);
}
fprintf(stderr,"\n");
}
}
break;
case EMCFANCTL_FAN_POLARITY:
for(long unsigned int k = 0;k < __arraycount(emcfanctlpcmds);k++) {
fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n",
p,
emcfanctlcmds[i].cmd,
emcfanctlfancmds[j].cmd,
emcfanctlpcmds[k].helpargs);
}
break;
case EMCFANCTL_FAN_PWM_BASEFREQ:
for(long unsigned int k = 0;k < __arraycount(emcfanctlddcmds);k++) {
if (emcfanctlddcmds[k].id != EMCFANCTL_FAN_DD_WRITE) {
fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n",
p,
emcfanctlcmds[i].cmd,
emcfanctlfancmds[j].cmd,
emcfanctlddcmds[k].helpargs);
} else {
fprintf(stderr,"%s [-jdh] device %s <n> %s %s ",
p,
emcfanctlcmds[i].cmd,
emcfanctlfancmds[j].cmd,
emcfanctlddcmds[k].helpargs);
for(long unsigned int q = 0;q < __arraycount(fan_pwm_basefreq);q++) {
if (fan_pwm_basefreq[q].instance > 0)
break;
fprintf(stderr,"%d|",fan_pwm_basefreq[q].human_int);
}
fprintf(stderr,"\n");
}
}
break;
case EMCFANCTL_FAN_PWM_OUTPUTTYPE:
for(long unsigned int k = 0;k < __arraycount(emcfanctlotcmds);k++) {
fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n",
p,
emcfanctlcmds[i].cmd,
emcfanctlfancmds[j].cmd,
emcfanctlotcmds[k].helpargs);
}
break;
default:
fprintf(stderr,"%s [-jdh] device %s %s\n",
p,
emcfanctlcmds[i].cmd,
emcfanctlfancmds[j].helpargs);
break;
};
}
break;
case EMCFANCTL_APD:
case EMCFANCTL_SMBUSTO:
for(long unsigned int j = 0;j < __arraycount(emcfanctlapdsmtocmds);j++) {
fprintf(stderr,"%s [-jdh] device %s %s\n",
p,
emcfanctlcmds[i].cmd,
emcfanctlapdsmtocmds[j].helpargs);
}
break;
default:
fprintf(stderr,"%s [-jdh] device %s %s\n",
p,emcfanctlcmds[i].cmd,emcfanctlcmds[i].helpargs);
break;
};
}
}
int
valid_cmd(const struct emcfanctlcmd c[], long unsigned int csize, char *cmdtocheck)
{
int r = -1;
for(long unsigned int i = 0;i < csize;i++) {
if (strncmp(cmdtocheck,c[i].cmd,16) == 0) {
r = i;
break;
}
}
return r;
}
int
main(int argc, char *argv[])
{
int c;
bool debug = false;
uint8_t product_id;
int product_family;
int fd = -1, valid, error = 0, validsub = -1, validsubsub = -1;
bool jsonify = false;
int start_reg = 0xff, end_reg, value, tvalue, instance;
int the_fan;
while ((c = getopt(argc, argv, "djh")) != -1 ) {
switch (c) {
case 'd':
debug = true;
break;
case 'j':
jsonify = true;
break;
case 'h':
default:
usage();
exit(0);
}
}
argc -= optind;
argv += optind;
if (debug) {
fprintf(stderr,"ARGC: %d\n", argc);
fprintf(stderr,"ARGV[0]: %s ; ARGV[1]: %s ; ARGV[2]: %s ; ARGV[3]: %s; ARGV[4]: %s; ARGV[5]: %s\n",
argv[0],argv[1],argv[2],argv[3],argv[4],argv[5]);
}
if (argc <= 1) {
usage();
exit(0);
}
fd = open(argv[0], O_RDWR, 0);
if (fd == -1) {
err(EXIT_FAILURE, "open %s", argv[0]);
}
error = emcfan_read_register(fd, EMCFAN_PRODUCT_ID, &product_id, debug);
if (error)
err(EXIT_FAILURE, "read product_id %d", error);
int iindex;
iindex = emcfan_find_info(product_id);
if (iindex == -1) {
printf("Unknown info for product_id: %d\n",product_id);
exit(2);
}
product_family = emcfan_chip_infos[iindex].family;
/* Parse out the command line into what the requested action is */
valid = valid_cmd(emcfanctlcmds,__arraycount(emcfanctlcmds),argv[1]);
if (valid != -1) {
switch (emcfanctlcmds[valid].id) {
case EMCFANCTL_INFO:
error = output_emcfan_info(fd, product_id, product_family, jsonify, debug);
if (error != 0) {
errno = error;
err(EXIT_FAILURE, "output info");
}
break;
case EMCFANCTL_APD:
if (argc >= 3) {
validsub = valid_cmd(emcfanctlapdsmtocmds,__arraycount(emcfanctlapdsmtocmds),argv[2]);
if (product_id == EMCFAN_PRODUCT_2103_24 ||
product_id == EMCFAN_PRODUCT_2104 ||
product_id == EMCFAN_PRODUCT_2106) {
switch(emcfanctlapdsmtocmds[validsub].id) {
case EMCFANCTL_APD_READ:
error = output_emcfan_apd(fd, product_id, product_family, EMCFAN_CHIP_CONFIG, jsonify, debug);
if (error != 0) {
fprintf(stderr, "Error output for apd subcommand: %d\n",error);
exit(1);
}
break;
case EMCFANCTL_APD_ON:
case EMCFANCTL_APD_OFF:
tvalue = find_translated_bits_by_str(apd, __arraycount(apd), argv[2]);
if (tvalue < 0) {
fprintf(stderr,"Error converting human value: %s\n", argv[4]);
exit(1);
}
error = emcfan_rmw_register(fd, EMCFAN_CHIP_CONFIG, 0, apd, __arraycount(apd), tvalue, debug);
if (error != 0) {
errno = error;
err(EXIT_FAILURE, "read/modify/write register");
}
break;
default:
fprintf(stderr,"Unhandled subcommand to apd: %s %d\n\n", argv[2], validsub);
usage();
exit(1);
break;
};
} else {
errno = EINVAL;
err(EXIT_FAILURE, "This chip does not support the APD command");
}
} else {
fprintf(stderr,"Missing arguments to apd command\n\n");
usage();
exit(1);
}
break;
case EMCFANCTL_SMBUSTO:
if (argc >= 3) {
if (product_id == EMCFAN_PRODUCT_2101 ||
product_id == EMCFAN_PRODUCT_2101R) {
start_reg = EMCFAN_2101_CHIP_CONFIG;
instance = 2101;
} else {
if (product_family == EMCFAN_FAMILY_230X) {
start_reg = EMCFAN_CHIP_CONFIG;
instance = 2301;
} else {
start_reg = EMCFAN_CHIP_CONFIG_2;
instance = 2103;
}
}
validsub = valid_cmd(emcfanctlapdsmtocmds,__arraycount(emcfanctlapdsmtocmds),argv[2]);
switch(emcfanctlapdsmtocmds[validsub].id) {
case EMCFANCTL_APD_READ:
error = output_emcfan_smbusto(fd, product_id, product_family, start_reg, instance, jsonify, debug);
if (error != 0) {
fprintf(stderr, "Error output for SMBUS timeout subcommand: %d\n",error);
exit(1);
}
break;
case EMCFANCTL_APD_ON:
case EMCFANCTL_APD_OFF:
tvalue = find_translated_bits_by_str_instance(smbus_timeout, __arraycount(smbus_timeout), argv[2], instance);
if (tvalue < 0) {
fprintf(stderr,"Error converting human value: %s\n", argv[4]);
exit(1);
}
error = emcfan_rmw_register(fd, start_reg, 0, smbus_timeout, __arraycount(smbus_timeout), tvalue, debug);
if (error != 0) {
errno = error;
err(EXIT_FAILURE, "read/modify/write register");
}
break;
default:
fprintf(stderr,"Unhandled subcommand to SMBUS timeout: %s %d\n\n", argv[2], validsub);
usage();
exit(1);
break;
};
} else {
fprintf(stderr,"Missing arguments to SMBUS timeout command\n\n");
usage();
exit(1);
}
break;
case EMCFANCTL_REGISTER:
if (argc >= 3) {
validsub = valid_cmd(emcfanctlregistercmds,__arraycount(emcfanctlregistercmds),argv[2]);
switch (emcfanctlregistercmds[validsub].id) {
case EMCFANCTL_REGISTER_LIST:
output_emcfan_register_list(product_id, product_family, jsonify, debug);
break;
case EMCFANCTL_REGISTER_READ:
start_reg = end_reg = 0x00;
if (argc >= 4) {
start_reg = (uint8_t)strtoi(argv[3], NULL, 0, 0, 0xff, &error);
if (error) {
start_reg = emcfan_reg_by_name(product_id, product_family, argv[3]);
if (start_reg == -1) {
fprintf(stderr,"Bad conversion for read register start: %s\n", argv[3]);
exit(1);
}
}
end_reg = start_reg;
if (argc == 5) {
end_reg = (uint8_t)strtoi(argv[4], NULL, 0, 0, 0xff, &error);
if (error) {
end_reg = emcfan_reg_by_name(product_id, product_family, argv[4]);
if (end_reg == -1) {
fprintf(stderr,"Bad conversion for read register end: %s\n", argv[4]);
exit(1);
}
}
}
} else {
end_reg = 0xff;
}
if (end_reg < start_reg) {
fprintf(stderr,"Register end can not be less than register start: %d %d\n\n", start_reg, end_reg);
usage();
exit(1);
}
if (debug)
fprintf(stderr,"register read: START_REG: %d 0x%02X, END_REG: %d 0x%02X\n",start_reg, start_reg, end_reg, end_reg);
error = output_emcfan_register_read(fd, product_id, product_family, start_reg, end_reg, jsonify, debug);
if (error != 0) {
fprintf(stderr, "Error read register: %d\n",error);
exit(1);
}
break;
case EMCFANCTL_REGISTER_WRITE:
if (argc == 5) {
start_reg = (uint8_t)strtoi(argv[3], NULL, 0, 0, 0xff, &error);
if (error) {
start_reg = emcfan_reg_by_name(product_id, product_family, argv[3]);
if (start_reg == -1) {
fprintf(stderr,"Bad conversion for write register: %s\n", argv[3]);
exit(1);
}
}
value = (uint8_t)strtoi(argv[4], NULL, 0, 0, 0xff, &error);
if (error) {
fprintf(stderr,"Could not convert value for register write.\n");
usage();
exit(1);
}
} else {
fprintf(stderr,"Not enough arguments for register write.\n");
usage();
exit(1);
}
if (debug)
fprintf(stderr,"register write: START_REG: %d 0x%02X, VALUE: %d 0x%02X\n",start_reg, start_reg, value, value);
error = emcfan_write_register(fd, start_reg, value, debug);
if (error != 0) {
errno = error;
err(EXIT_FAILURE, "write register");
}
break;
default:
fprintf(stderr,"Unhandled subcommand to register: %s %d\n\n", argv[2], validsub);
usage();
exit(1);
break;
};
} else {
fprintf(stderr,"Missing arguments to register command\n\n");
usage();
exit(1);
}
break;
case EMCFANCTL_FAN:
if (argc >= 4) {
the_fan = strtoi(argv[2], NULL, 0, 1, emcfan_chip_infos[iindex].num_fans, &error);
if (error) {
fprintf(stderr,"Bad conversion for fan number: %s\n", argv[2]);
exit(1);
}
the_fan--;
validsub = valid_cmd(emcfanctlfancmds,__arraycount(emcfanctlfancmds),argv[3]);
switch (emcfanctlfancmds[validsub].id) {
case EMCFANCTL_FAN_STATUS:
if (product_id != EMCFAN_PRODUCT_2101 &&
product_id != EMCFAN_PRODUCT_2101R) {
if (product_family == EMCFAN_FAMILY_230X) {
start_reg = EMCFAN_230X_FAN_STATUS;
end_reg = EMCFAN_230X_FAN_DRIVE_STATUS;
} else {
start_reg = EMCFAN_210_346_FAN_STATUS;
end_reg = 0xff;
}
} else {
errno = EINVAL;
err(EXIT_FAILURE, "EMC2101 and EMC2101R do not support this subcommand");
}
break;
case EMCFANCTL_FAN_DRIVE:
start_reg = emcfan_chip_infos[iindex].fan_drive_registers[the_fan];
break;
case EMCFANCTL_FAN_DIVIDER:
start_reg = emcfan_chip_infos[iindex].fan_divider_registers[the_fan];
break;
case EMCFANCTL_FAN_MINEXPECTED_RPM:
case EMCFANCTL_FAN_EDGES:
if (product_id != EMCFAN_PRODUCT_2101 &&
product_id != EMCFAN_PRODUCT_2101R) {
if (debug)
fprintf(stderr,"fan subcommand minexpected_rpm / edges: the_fan=%d\n",the_fan);
if (product_family == EMCFAN_FAMILY_210X) {
the_fan = strtoi(argv[2], NULL, 0, 1, emcfan_chip_infos[iindex].num_tachs, &error);
if (error) {
fprintf(stderr,"Bad conversion for fan number: %s\n", argv[2]);
exit(1);
}
the_fan--;
switch(the_fan) {
case 0:
start_reg = EMCFAN_210_346_CONFIG_1;
break;
case 1:
start_reg = EMCFAN_210_346_CONFIG_2;
break;
default:
start_reg = 0xff;
break;
};
} else {
switch(the_fan) {
case 0:
start_reg = EMCFAN_230X_CONFIG_1;
break;
case 1:
start_reg = EMCFAN_230X_CONFIG_2;
break;
case 2:
start_reg = EMCFAN_230X_CONFIG_3;
break;
case 3:
start_reg = EMCFAN_230X_CONFIG_4;
break;
case 4:
start_reg = EMCFAN_230X_CONFIG_5;
break;
default:
start_reg = 0xff;
break;
};
}
} else {
errno = EINVAL;
err(EXIT_FAILURE, "EMC2101 and EMC2101R do not support this subcommand");
}
break;
case EMCFANCTL_FAN_POLARITY:
if (product_id != EMCFAN_PRODUCT_2101 &&
product_id != EMCFAN_PRODUCT_2101R) {
start_reg = EMCFAN_POLARITY_CONFIG;
} else {
start_reg = EMCFAN_2101_FAN_CONFIG;
}
break;
case EMCFANCTL_FAN_PWM_BASEFREQ:
if (product_id != EMCFAN_PRODUCT_2101 &&
product_id != EMCFAN_PRODUCT_2101R) {
if (product_family == EMCFAN_FAMILY_210X)
start_reg = EMCFAN_210_346_PWM_BASEFREQ;
else
if (the_fan <= 2)
start_reg = EMCFAN_230X_BASE_FREQ_123;
else
start_reg = EMCFAN_230X_BASE_FREQ_45;
} else {
errno = EINVAL;
err(EXIT_FAILURE, "EMC2101 and EMC2101R do not support this subcommand");
}
break;
case EMCFANCTL_FAN_PWM_OUTPUTTYPE:
if (product_family == EMCFAN_FAMILY_230X) {
start_reg = EMCFAN_230X_OUTPUT_CONFIG;
} else {
errno = EINVAL;
err(EXIT_FAILURE, "This subcommand is only supported on the EMC230X family");
}
break;
default:
fprintf(stderr,"Unhandled subcommand to fan: %s %d\n\n", argv[3], validsub);
usage();
exit(1);
break;
};
if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_STATUS) {
error = output_emcfan_fan_status(fd, product_id, product_family, start_reg, end_reg, the_fan, jsonify, debug);
if (error != 0) {
fprintf(stderr, "Error fan status for fan subcommand: %d\n",error);
exit(1);
}
}
if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_DRIVE ||
emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_DIVIDER) {
validsubsub = valid_cmd(emcfanctlddcmds,__arraycount(emcfanctlddcmds),argv[4]);
switch(emcfanctlddcmds[validsubsub].id) {
case EMCFANCTL_FAN_DD_READ:
if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_DRIVE)
error = output_emcfan_drive(fd, product_id, product_family, start_reg, jsonify, debug);
else
error = output_emcfan_divider(fd, product_id, product_family, start_reg, jsonify, debug);
if (error != 0) {
fprintf(stderr, "Error read drive / divider for fan subcommand: %d\n",error);
exit(1);
}
break;
case EMCFANCTL_FAN_DD_WRITE:
value = (uint8_t)strtoi(argv[5], NULL, 0, 0, 0xff, &error);
if (error) {
fprintf(stderr,"Could not convert value for fan subcommand write.\n");
usage();
exit(1);
}
error = emcfan_write_register(fd, start_reg, value, debug);
if (error != 0) {
errno = error;
err(EXIT_FAILURE, "write register");
}
break;
default:
fprintf(stderr,"Unhandled subsubcommand to fan: %s %d\n\n", argv[4], validsubsub);
usage();
exit(1);
break;
};
}
if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_MINEXPECTED_RPM) {
if (start_reg == 0xff) {
fprintf(stderr,"fan minexpected rpm subcommand, unknown register\n");
exit(1);
}
validsubsub = valid_cmd(emcfanctlddcmds,__arraycount(emcfanctlddcmds),argv[4]);
switch(emcfanctlddcmds[validsubsub].id) {
case EMCFANCTL_FAN_DD_READ:
error = output_emcfan_minexpected_rpm(fd, product_id, product_family, start_reg, jsonify, debug);
if (error != 0) {
fprintf(stderr, "Error read minexpected rpm subcommand: %d\n",error);
exit(1);
}
break;
case EMCFANCTL_FAN_DD_WRITE:
value = (int)strtoi(argv[5], NULL, 0, 0, 0xffff, &error);
if (error) {
fprintf(stderr,"Could not convert value for minexpected rpm subcommand write.\n");
usage();
exit(1);
}
tvalue = find_translated_bits_by_hint(fan_minexpectedrpm, __arraycount(fan_minexpectedrpm), value);
if (tvalue < 0) {
fprintf(stderr,"Error converting human value: %d %d\n",value, tvalue);
exit(1);
}
error = emcfan_rmw_register(fd, start_reg, value, fan_minexpectedrpm, __arraycount(fan_minexpectedrpm), tvalue, debug);
if (error != 0) {
errno = error;
err(EXIT_FAILURE, "read/modify/write register");
}
break;
default:
fprintf(stderr,"Unhandled subsubcommand to minexpected rpm subcommand: %s %d\n\n", argv[4], validsubsub);
usage();
exit(1);
break;
};
}
if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_EDGES) {
if (start_reg == 0xff) {
fprintf(stderr,"fan edges subcommand, unknown register\n");
exit(1);
}
validsubsub = valid_cmd(emcfanctlddcmds,__arraycount(emcfanctlddcmds),argv[4]);
switch(emcfanctlddcmds[validsubsub].id) {
case EMCFANCTL_FAN_DD_READ:
error = output_emcfan_edges(fd, product_id, product_family, start_reg, jsonify, debug);
if (error != 0) {
fprintf(stderr, "Error read edges subcommand: %d\n",error);
exit(1);
}
break;
case EMCFANCTL_FAN_DD_WRITE:
value = (uint8_t)strtoi(argv[5], NULL, 0, 0, 9, &error);
if (error) {
fprintf(stderr,"Could not convert value for edges subcommand write.\n");
usage();
exit(1);
}
tvalue = find_translated_bits_by_hint(fan_numedges, __arraycount(fan_numedges), value);
if (tvalue < 0) {
fprintf(stderr,"Error converting human value: %d\n",value);
exit(1);
}
error = emcfan_rmw_register(fd, start_reg, value, fan_numedges, __arraycount(fan_numedges), tvalue, debug);
if (error != 0) {
errno = error;
err(EXIT_FAILURE, "read/modify/write register");
}
break;
default:
fprintf(stderr,"Unhandled subsubcommand to minexpected rpm subcommand: %s %d\n\n", argv[4], validsubsub);
usage();
exit(1);
break;
};
}
if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_POLARITY) {
if (start_reg == 0xff) {
fprintf(stderr,"fan polarity subcommand, unknown register\n");
exit(1);
}
validsubsub = valid_cmd(emcfanctlpcmds,__arraycount(emcfanctlpcmds),argv[4]);
switch(emcfanctlpcmds[validsubsub].id) {
case EMCFANCTL_FAN_P_READ:
error = output_emcfan_polarity(fd, product_id, product_family, start_reg, the_fan, jsonify, debug);
if (error != 0) {
fprintf(stderr, "Error output for polarity subcommand: %d\n",error);
exit(1);
}
break;
case EMCFANCTL_FAN_P_INVERTED:
case EMCFANCTL_FAN_P_NONINVERTED:
if (product_id == EMCFAN_PRODUCT_2101 ||
product_id == EMCFAN_PRODUCT_2101R) {
tvalue = find_translated_bits_by_str_instance(fan_polarity, __arraycount(fan_polarity), argv[4], 2101);
} else {
tvalue = find_translated_bits_by_str_instance(fan_polarity, __arraycount(fan_polarity), argv[4], the_fan);
}
if (tvalue < 0) {
fprintf(stderr,"Error converting human value: %s\n", argv[4]);
exit(1);
}
error = emcfan_rmw_register(fd, start_reg, 0, fan_polarity, __arraycount(fan_polarity), tvalue, debug);
if (error != 0) {
errno = error;
err(EXIT_FAILURE, "read/modify/write register");
}
break;
default:
fprintf(stderr,"Unhandled subsubcommand to polarity subcommand: %s %d\n\n", argv[4], validsubsub);
usage();
exit(1);
break;
};
}
if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_PWM_BASEFREQ) {
if (start_reg == 0xff) {
fprintf(stderr,"fan base_freq subcommand, unknown register\n");
exit(1);
}
validsubsub = valid_cmd(emcfanctlddcmds,__arraycount(emcfanctlddcmds),argv[4]);
switch(emcfanctlddcmds[validsubsub].id) {
case EMCFANCTL_FAN_DD_READ:
if (debug)
fprintf(stderr,"%s: the_fan=%d\n",__func__,the_fan);
if (product_id != EMCFAN_PRODUCT_2305)
error = output_emcfan_pwm_basefreq(fd, product_id, product_family, start_reg, the_fan, jsonify, debug);
else
if (the_fan <= 2)
error = output_emcfan_pwm_basefreq(fd, product_id, product_family, start_reg, the_fan, jsonify, debug);
else
error = output_emcfan_pwm_basefreq(fd, product_id, product_family, start_reg,
the_fan + 23050, jsonify, debug);
if (error != 0) {
fprintf(stderr, "Error read base_freq subcommand: %d\n",error);
exit(1);
}
break;
case EMCFANCTL_FAN_DD_WRITE:
value = (int)strtoi(argv[5], NULL, 0, 0, 0xffff, &error);
if (error) {
fprintf(stderr,"Could not convert value for base_freq subcommand write.\n");
usage();
exit(1);
}
if (product_id != EMCFAN_PRODUCT_2305)
tvalue = find_translated_bits_by_hint_instance(fan_pwm_basefreq, __arraycount(fan_pwm_basefreq), value, the_fan);
else
if (the_fan <= 2)
tvalue = find_translated_bits_by_hint_instance(fan_pwm_basefreq, __arraycount(fan_pwm_basefreq), value, the_fan);
else
tvalue = find_translated_bits_by_hint_instance(fan_pwm_basefreq, __arraycount(fan_pwm_basefreq),
value, the_fan + 23050);
if (tvalue < 0) {
fprintf(stderr,"Error converting human value: %d %d\n",value, tvalue);
exit(1);
}
error = emcfan_rmw_register(fd, start_reg, value, fan_pwm_basefreq, __arraycount(fan_pwm_basefreq), tvalue, debug);
if (error != 0) {
errno = error;
err(EXIT_FAILURE, "read/modify/write register");
}
break;
default:
fprintf(stderr,"Unhandled subsubcommand to base_freq subcommand: %s %d\n\n", argv[4], validsubsub);
usage();
exit(1);
break;
};
break;
}
if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_PWM_OUTPUTTYPE) {
if (start_reg == 0xff) {
fprintf(stderr,"fan pwm_output_type subcommand, unknown register\n");
exit(1);
}
validsubsub = valid_cmd(emcfanctlotcmds,__arraycount(emcfanctlotcmds),argv[4]);
switch(emcfanctlotcmds[validsubsub].id) {
case EMCFANCTL_FAN_OT_READ:
error = output_emcfan_pwm_output_type(fd, product_id, product_family, start_reg, the_fan, jsonify, debug);
if (error != 0) {
fprintf(stderr, "Error output for pwm_output_type subcommand: %d\n",error);
exit(1);
}
break;
case EMCFANCTL_FAN_OT_PUSHPULL:
case EMCFANCTL_FAN_OT_OPENDRAIN:
tvalue = find_translated_bits_by_str_instance(fan_pwm_output_type, __arraycount(fan_pwm_output_type), argv[4], the_fan);
if (tvalue < 0) {
fprintf(stderr,"Error converting human value: %s\n", argv[4]);
exit(1);
}
error = emcfan_rmw_register(fd, start_reg, 0, fan_pwm_output_type, __arraycount(fan_pwm_output_type), tvalue, debug);
if (error != 0) {
errno = error;
err(EXIT_FAILURE, "read/modify/write register");
}
break;
default:
fprintf(stderr,"Unhandled subsubcommand to pwm_output_type subcommand: %s %d\n\n", argv[4], validsubsub);
usage();
exit(1);
break;
};
}
} else {
fprintf(stderr,"Missing arguments to fan command\n\n");
usage();
exit(1);
}
break;
default:
fprintf(stderr,"Unknown handling of command: %d\n",valid);
exit(2);
break;
}
} else {
fprintf(stderr,"Unknown command: %s\n\n",argv[1]);
usage();
exit(1);
}
close(fd);
exit(0);
}