/* $NetBSD: emcfanctloutputs.c,v 1.2 2025/03/12 14:01:49 brad Exp $ */ /* * Copyright (c) 2025 Brad Spencer * * 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. */ #ifdef __RCSID __RCSID("$NetBSD: emcfanctloutputs.c,v 1.2 2025/03/12 14:01:49 brad Exp $"); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define EXTERN #include "emcfanctl.h" #include "emcfanctlconst.h" #include "emcfanctlutil.h" #include "emcfanctloutputs.h" int output_emcfan_info(int fd, uint8_t product_id, int product_family, bool jsonify, bool debug) { int err = 0; uint8_t res; mj_t obj; char *s = NULL; char *pn; char fn[8]; err = emcfan_read_register(fd, EMCFAN_REVISION, &res, debug); if (err != 0) goto out; if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, "revision", "integer", (int64_t)res); mj_append_field(&obj, "product_id", "integer", (int64_t)product_id); mj_append_field(&obj, "product_family", "integer", (int64_t)product_family); pn = emcfan_product_to_name(product_id); mj_append_field(&obj, "chip_name", "string", pn, strlen(pn)); emcfan_family_to_name(product_family, fn, sizeof(fn)); mj_append_field(&obj, "family_name", "string", fn, strlen(fn)); mj_asprint(&s, &obj, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); } else { emcfan_family_to_name(product_family, fn, sizeof(fn)); printf("Product Family: %s\n", fn); printf("Chip name: %s\n", emcfan_product_to_name(product_id)); printf("Revision: %d\n", res); } out: return(err); } static void output_emcfan_generic_reg_list(uint8_t product_id, const struct emcfan_registers the_registers[], long unsigned int the_registers_size, bool jsonify, bool debug) { mj_t array; mj_t obj; char *s = NULL; int iindex; iindex = emcfan_find_info(product_id); if (iindex == -1) { printf("Unknown info for product_id: %d\n",product_id); exit(2); } if (debug) fprintf(stderr, "output_emcfan_generic_reg_list: iindex=%d\n",iindex); if (jsonify) { memset(&array, 0x0, sizeof(array)); mj_create(&array, "array"); } for(long unsigned int i = 0;i < the_registers_size;i++) { if (emcfan_reg_is_real(iindex, the_registers[i].reg)) { if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, "register_name", "string", the_registers[i].name, strlen(the_registers[i].name)); mj_append_field(&obj, "register", "integer", (int64_t)the_registers[i].reg); mj_append(&array, "object", &obj); mj_delete(&obj); } else { printf("%s\t%d\t0x%02X\n",the_registers[i].name,the_registers[i].reg,the_registers[i].reg); } } } if (jsonify) { mj_asprint(&s, &array, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); mj_delete(&array); } } void output_emcfan_register_list(uint8_t product_id, int product_family, bool jsonify, bool debug) { if (debug) fprintf(stderr,"output_emcfan_list: product_id=%d, product_family=%d\n",product_id, product_family); switch(product_family) { case EMCFAN_FAMILY_210X: switch(product_id) { case EMCFAN_PRODUCT_2101: case EMCFAN_PRODUCT_2101R: output_emcfan_generic_reg_list(product_id, emcfanctl_2101_registers, __arraycount(emcfanctl_2101_registers), jsonify, debug); break; case EMCFAN_PRODUCT_2103_1: output_emcfan_generic_reg_list(product_id, emcfanctl_2103_1_registers, __arraycount(emcfanctl_2103_1_registers), jsonify, debug); break; case EMCFAN_PRODUCT_2103_24: output_emcfan_generic_reg_list(product_id, emcfanctl_2103_24_registers, __arraycount(emcfanctl_2103_24_registers), jsonify, debug); break; case EMCFAN_PRODUCT_2104: output_emcfan_generic_reg_list(product_id, emcfanctl_2104_registers, __arraycount(emcfanctl_2104_registers), jsonify, debug); break; case EMCFAN_PRODUCT_2106: output_emcfan_generic_reg_list(product_id, emcfanctl_2106_registers, __arraycount(emcfanctl_2106_registers), jsonify, debug); break; default: printf("UNSUPPORTED YET %d\n",product_id); exit(99); break; }; break; case EMCFAN_FAMILY_230X: output_emcfan_generic_reg_list(product_id, emcfanctl_230x_registers, __arraycount(emcfanctl_230x_registers), jsonify, debug); break; }; } static int output_emcfan_230x_read_reg(int fd, uint8_t product_id, int product_family, uint8_t start, uint8_t end, bool jsonify, bool debug) { int err = 0; uint8_t res; mj_t array; mj_t obj; char *s = NULL; int iindex; const char *rn; iindex = emcfan_find_info(product_id); if (iindex == -1) { printf("Unknown info for product_id: %d\n",product_id); exit(2); } if (debug) fprintf(stderr, "output_emcfan_230x_read_reg: product_id=%d, product_family=%d, iindex=%d\n",product_id, product_family, iindex); if (jsonify) { memset(&array, 0x0, sizeof(array)); mj_create(&array, "array"); } for(int i = start; i <= end; i++) { if (emcfan_reg_is_real(iindex, i)) { err = emcfan_read_register(fd, i, &res, debug); if (err != 0) break; if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); rn = emcfan_regname_by_reg(product_id, product_family, i); mj_append_field(&obj, "register_name", "string", rn, strlen(rn)); mj_append_field(&obj, "register", "integer", (int64_t)i); mj_append_field(&obj, "register_value", "integer", (int64_t)res); mj_append(&array, "object", &obj); mj_delete(&obj); } else { printf("%s;%d (0x%02X);%d (0x%02X)\n",emcfan_regname_by_reg(product_id, product_family, i),i,i,res,res); } } } if (jsonify) { mj_asprint(&s, &array, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); mj_delete(&array); } return(err); } int output_emcfan_register_read(int fd, uint8_t product_id, int product_family, uint8_t start, uint8_t end, bool jsonify, bool debug) { int err = 0; if (debug) fprintf(stderr,"output_emcfan_register_read: start=%d 0x%02X, end=%d 0x%02X\n",start, start, end, end); switch(product_family) { case EMCFAN_FAMILY_210X: err = output_emcfan_230x_read_reg(fd, product_id, product_family, start, end, jsonify, debug); break; case EMCFAN_FAMILY_230X: err = output_emcfan_230x_read_reg(fd, product_id, product_family, start, end, jsonify, debug); break; }; return(err); } int output_emcfan_minexpected_rpm(int fd, uint8_t product_id, int product_family, uint8_t config_reg, bool jsonify, bool debug) { int err = 0; uint8_t raw_res, res; uint8_t clear_mask; int human_value; char *s = NULL; mj_t obj; err = emcfan_read_register(fd, config_reg, &raw_res, debug); if (err != 0) goto out; clear_mask = fan_minexpectedrpm[0].clear_mask; res = raw_res & clear_mask; if (debug) fprintf(stderr,"%s: clear_mask=0x%02X 0x%02X, raw_res=%d (0x%02X), res=%d (0x%02X)\n",__func__,clear_mask,(uint8_t)~clear_mask,raw_res,raw_res,res,res); human_value = find_human_int(fan_minexpectedrpm, __arraycount(fan_minexpectedrpm), res); if (human_value == -10191) return(EINVAL); if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, "minimum_expected_rpm", "integer", (int64_t)human_value); mj_append_field(&obj, "register", "integer", (int64_t)config_reg); mj_append_field(&obj, "register_value", "integer", (int64_t)raw_res); mj_asprint(&s, &obj, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); } else { printf("Minumum expected rpm:%d\n",human_value); } out: return(err); } int output_emcfan_edges(int fd, uint8_t product_id, int product_family, uint8_t config_reg, bool jsonify, bool debug) { int err = 0; uint8_t raw_res, res; uint8_t clear_mask; int human_value; char *s = NULL; mj_t obj; err = emcfan_read_register(fd, config_reg, &raw_res, debug); if (err != 0) goto out; clear_mask = fan_numedges[0].clear_mask; res = raw_res & clear_mask; if (debug) fprintf(stderr,"%s: clear_mask=0x%02X 0x%02X, raw_res=%d (0x%02X), res=%d (0x%02X)\n",__func__,clear_mask,(uint8_t)~clear_mask,raw_res,raw_res,res,res); human_value = find_human_int(fan_numedges, __arraycount(fan_numedges), res); if (human_value == -10191) return(EINVAL); if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, "num_edges", "integer", (int64_t)human_value); mj_append_field(&obj, "register", "integer", (int64_t)config_reg); mj_append_field(&obj, "register_value", "integer", (int64_t)raw_res); mj_asprint(&s, &obj, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); } else { printf("Number of edges:%d\n",human_value); } out: return(err); } static int output_emcfan_simple_int(int fd, uint8_t product_id, int product_family, uint8_t reg, const char *what, const char *whatj, bool jsonify, bool debug) { int err = 0; uint8_t res; char *s = NULL; mj_t obj; err = emcfan_read_register(fd, reg, &res, debug); if (err != 0) goto out; if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, whatj, "integer", (int64_t)res); mj_append_field(&obj, "register", "integer", (int64_t)reg); mj_append_field(&obj, "register_value", "integer",(int64_t) res); mj_asprint(&s, &obj, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); } else { printf("%s:%d\n",what, res); } out: return(err); } int output_emcfan_drive(int fd, uint8_t product_id, int product_family, uint8_t reg, bool jsonify, bool debug) { return(output_emcfan_simple_int(fd, product_id, product_family, reg, "Drive", "drive_level", jsonify, debug)); } int output_emcfan_divider(int fd, uint8_t product_id, int product_family, uint8_t reg, bool jsonify, bool debug) { return(output_emcfan_simple_int(fd, product_id, product_family, reg, "Divider", "frequency_divider", jsonify, debug)); } int output_emcfan_pwm_basefreq(int fd, uint8_t product_id, int product_family, uint8_t reg, int the_fan, bool jsonify, bool debug) { int err = 0; uint8_t res; int tindex; char *s = NULL; mj_t obj; err = emcfan_read_register(fd, reg, &res, debug); if (err != 0) goto out; tindex = find_translated_blob_by_bits_instance(fan_pwm_basefreq, __arraycount(fan_pwm_basefreq), res, the_fan); if (debug) fprintf(stderr,"%s: reg=%d 0x%02X, res=0x%02x, tindex=%d, the_fan=%d\n",__func__,reg,reg,res,tindex,the_fan); if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, "fan", "integer", (int64_t)the_fan+1); mj_append_field(&obj, "pwm_base_frequency", "integer", (int64_t)fan_pwm_basefreq[tindex].human_int); mj_append_field(&obj, "register", "integer", (int64_t)reg); mj_append_field(&obj, "register_value", "integer", (int64_t)res); mj_asprint(&s, &obj, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); } else { printf("PWM Base Frequency:%d\n",fan_pwm_basefreq[tindex].human_int); } out: return(err); } int output_emcfan_polarity(int fd, uint8_t product_id, int product_family, uint8_t reg, int the_fan, bool jsonify, bool debug) { int err = 0; uint8_t res; int mask; bool inverted = false; char *s = NULL; mj_t obj; err = emcfan_read_register(fd, reg, &res, debug); if (err != 0) goto out; if (product_id == EMCFAN_PRODUCT_2101 || product_id == EMCFAN_PRODUCT_2101R) { mask = 0x10; } else { mask = 1 << the_fan; } if (res & mask) inverted = true; if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, "fan", "integer", (int64_t)the_fan+1); mj_append_field(&obj, "inverted", "integer", (int64_t)inverted); mj_append_field(&obj, "register", "integer", (int64_t)reg); mj_append_field(&obj, "register_value", "integer", (int64_t)res); mj_asprint(&s, &obj, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); } else { printf("Inverted:%s\n",(inverted ? "Yes" : "No")); } out: return(err); } int output_emcfan_pwm_output_type(int fd, uint8_t product_id, int product_family, uint8_t reg, int the_fan, bool jsonify, bool debug) { int err = 0; uint8_t res; int mask; bool pushpull = false; char *s = NULL; mj_t obj; err = emcfan_read_register(fd, reg, &res, debug); if (err != 0) goto out; mask = 1 << the_fan; if (res & mask) pushpull= true; if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, "fan", "integer", (int64_t)the_fan+1); mj_append_field(&obj, "pwm_output_type", "integer", (int64_t)pushpull); mj_append_field(&obj, "register", "integer", (int64_t)reg); mj_append_field(&obj, "register_value", "integer", (int64_t)res); mj_asprint(&s, &obj, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); } else { printf("PWM Output Type:%s\n",(pushpull ? "push-pull" : "open drain")); } out: return(err); } int output_emcfan_fan_status(int fd, uint8_t product_id, int product_family, uint8_t start_reg, uint8_t end_reg, int the_fan, bool jsonify, bool debug) { int err = 0; uint8_t res[4]; bool stalled = false; bool spin_up_fail = false; bool drive_fail = false; uint8_t stall_mask = 0; uint8_t spin_mask = 0; uint8_t drive_mask = 0; char *s = NULL; mj_t obj; res[0] = res[1] = res[2] = res[3] = 0; if (product_family == EMCFAN_FAMILY_210X) { err = emcfan_read_register(fd, start_reg, &res[0], debug); if (err != 0) goto out; err = emcfan_read_register(fd, start_reg, &res[0], debug); if (err != 0) goto out; switch(the_fan) { case 0: stall_mask = 0b00000001; spin_mask = 0b00000010; drive_mask = 0b00100000; break; case 1: stall_mask = 0b00000100; spin_mask = 0b00001000; drive_mask = 0b01000000; break; default: fprintf(stderr,"No status for fan: %d\n", the_fan + 1); err = EINVAL; }; if (debug) fprintf(stderr,"%s: product_family=%d, stall_mask=0x%02X, spin_mask=0x%02X, drive_mask=0x%02X, res=0x%02X\n",__func__, product_family, stall_mask, spin_mask, drive_mask, res[0]); stalled = (res[0] & stall_mask); spin_up_fail = (res[0] & spin_mask); drive_fail = (res[0] & drive_mask); } else { int j = 0; for(uint8_t i = start_reg; i <= end_reg;i++,j++) { err = emcfan_read_register(fd, i, &res[j], debug); if (err != 0) goto out; } j = 0; for(uint8_t i = start_reg; i <= end_reg;i++,j++) { err = emcfan_read_register(fd, i, &res[j], debug); if (err != 0) goto out; } if (debug) fprintf(stderr,"%s: product_family=%d, res[0]=0x%02X, res[1]=0x%02X, res[2]=0x%02X, res[3]=0x%02X\n", __func__, product_family, res[0], res[1], res[2], res[3]); stalled = (res[1] & (1 << the_fan)); spin_up_fail = (res[2] & (1 << the_fan)); drive_fail = (res[3] & (1 << the_fan)); } if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, "fan", "integer", (int64_t)the_fan+1); mj_append_field(&obj, "stalled", "integer", (int64_t)stalled); mj_append_field(&obj, "spin_up_fail", "integer", (int64_t)spin_up_fail); mj_append_field(&obj, "drive_fail", "integer", (int64_t)drive_fail); mj_append_field(&obj, "register1", "integer", (int64_t)start_reg); mj_append_field(&obj, "register1_value", "integer", (int64_t)res[0]); mj_append_field(&obj, "register2", "integer", (int64_t)start_reg+1); mj_append_field(&obj, "register2_value", "integer", (int64_t)res[1]); mj_append_field(&obj, "register3", "integer", (int64_t)start_reg+2); mj_append_field(&obj, "register3_value", "integer", (int64_t)res[2]); mj_append_field(&obj, "register4", "integer", (int64_t)start_reg+3); mj_append_field(&obj, "register4_value", "integer", (int64_t)res[3]); mj_asprint(&s, &obj, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); } else { printf("Stalled: %s\n",stalled ? "Yes" : "No"); printf("Spin up failed: %s\n",spin_up_fail ? "Yes" : "No"); printf("Drive failed: %s\n",drive_fail ? "Yes" : "No"); } out: return(err); } int output_emcfan_apd(int fd, uint8_t product_id, int product_family, uint8_t reg, bool jsonify, bool debug) { int err = 0; uint8_t res; bool antiparalleldiode = false; char *s = NULL; mj_t obj; err = emcfan_read_register(fd, reg, &res, debug); if (err != 0) goto out; if (res & 0x01) antiparalleldiode = true; if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, "apd", "integer", (int64_t)antiparalleldiode); mj_append_field(&obj, "register", "integer", (int64_t)reg); mj_append_field(&obj, "register_value", "integer", (int64_t)res); mj_asprint(&s, &obj, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); } else { printf("APD:%s\n",(antiparalleldiode ? "On" : "Off")); } out: return(err); } int output_emcfan_smbusto(int fd, uint8_t product_id, int product_family, uint8_t reg, int instance, bool jsonify, bool debug) { int err = 0; uint8_t res; int tindex; bool smbusto = false; char *s = NULL; mj_t obj; err = emcfan_read_register(fd, reg, &res, debug); if (err != 0) goto out; tindex = find_translated_blob_by_bits_instance(smbus_timeout, __arraycount(smbus_timeout), res, instance); if (debug) fprintf(stderr,"%s: reg=%d 0x%02X, res=0x%02x, tindex=%d, instance=%d\n",__func__,reg,reg,res,tindex,instance); /* The logic is inverted for the timeout */ smbusto = (res & smbus_timeout[tindex].clear_mask) ? false : true; if (jsonify) { memset(&obj, 0x0, sizeof(obj)); mj_create(&obj, "object"); mj_append_field(&obj, "smbus_timeout", "integer", (int64_t)smbusto); mj_append_field(&obj, "register", "integer", (int64_t)reg); mj_append_field(&obj, "register_value", "integer", (int64_t)res); mj_asprint(&s, &obj, MJ_JSON_ENCODE); printf("%s",s); if (s != NULL) free(s); } else { printf("SMBUS timeout:%s\n",(smbusto ? "On" : "Off")); } out: return(err); }