Home | History | Annotate | Line # | Download | only in emcfanctl
      1 /*	$NetBSD: emcfanctloutputs.c,v 1.2 2025/03/12 14:01:49 brad Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2025 Brad Spencer <brad (at) anduin.eldar.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #ifdef __RCSID
     20 __RCSID("$NetBSD: emcfanctloutputs.c,v 1.2 2025/03/12 14:01:49 brad Exp $");
     21 #endif
     22 
     23 #include <inttypes.h>
     24 #include <stdbool.h>
     25 #include <stdio.h>
     26 #include <stdlib.h>
     27 #include <unistd.h>
     28 #include <err.h>
     29 #include <fcntl.h>
     30 #include <string.h>
     31 #include <limits.h>
     32 #include <errno.h>
     33 #include <mj.h>
     34 
     35 #include <dev/i2c/emcfanreg.h>
     36 #include <dev/i2c/emcfaninfo.h>
     37 
     38 #define EXTERN
     39 #include "emcfanctl.h"
     40 #include "emcfanctlconst.h"
     41 #include "emcfanctlutil.h"
     42 #include "emcfanctloutputs.h"
     43 
     44 int
     45 output_emcfan_info(int fd, uint8_t product_id, int product_family, bool jsonify, bool debug)
     46 {
     47 	int err = 0;
     48 	uint8_t res;
     49 	mj_t obj;
     50 	char *s = NULL;
     51 	char *pn;
     52 	char fn[8];
     53 
     54 	err = emcfan_read_register(fd, EMCFAN_REVISION, &res, debug);
     55 	if (err != 0)
     56 		goto out;
     57 
     58 	if (jsonify) {
     59 		memset(&obj, 0x0, sizeof(obj));
     60 		mj_create(&obj, "object");
     61 		mj_append_field(&obj, "revision", "integer", (int64_t)res);
     62 		mj_append_field(&obj, "product_id", "integer", (int64_t)product_id);
     63 		mj_append_field(&obj, "product_family", "integer", (int64_t)product_family);
     64 		pn = emcfan_product_to_name(product_id);
     65 		mj_append_field(&obj, "chip_name", "string", pn, strlen(pn));
     66 		emcfan_family_to_name(product_family, fn, sizeof(fn));
     67 		mj_append_field(&obj, "family_name", "string", fn, strlen(fn));
     68 		mj_asprint(&s, &obj, MJ_JSON_ENCODE);
     69 		printf("%s",s);
     70 		if (s != NULL)
     71 			free(s);
     72 	} else {
     73 		emcfan_family_to_name(product_family, fn, sizeof(fn));
     74 		printf("Product Family: %s\n", fn);
     75 		printf("Chip name: %s\n", emcfan_product_to_name(product_id));
     76 		printf("Revision: %d\n", res);
     77 	}
     78 
     79  out:
     80 	return(err);
     81 }
     82 
     83 static void
     84 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)
     85 {
     86 	mj_t array;
     87 	mj_t obj;
     88 	char *s = NULL;
     89 	int iindex;
     90 
     91 	iindex = emcfan_find_info(product_id);
     92 	if (iindex == -1) {
     93 		printf("Unknown info for product_id: %d\n",product_id);
     94 		exit(2);
     95 	}
     96 
     97 	if (debug)
     98 		fprintf(stderr, "output_emcfan_generic_reg_list: iindex=%d\n",iindex);
     99 
    100 	if (jsonify) {
    101 		memset(&array, 0x0, sizeof(array));
    102 		mj_create(&array, "array");
    103 	}
    104 
    105 	for(long unsigned int i = 0;i < the_registers_size;i++) {
    106 		if (emcfan_reg_is_real(iindex, the_registers[i].reg)) {
    107 			if (jsonify) {
    108 				memset(&obj, 0x0, sizeof(obj));
    109 				mj_create(&obj, "object");
    110 				mj_append_field(&obj, "register_name", "string", the_registers[i].name, strlen(the_registers[i].name));
    111 				mj_append_field(&obj, "register", "integer", (int64_t)the_registers[i].reg);
    112 				mj_append(&array, "object", &obj);
    113 				mj_delete(&obj);
    114 			} else {
    115 				printf("%s\t%d\t0x%02X\n",the_registers[i].name,the_registers[i].reg,the_registers[i].reg);
    116 			}
    117 		}
    118 	}
    119 
    120 	if (jsonify) {
    121 		mj_asprint(&s, &array, MJ_JSON_ENCODE);
    122 		printf("%s",s);
    123 		if (s != NULL)
    124 			free(s);
    125 		mj_delete(&array);
    126 	}
    127 }
    128 
    129 void
    130 output_emcfan_register_list(uint8_t product_id, int product_family, bool jsonify, bool debug)
    131 {
    132 	if (debug)
    133 		fprintf(stderr,"output_emcfan_list: product_id=%d, product_family=%d\n",product_id, product_family);
    134 
    135 	switch(product_family) {
    136 	case EMCFAN_FAMILY_210X:
    137 		switch(product_id) {
    138 		case EMCFAN_PRODUCT_2101:
    139 		case EMCFAN_PRODUCT_2101R:
    140 			output_emcfan_generic_reg_list(product_id, emcfanctl_2101_registers, __arraycount(emcfanctl_2101_registers), jsonify, debug);
    141 			break;
    142 		case EMCFAN_PRODUCT_2103_1:
    143 			output_emcfan_generic_reg_list(product_id, emcfanctl_2103_1_registers, __arraycount(emcfanctl_2103_1_registers), jsonify, debug);
    144 			break;
    145 		case EMCFAN_PRODUCT_2103_24:
    146 			output_emcfan_generic_reg_list(product_id, emcfanctl_2103_24_registers, __arraycount(emcfanctl_2103_24_registers), jsonify, debug);
    147 			break;
    148 		case EMCFAN_PRODUCT_2104:
    149 			output_emcfan_generic_reg_list(product_id, emcfanctl_2104_registers, __arraycount(emcfanctl_2104_registers), jsonify, debug);
    150 			break;
    151 		case EMCFAN_PRODUCT_2106:
    152 			output_emcfan_generic_reg_list(product_id, emcfanctl_2106_registers, __arraycount(emcfanctl_2106_registers), jsonify, debug);
    153 			break;
    154 		default:
    155 			printf("UNSUPPORTED YET %d\n",product_id);
    156 			exit(99);
    157 			break;
    158 		};
    159 		break;
    160 	case EMCFAN_FAMILY_230X:
    161 		output_emcfan_generic_reg_list(product_id, emcfanctl_230x_registers, __arraycount(emcfanctl_230x_registers), jsonify, debug);
    162 		break;
    163 	};
    164 }
    165 
    166 static int
    167 output_emcfan_230x_read_reg(int fd, uint8_t product_id, int product_family, uint8_t start, uint8_t end, bool jsonify, bool debug)
    168 {
    169 	int err = 0;
    170 	uint8_t res;
    171 	mj_t array;
    172 	mj_t obj;
    173 	char *s = NULL;
    174 	int iindex;
    175 	const char *rn;
    176 
    177 	iindex = emcfan_find_info(product_id);
    178 	if (iindex == -1) {
    179 		printf("Unknown info for product_id: %d\n",product_id);
    180 		exit(2);
    181 	}
    182 
    183 	if (debug)
    184 		fprintf(stderr, "output_emcfan_230x_read_reg: product_id=%d, product_family=%d, iindex=%d\n",product_id, product_family, iindex);
    185 
    186 	if (jsonify) {
    187 		memset(&array, 0x0, sizeof(array));
    188 		mj_create(&array, "array");
    189 	}
    190 
    191 	for(int i = start; i <= end; i++) {
    192 		if (emcfan_reg_is_real(iindex, i)) {
    193 			err = emcfan_read_register(fd, i, &res, debug);
    194 			if (err != 0)
    195 				break;
    196 			if (jsonify) {
    197 				memset(&obj, 0x0, sizeof(obj));
    198 				mj_create(&obj, "object");
    199 				rn = emcfan_regname_by_reg(product_id, product_family, i);
    200 				mj_append_field(&obj, "register_name", "string", rn, strlen(rn));
    201 				mj_append_field(&obj, "register", "integer", (int64_t)i);
    202 				mj_append_field(&obj, "register_value", "integer", (int64_t)res);
    203 				mj_append(&array, "object", &obj);
    204 				mj_delete(&obj);
    205 			} else {
    206 				printf("%s;%d (0x%02X);%d (0x%02X)\n",emcfan_regname_by_reg(product_id, product_family, i),i,i,res,res);
    207 			}
    208 		}
    209 	}
    210 
    211 	if (jsonify) {
    212 		mj_asprint(&s, &array, MJ_JSON_ENCODE);
    213 		printf("%s",s);
    214 		if (s != NULL)
    215 			free(s);
    216 		mj_delete(&array);
    217 	}
    218 
    219 	return(err);
    220 }
    221 
    222 int
    223 output_emcfan_register_read(int fd, uint8_t product_id, int product_family, uint8_t start, uint8_t end, bool jsonify, bool debug)
    224 {
    225 	int err = 0;
    226 
    227 	if (debug)
    228 		fprintf(stderr,"output_emcfan_register_read: start=%d 0x%02X, end=%d 0x%02X\n",start, start, end, end);
    229 
    230 	switch(product_family) {
    231 	case EMCFAN_FAMILY_210X:
    232 		err = output_emcfan_230x_read_reg(fd, product_id, product_family, start, end, jsonify, debug);
    233 		break;
    234 	case EMCFAN_FAMILY_230X:
    235 		err = output_emcfan_230x_read_reg(fd, product_id, product_family, start, end, jsonify, debug);
    236 		break;
    237 	};
    238 
    239 	return(err);
    240 }
    241 
    242 int
    243 output_emcfan_minexpected_rpm(int fd, uint8_t product_id, int product_family, uint8_t config_reg, bool jsonify, bool debug)
    244 {
    245 	int err = 0;
    246 	uint8_t raw_res, res;
    247 	uint8_t clear_mask;
    248 	int human_value;
    249 	char *s = NULL;
    250 	mj_t obj;
    251 
    252 	err = emcfan_read_register(fd, config_reg, &raw_res, debug);
    253 	if (err != 0)
    254 		goto out;
    255 
    256 	clear_mask = fan_minexpectedrpm[0].clear_mask;
    257 	res = raw_res & clear_mask;
    258 	if (debug)
    259 		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);
    260 	human_value = find_human_int(fan_minexpectedrpm, __arraycount(fan_minexpectedrpm), res);
    261 
    262 	if (human_value == -10191)
    263 		return(EINVAL);
    264 
    265 	if (jsonify) {
    266 		memset(&obj, 0x0, sizeof(obj));
    267 		mj_create(&obj, "object");
    268 		mj_append_field(&obj, "minimum_expected_rpm", "integer", (int64_t)human_value);
    269 		mj_append_field(&obj, "register", "integer", (int64_t)config_reg);
    270 		mj_append_field(&obj, "register_value", "integer", (int64_t)raw_res);
    271 		mj_asprint(&s, &obj, MJ_JSON_ENCODE);
    272 		printf("%s",s);
    273 		if (s != NULL)
    274 			free(s);
    275 	} else {
    276 		printf("Minumum expected rpm:%d\n",human_value);
    277 	}
    278 
    279  out:
    280 	return(err);
    281 }
    282 
    283 int
    284 output_emcfan_edges(int fd, uint8_t product_id, int product_family, uint8_t config_reg, bool jsonify, bool debug)
    285 {
    286 	int err = 0;
    287 	uint8_t raw_res, res;
    288 	uint8_t clear_mask;
    289 	int human_value;
    290 	char *s = NULL;
    291 	mj_t obj;
    292 
    293 	err = emcfan_read_register(fd, config_reg, &raw_res, debug);
    294 	if (err != 0)
    295 		goto out;
    296 
    297 	clear_mask = fan_numedges[0].clear_mask;
    298 	res = raw_res & clear_mask;
    299 	if (debug)
    300 		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);
    301 	human_value = find_human_int(fan_numedges, __arraycount(fan_numedges), res);
    302 
    303 	if (human_value == -10191)
    304 		return(EINVAL);
    305 
    306 	if (jsonify) {
    307 		memset(&obj, 0x0, sizeof(obj));
    308 		mj_create(&obj, "object");
    309 		mj_append_field(&obj, "num_edges", "integer", (int64_t)human_value);
    310 		mj_append_field(&obj, "register", "integer", (int64_t)config_reg);
    311 		mj_append_field(&obj, "register_value", "integer", (int64_t)raw_res);
    312 		mj_asprint(&s, &obj, MJ_JSON_ENCODE);
    313 		printf("%s",s);
    314 		if (s != NULL)
    315 			free(s);
    316 	} else {
    317 		printf("Number of edges:%d\n",human_value);
    318 	}
    319 
    320  out:
    321 	return(err);
    322 }
    323 
    324 static int
    325 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)
    326 {
    327 	int err = 0;
    328 	uint8_t res;
    329 	char *s = NULL;
    330 	mj_t obj;
    331 
    332 	err = emcfan_read_register(fd, reg, &res, debug);
    333 	if (err != 0)
    334 		goto out;
    335 
    336 	if (jsonify) {
    337 		memset(&obj, 0x0, sizeof(obj));
    338 		mj_create(&obj, "object");
    339 		mj_append_field(&obj, whatj, "integer", (int64_t)res);
    340 		mj_append_field(&obj, "register", "integer", (int64_t)reg);
    341 		mj_append_field(&obj, "register_value", "integer",(int64_t) res);
    342 		mj_asprint(&s, &obj, MJ_JSON_ENCODE);
    343 		printf("%s",s);
    344 		if (s != NULL)
    345 			free(s);
    346 	} else {
    347 		printf("%s:%d\n",what, res);
    348 	}
    349 
    350  out:
    351 	return(err);
    352 }
    353 
    354 int
    355 output_emcfan_drive(int fd, uint8_t product_id, int product_family, uint8_t reg, bool jsonify, bool debug)
    356 {
    357 	return(output_emcfan_simple_int(fd, product_id, product_family, reg, "Drive", "drive_level", jsonify, debug));
    358 }
    359 
    360 int
    361 output_emcfan_divider(int fd, uint8_t product_id, int product_family, uint8_t reg, bool jsonify, bool debug)
    362 {
    363 	return(output_emcfan_simple_int(fd, product_id, product_family, reg, "Divider", "frequency_divider", jsonify, debug));
    364 }
    365 
    366 int
    367 output_emcfan_pwm_basefreq(int fd, uint8_t product_id, int product_family, uint8_t reg, int the_fan, bool jsonify, bool debug)
    368 {
    369 	int err = 0;
    370 	uint8_t res;
    371 	int tindex;
    372 	char *s = NULL;
    373 	mj_t obj;
    374 
    375 	err = emcfan_read_register(fd, reg, &res, debug);
    376 	if (err != 0)
    377 		goto out;
    378 
    379 	tindex = find_translated_blob_by_bits_instance(fan_pwm_basefreq, __arraycount(fan_pwm_basefreq), res, the_fan);
    380 
    381 	if (debug)
    382 		fprintf(stderr,"%s: reg=%d 0x%02X, res=0x%02x, tindex=%d, the_fan=%d\n",__func__,reg,reg,res,tindex,the_fan);
    383 
    384 	if (jsonify) {
    385 		memset(&obj, 0x0, sizeof(obj));
    386 		mj_create(&obj, "object");
    387 		mj_append_field(&obj, "fan", "integer", (int64_t)the_fan+1);
    388 		mj_append_field(&obj, "pwm_base_frequency", "integer", (int64_t)fan_pwm_basefreq[tindex].human_int);
    389 		mj_append_field(&obj, "register", "integer", (int64_t)reg);
    390 		mj_append_field(&obj, "register_value", "integer", (int64_t)res);
    391 		mj_asprint(&s, &obj, MJ_JSON_ENCODE);
    392 		printf("%s",s);
    393 		if (s != NULL)
    394 			free(s);
    395 	} else {
    396 		printf("PWM Base Frequency:%d\n",fan_pwm_basefreq[tindex].human_int);
    397 	}
    398 
    399  out:
    400 	return(err);
    401 }
    402 
    403 int
    404 output_emcfan_polarity(int fd, uint8_t product_id, int product_family, uint8_t reg, int the_fan, bool jsonify, bool debug)
    405 {
    406 	int err = 0;
    407 	uint8_t res;
    408 	int mask;
    409 	bool inverted = false;
    410 	char *s = NULL;
    411 	mj_t obj;
    412 
    413 	err = emcfan_read_register(fd, reg, &res, debug);
    414 	if (err != 0)
    415 		goto out;
    416 
    417 	if (product_id == EMCFAN_PRODUCT_2101 ||
    418 	    product_id == EMCFAN_PRODUCT_2101R) {
    419 		mask = 0x10;
    420 	} else {
    421 		mask = 1 << the_fan;
    422 	}
    423 
    424 	if (res & mask)
    425 		inverted = true;
    426 
    427 	if (jsonify) {
    428 		memset(&obj, 0x0, sizeof(obj));
    429 		mj_create(&obj, "object");
    430 		mj_append_field(&obj, "fan", "integer", (int64_t)the_fan+1);
    431 		mj_append_field(&obj, "inverted", "integer", (int64_t)inverted);
    432 		mj_append_field(&obj, "register", "integer", (int64_t)reg);
    433 		mj_append_field(&obj, "register_value", "integer", (int64_t)res);
    434 		mj_asprint(&s, &obj, MJ_JSON_ENCODE);
    435 		printf("%s",s);
    436 		if (s != NULL)
    437 			free(s);
    438 	} else {
    439 		printf("Inverted:%s\n",(inverted ? "Yes" : "No"));
    440 	}
    441 
    442  out:
    443 	return(err);
    444 }
    445 
    446 int
    447 output_emcfan_pwm_output_type(int fd, uint8_t product_id, int product_family, uint8_t reg, int the_fan, bool jsonify, bool debug)
    448 {
    449 	int err = 0;
    450 	uint8_t res;
    451 	int mask;
    452 	bool pushpull = false;
    453 	char *s = NULL;
    454 	mj_t obj;
    455 
    456 	err = emcfan_read_register(fd, reg, &res, debug);
    457 	if (err != 0)
    458 		goto out;
    459 
    460 	mask = 1 << the_fan;
    461 
    462 	if (res & mask)
    463 		pushpull= true;
    464 
    465 	if (jsonify) {
    466 		memset(&obj, 0x0, sizeof(obj));
    467 		mj_create(&obj, "object");
    468 		mj_append_field(&obj, "fan", "integer", (int64_t)the_fan+1);
    469 		mj_append_field(&obj, "pwm_output_type", "integer", (int64_t)pushpull);
    470 		mj_append_field(&obj, "register", "integer", (int64_t)reg);
    471 		mj_append_field(&obj, "register_value", "integer", (int64_t)res);
    472 		mj_asprint(&s, &obj, MJ_JSON_ENCODE);
    473 		printf("%s",s);
    474 		if (s != NULL)
    475 			free(s);
    476 	} else {
    477 		printf("PWM Output Type:%s\n",(pushpull ? "push-pull" : "open drain"));
    478 	}
    479 
    480  out:
    481 	return(err);
    482 }
    483 
    484 int
    485 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)
    486 {
    487 	int err = 0;
    488 	uint8_t res[4];
    489 	bool stalled = false;
    490 	bool spin_up_fail = false;
    491 	bool drive_fail = false;
    492 	uint8_t stall_mask = 0;
    493 	uint8_t spin_mask = 0;
    494 	uint8_t drive_mask = 0;
    495 	char *s = NULL;
    496 	mj_t obj;
    497 
    498 	res[0] = res[1] = res[2] = res[3] = 0;
    499 
    500 	if (product_family == EMCFAN_FAMILY_210X) {
    501 		err = emcfan_read_register(fd, start_reg, &res[0], debug);
    502 		if (err != 0)
    503 			goto out;
    504 		err = emcfan_read_register(fd, start_reg, &res[0], debug);
    505 		if (err != 0)
    506 			goto out;
    507 
    508 		switch(the_fan) {
    509 		case 0:
    510 			stall_mask = 0b00000001;
    511 			spin_mask = 0b00000010;
    512 			drive_mask = 0b00100000;
    513 			break;
    514 		case 1:
    515 			stall_mask = 0b00000100;
    516 			spin_mask = 0b00001000;
    517 			drive_mask = 0b01000000;
    518 			break;
    519 		default:
    520 			fprintf(stderr,"No status for fan: %d\n", the_fan + 1);
    521 			err = EINVAL;
    522 		};
    523 		if (debug)
    524 			fprintf(stderr,"%s: product_family=%d, stall_mask=0x%02X, spin_mask=0x%02X, drive_mask=0x%02X, res=0x%02X\n",__func__,
    525 			    product_family, stall_mask, spin_mask, drive_mask, res[0]);
    526 		stalled = (res[0] & stall_mask);
    527 		spin_up_fail = (res[0] & spin_mask);
    528 		drive_fail = (res[0] & drive_mask);
    529 	} else {
    530 		int j = 0;
    531 		for(uint8_t i = start_reg; i <= end_reg;i++,j++) {
    532 			err = emcfan_read_register(fd, i, &res[j], debug);
    533 			if (err != 0)
    534 				goto out;
    535 		}
    536 		j = 0;
    537 		for(uint8_t i = start_reg; i <= end_reg;i++,j++) {
    538 			err = emcfan_read_register(fd, i, &res[j], debug);
    539 			if (err != 0)
    540 				goto out;
    541 		}
    542 
    543 		if (debug)
    544 			fprintf(stderr,"%s: product_family=%d, res[0]=0x%02X, res[1]=0x%02X, res[2]=0x%02X, res[3]=0x%02X\n",
    545 			    __func__, product_family, res[0], res[1], res[2], res[3]);
    546 		stalled = (res[1] & (1 << the_fan));
    547 		spin_up_fail = (res[2] & (1 << the_fan));
    548 		drive_fail = (res[3] & (1 << the_fan));
    549 	}
    550 
    551 	if (jsonify) {
    552 		memset(&obj, 0x0, sizeof(obj));
    553 		mj_create(&obj, "object");
    554 		mj_append_field(&obj, "fan", "integer", (int64_t)the_fan+1);
    555 		mj_append_field(&obj, "stalled", "integer", (int64_t)stalled);
    556 		mj_append_field(&obj, "spin_up_fail", "integer", (int64_t)spin_up_fail);
    557 		mj_append_field(&obj, "drive_fail", "integer", (int64_t)drive_fail);
    558 		mj_append_field(&obj, "register1", "integer", (int64_t)start_reg);
    559 		mj_append_field(&obj, "register1_value", "integer", (int64_t)res[0]);
    560 		mj_append_field(&obj, "register2", "integer", (int64_t)start_reg+1);
    561 		mj_append_field(&obj, "register2_value", "integer", (int64_t)res[1]);
    562 		mj_append_field(&obj, "register3", "integer", (int64_t)start_reg+2);
    563 		mj_append_field(&obj, "register3_value", "integer", (int64_t)res[2]);
    564 		mj_append_field(&obj, "register4", "integer", (int64_t)start_reg+3);
    565 		mj_append_field(&obj, "register4_value", "integer", (int64_t)res[3]);
    566 		mj_asprint(&s, &obj, MJ_JSON_ENCODE);
    567 		printf("%s",s);
    568 		if (s != NULL)
    569 			free(s);
    570 	} else {
    571 		printf("Stalled: %s\n",stalled ? "Yes" : "No");
    572 		printf("Spin up failed: %s\n",spin_up_fail ? "Yes" : "No");
    573 		printf("Drive failed: %s\n",drive_fail ? "Yes" : "No");
    574 	}
    575 
    576  out:
    577 	return(err);
    578 }
    579 
    580 int
    581 output_emcfan_apd(int fd, uint8_t product_id, int product_family, uint8_t reg, bool jsonify, bool debug)
    582 {
    583 	int err = 0;
    584 	uint8_t res;
    585 	bool antiparalleldiode = false;
    586 	char *s = NULL;
    587 	mj_t obj;
    588 
    589 	err = emcfan_read_register(fd, reg, &res, debug);
    590 	if (err != 0)
    591 		goto out;
    592 
    593 	if (res & 0x01)
    594 		antiparalleldiode = true;
    595 
    596 	if (jsonify) {
    597 		memset(&obj, 0x0, sizeof(obj));
    598 		mj_create(&obj, "object");
    599 		mj_append_field(&obj, "apd", "integer", (int64_t)antiparalleldiode);
    600 		mj_append_field(&obj, "register", "integer", (int64_t)reg);
    601 		mj_append_field(&obj, "register_value", "integer", (int64_t)res);
    602 		mj_asprint(&s, &obj, MJ_JSON_ENCODE);
    603 		printf("%s",s);
    604 		if (s != NULL)
    605 			free(s);
    606 	} else {
    607 		printf("APD:%s\n",(antiparalleldiode ? "On" : "Off"));
    608 	}
    609 
    610  out:
    611 	return(err);
    612 }
    613 
    614 int
    615 output_emcfan_smbusto(int fd, uint8_t product_id, int product_family, uint8_t reg, int instance, bool jsonify, bool debug)
    616 {
    617 	int err = 0;
    618 	uint8_t res;
    619 	int tindex;
    620 	bool smbusto = false;
    621 	char *s = NULL;
    622 	mj_t obj;
    623 
    624 	err = emcfan_read_register(fd, reg, &res, debug);
    625 	if (err != 0)
    626 		goto out;
    627 
    628 	tindex = find_translated_blob_by_bits_instance(smbus_timeout, __arraycount(smbus_timeout), res, instance);
    629 
    630 	if (debug)
    631 		fprintf(stderr,"%s: reg=%d 0x%02X, res=0x%02x, tindex=%d, instance=%d\n",__func__,reg,reg,res,tindex,instance);
    632 
    633 	/* The logic is inverted for the timeout */
    634 	smbusto = (res & smbus_timeout[tindex].clear_mask) ? false : true;
    635 
    636 	if (jsonify) {
    637 		memset(&obj, 0x0, sizeof(obj));
    638 		mj_create(&obj, "object");
    639 		mj_append_field(&obj, "smbus_timeout", "integer", (int64_t)smbusto);
    640 		mj_append_field(&obj, "register", "integer", (int64_t)reg);
    641 		mj_append_field(&obj, "register_value", "integer", (int64_t)res);
    642 		mj_asprint(&s, &obj, MJ_JSON_ENCODE);
    643 		printf("%s",s);
    644 		if (s != NULL)
    645 			free(s);
    646 	} else {
    647 		printf("SMBUS timeout:%s\n",(smbusto ? "On" : "Off"));
    648 	}
    649 
    650  out:
    651 	return(err);
    652 }
    653 
    654