1 /* $NetBSD: emcfanctl.c,v 1.1 2025/03/11 13:56:48 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 #include <sys/cdefs.h> 20 #ifdef __RCSID 21 __RCSID("$NetBSD: emcfanctl.c,v 1.1 2025/03/11 13:56:48 brad Exp $"); 22 #endif 23 24 /* Main userland program that can interfact with a EMC-210x and EMC-230x 25 * fan controller using emcfan(4). 26 */ 27 28 #include <stdio.h> 29 #include <stdint.h> 30 #include <unistd.h> 31 #include <stdbool.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <inttypes.h> 35 #include <err.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <sys/ioctl.h> 39 40 #include <dev/i2c/emcfanreg.h> 41 #include <dev/i2c/emcfaninfo.h> 42 43 #define EXTERN extern 44 #include "emcfanctl.h" 45 #include "emcfanctlconst.h" 46 #include "emcfanctlutil.h" 47 #include "emcfanctloutputs.h" 48 49 int valid_cmd(const struct emcfanctlcmd[], long unsigned int, char *); 50 51 static void 52 usage(void) 53 { 54 const char *p = getprogname(); 55 56 fprintf(stderr, "Usage: %s [-jdh] device cmd args\n\n", 57 p); 58 59 for(long unsigned int i = 0;i < __arraycount(emcfanctlcmds);i++) { 60 switch (emcfanctlcmds[i].id) { 61 case EMCFANCTL_REGISTER: 62 for(long unsigned int j = 0;j < __arraycount(emcfanctlregistercmds);j++) { 63 fprintf(stderr,"%s [-jdh] device %s %s\n", 64 p, 65 emcfanctlcmds[i].cmd, 66 emcfanctlregistercmds[j].helpargs); 67 } 68 break; 69 case EMCFANCTL_FAN: 70 for(long unsigned int j = 0;j < __arraycount(emcfanctlfancmds);j++) { 71 switch (emcfanctlfancmds[j].id) { 72 case EMCFANCTL_FAN_DRIVE: 73 case EMCFANCTL_FAN_DIVIDER: 74 for(long unsigned int k = 0;k < __arraycount(emcfanctlddcmds);k++) { 75 fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n", 76 p, 77 emcfanctlcmds[i].cmd, 78 emcfanctlfancmds[j].cmd, 79 emcfanctlddcmds[k].helpargs); 80 } 81 break; 82 case EMCFANCTL_FAN_MINEXPECTED_RPM: 83 for(long unsigned int k = 0;k < __arraycount(emcfanctlddcmds);k++) { 84 if (emcfanctlddcmds[k].id != EMCFANCTL_FAN_DD_WRITE) { 85 fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n", 86 p, 87 emcfanctlcmds[i].cmd, 88 emcfanctlfancmds[j].cmd, 89 emcfanctlddcmds[k].helpargs); 90 } else { 91 fprintf(stderr,"%s [-jdh] device %s <n> %s %s ", 92 p, 93 emcfanctlcmds[i].cmd, 94 emcfanctlfancmds[j].cmd, 95 emcfanctlddcmds[k].helpargs); 96 for(long unsigned int q = 0;q < __arraycount(fan_minexpectedrpm);q++) { 97 fprintf(stderr,"%d|",fan_minexpectedrpm[q].human_int); 98 } 99 fprintf(stderr,"\n"); 100 } 101 } 102 break; 103 case EMCFANCTL_FAN_EDGES: 104 for(long unsigned int k = 0;k < __arraycount(emcfanctlddcmds);k++) { 105 if (emcfanctlddcmds[k].id != EMCFANCTL_FAN_DD_WRITE) { 106 fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n", 107 p, 108 emcfanctlcmds[i].cmd, 109 emcfanctlfancmds[j].cmd, 110 emcfanctlddcmds[k].helpargs); 111 } else { 112 fprintf(stderr,"%s [-jdh] device %s <n> %s %s ", 113 p, 114 emcfanctlcmds[i].cmd, 115 emcfanctlfancmds[j].cmd, 116 emcfanctlddcmds[k].helpargs); 117 for(long unsigned int q = 0;q < __arraycount(fan_numedges);q++) { 118 fprintf(stderr,"%d|",fan_numedges[q].human_int); 119 } 120 fprintf(stderr,"\n"); 121 } 122 } 123 break; 124 case EMCFANCTL_FAN_POLARITY: 125 for(long unsigned int k = 0;k < __arraycount(emcfanctlpcmds);k++) { 126 fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n", 127 p, 128 emcfanctlcmds[i].cmd, 129 emcfanctlfancmds[j].cmd, 130 emcfanctlpcmds[k].helpargs); 131 } 132 break; 133 case EMCFANCTL_FAN_PWM_BASEFREQ: 134 for(long unsigned int k = 0;k < __arraycount(emcfanctlddcmds);k++) { 135 if (emcfanctlddcmds[k].id != EMCFANCTL_FAN_DD_WRITE) { 136 fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n", 137 p, 138 emcfanctlcmds[i].cmd, 139 emcfanctlfancmds[j].cmd, 140 emcfanctlddcmds[k].helpargs); 141 } else { 142 fprintf(stderr,"%s [-jdh] device %s <n> %s %s ", 143 p, 144 emcfanctlcmds[i].cmd, 145 emcfanctlfancmds[j].cmd, 146 emcfanctlddcmds[k].helpargs); 147 for(long unsigned int q = 0;q < __arraycount(fan_pwm_basefreq);q++) { 148 if (fan_pwm_basefreq[q].instance > 0) 149 break; 150 fprintf(stderr,"%d|",fan_pwm_basefreq[q].human_int); 151 } 152 fprintf(stderr,"\n"); 153 } 154 } 155 break; 156 case EMCFANCTL_FAN_PWM_OUTPUTTYPE: 157 for(long unsigned int k = 0;k < __arraycount(emcfanctlotcmds);k++) { 158 fprintf(stderr,"%s [-jdh] device %s <n> %s %s\n", 159 p, 160 emcfanctlcmds[i].cmd, 161 emcfanctlfancmds[j].cmd, 162 emcfanctlotcmds[k].helpargs); 163 } 164 break; 165 default: 166 fprintf(stderr,"%s [-jdh] device %s %s\n", 167 p, 168 emcfanctlcmds[i].cmd, 169 emcfanctlfancmds[j].helpargs); 170 break; 171 }; 172 } 173 break; 174 case EMCFANCTL_APD: 175 case EMCFANCTL_SMBUSTO: 176 for(long unsigned int j = 0;j < __arraycount(emcfanctlapdsmtocmds);j++) { 177 fprintf(stderr,"%s [-jdh] device %s %s\n", 178 p, 179 emcfanctlcmds[i].cmd, 180 emcfanctlapdsmtocmds[j].helpargs); 181 } 182 break; 183 default: 184 fprintf(stderr,"%s [-jdh] device %s %s\n", 185 p,emcfanctlcmds[i].cmd,emcfanctlcmds[i].helpargs); 186 break; 187 }; 188 } 189 } 190 191 int 192 valid_cmd(const struct emcfanctlcmd c[], long unsigned int csize, char *cmdtocheck) 193 { 194 int r = -1; 195 196 for(long unsigned int i = 0;i < csize;i++) { 197 if (strncmp(cmdtocheck,c[i].cmd,16) == 0) { 198 r = i; 199 break; 200 } 201 } 202 203 return r; 204 } 205 206 int 207 main(int argc, char *argv[]) 208 { 209 int c; 210 bool debug = false; 211 uint8_t product_id; 212 int product_family; 213 int fd = -1, valid, error = 0, validsub = -1, validsubsub = -1; 214 bool jsonify = false; 215 int start_reg = 0xff, end_reg, value, tvalue, instance; 216 int the_fan; 217 218 while ((c = getopt(argc, argv, "djh")) != -1 ) { 219 switch (c) { 220 case 'd': 221 debug = true; 222 break; 223 case 'j': 224 jsonify = true; 225 break; 226 case 'h': 227 default: 228 usage(); 229 exit(0); 230 } 231 } 232 233 argc -= optind; 234 argv += optind; 235 236 if (debug) { 237 fprintf(stderr,"ARGC: %d\n", argc); 238 fprintf(stderr,"ARGV[0]: %s ; ARGV[1]: %s ; ARGV[2]: %s ; ARGV[3]: %s; ARGV[4]: %s; ARGV[5]: %s\n", 239 argv[0],argv[1],argv[2],argv[3],argv[4],argv[5]); 240 } 241 242 if (argc <= 1) { 243 usage(); 244 exit(0); 245 } 246 247 fd = open(argv[0], O_RDWR, 0); 248 if (fd == -1) { 249 err(EXIT_FAILURE, "open %s", argv[0]); 250 } 251 252 error = emcfan_read_register(fd, EMCFAN_PRODUCT_ID, &product_id, debug); 253 254 if (error) 255 err(EXIT_FAILURE, "read product_id %d", error); 256 257 int iindex; 258 iindex = emcfan_find_info(product_id); 259 if (iindex == -1) { 260 printf("Unknown info for product_id: %d\n",product_id); 261 exit(2); 262 } 263 264 product_family = emcfan_chip_infos[iindex].family; 265 266 /* Parse out the command line into what the requested action is */ 267 268 valid = valid_cmd(emcfanctlcmds,__arraycount(emcfanctlcmds),argv[1]); 269 if (valid != -1) { 270 switch (emcfanctlcmds[valid].id) { 271 case EMCFANCTL_INFO: 272 error = output_emcfan_info(fd, product_id, product_family, jsonify, debug); 273 if (error != 0) { 274 errno = error; 275 err(EXIT_FAILURE, "output info"); 276 } 277 break; 278 case EMCFANCTL_APD: 279 if (argc >= 3) { 280 validsub = valid_cmd(emcfanctlapdsmtocmds,__arraycount(emcfanctlapdsmtocmds),argv[2]); 281 if (product_id == EMCFAN_PRODUCT_2103_24 || 282 product_id == EMCFAN_PRODUCT_2104 || 283 product_id == EMCFAN_PRODUCT_2106) { 284 switch(emcfanctlapdsmtocmds[validsub].id) { 285 case EMCFANCTL_APD_READ: 286 error = output_emcfan_apd(fd, product_id, product_family, EMCFAN_CHIP_CONFIG, jsonify, debug); 287 if (error != 0) { 288 fprintf(stderr, "Error output for apd subcommand: %d\n",error); 289 exit(1); 290 } 291 break; 292 case EMCFANCTL_APD_ON: 293 case EMCFANCTL_APD_OFF: 294 tvalue = find_translated_bits_by_str(apd, __arraycount(apd), argv[2]); 295 if (tvalue < 0) { 296 fprintf(stderr,"Error converting human value: %s\n", argv[4]); 297 exit(1); 298 } 299 error = emcfan_rmw_register(fd, EMCFAN_CHIP_CONFIG, 0, apd, __arraycount(apd), tvalue, debug); 300 if (error != 0) { 301 errno = error; 302 err(EXIT_FAILURE, "read/modify/write register"); 303 } 304 break; 305 default: 306 fprintf(stderr,"Unhandled subcommand to apd: %s %d\n\n", argv[2], validsub); 307 usage(); 308 exit(1); 309 break; 310 }; 311 } else { 312 errno = EINVAL; 313 err(EXIT_FAILURE, "This chip does not support the APD command"); 314 } 315 } else { 316 fprintf(stderr,"Missing arguments to apd command\n\n"); 317 usage(); 318 exit(1); 319 } 320 break; 321 case EMCFANCTL_SMBUSTO: 322 if (argc >= 3) { 323 if (product_id == EMCFAN_PRODUCT_2101 || 324 product_id == EMCFAN_PRODUCT_2101R) { 325 start_reg = EMCFAN_2101_CHIP_CONFIG; 326 instance = 2101; 327 } else { 328 if (product_family == EMCFAN_FAMILY_230X) { 329 start_reg = EMCFAN_CHIP_CONFIG; 330 instance = 2301; 331 } else { 332 start_reg = EMCFAN_CHIP_CONFIG_2; 333 instance = 2103; 334 } 335 } 336 337 validsub = valid_cmd(emcfanctlapdsmtocmds,__arraycount(emcfanctlapdsmtocmds),argv[2]); 338 switch(emcfanctlapdsmtocmds[validsub].id) { 339 case EMCFANCTL_APD_READ: 340 error = output_emcfan_smbusto(fd, product_id, product_family, start_reg, instance, jsonify, debug); 341 if (error != 0) { 342 fprintf(stderr, "Error output for SMBUS timeout subcommand: %d\n",error); 343 exit(1); 344 } 345 break; 346 case EMCFANCTL_APD_ON: 347 case EMCFANCTL_APD_OFF: 348 tvalue = find_translated_bits_by_str_instance(smbus_timeout, __arraycount(smbus_timeout), argv[2], instance); 349 if (tvalue < 0) { 350 fprintf(stderr,"Error converting human value: %s\n", argv[4]); 351 exit(1); 352 } 353 error = emcfan_rmw_register(fd, start_reg, 0, smbus_timeout, __arraycount(smbus_timeout), tvalue, debug); 354 if (error != 0) { 355 errno = error; 356 err(EXIT_FAILURE, "read/modify/write register"); 357 } 358 break; 359 default: 360 fprintf(stderr,"Unhandled subcommand to SMBUS timeout: %s %d\n\n", argv[2], validsub); 361 usage(); 362 exit(1); 363 break; 364 }; 365 } else { 366 fprintf(stderr,"Missing arguments to SMBUS timeout command\n\n"); 367 usage(); 368 exit(1); 369 } 370 break; 371 case EMCFANCTL_REGISTER: 372 if (argc >= 3) { 373 validsub = valid_cmd(emcfanctlregistercmds,__arraycount(emcfanctlregistercmds),argv[2]); 374 switch (emcfanctlregistercmds[validsub].id) { 375 case EMCFANCTL_REGISTER_LIST: 376 output_emcfan_register_list(product_id, product_family, jsonify, debug); 377 break; 378 case EMCFANCTL_REGISTER_READ: 379 start_reg = end_reg = 0x00; 380 if (argc >= 4) { 381 start_reg = (uint8_t)strtoi(argv[3], NULL, 0, 0, 0xff, &error); 382 if (error) { 383 start_reg = emcfan_reg_by_name(product_id, product_family, argv[3]); 384 if (start_reg == -1) { 385 fprintf(stderr,"Bad conversion for read register start: %s\n", argv[3]); 386 exit(1); 387 } 388 } 389 end_reg = start_reg; 390 if (argc == 5) { 391 end_reg = (uint8_t)strtoi(argv[4], NULL, 0, 0, 0xff, &error); 392 if (error) { 393 end_reg = emcfan_reg_by_name(product_id, product_family, argv[4]); 394 if (end_reg == -1) { 395 fprintf(stderr,"Bad conversion for read register end: %s\n", argv[4]); 396 exit(1); 397 } 398 } 399 } 400 } else { 401 end_reg = 0xff; 402 } 403 if (end_reg < start_reg) { 404 fprintf(stderr,"Register end can not be less than register start: %d %d\n\n", start_reg, end_reg); 405 usage(); 406 exit(1); 407 } 408 409 if (debug) 410 fprintf(stderr,"register read: START_REG: %d 0x%02X, END_REG: %d 0x%02X\n",start_reg, start_reg, end_reg, end_reg); 411 412 error = output_emcfan_register_read(fd, product_id, product_family, start_reg, end_reg, jsonify, debug); 413 if (error != 0) { 414 fprintf(stderr, "Error read register: %d\n",error); 415 exit(1); 416 } 417 418 break; 419 case EMCFANCTL_REGISTER_WRITE: 420 if (argc == 5) { 421 start_reg = (uint8_t)strtoi(argv[3], NULL, 0, 0, 0xff, &error); 422 if (error) { 423 start_reg = emcfan_reg_by_name(product_id, product_family, argv[3]); 424 if (start_reg == -1) { 425 fprintf(stderr,"Bad conversion for write register: %s\n", argv[3]); 426 exit(1); 427 } 428 } 429 430 value = (uint8_t)strtoi(argv[4], NULL, 0, 0, 0xff, &error); 431 if (error) { 432 fprintf(stderr,"Could not convert value for register write.\n"); 433 usage(); 434 exit(1); 435 } 436 } else { 437 fprintf(stderr,"Not enough arguments for register write.\n"); 438 usage(); 439 exit(1); 440 } 441 442 if (debug) 443 fprintf(stderr,"register write: START_REG: %d 0x%02X, VALUE: %d 0x%02X\n",start_reg, start_reg, value, value); 444 445 error = emcfan_write_register(fd, start_reg, value, debug); 446 if (error != 0) { 447 errno = error; 448 err(EXIT_FAILURE, "write register"); 449 } 450 451 break; 452 default: 453 fprintf(stderr,"Unhandled subcommand to register: %s %d\n\n", argv[2], validsub); 454 usage(); 455 exit(1); 456 break; 457 }; 458 } else { 459 fprintf(stderr,"Missing arguments to register command\n\n"); 460 usage(); 461 exit(1); 462 } 463 break; 464 case EMCFANCTL_FAN: 465 if (argc >= 4) { 466 the_fan = strtoi(argv[2], NULL, 0, 1, emcfan_chip_infos[iindex].num_fans, &error); 467 if (error) { 468 fprintf(stderr,"Bad conversion for fan number: %s\n", argv[2]); 469 exit(1); 470 } 471 the_fan--; 472 validsub = valid_cmd(emcfanctlfancmds,__arraycount(emcfanctlfancmds),argv[3]); 473 switch (emcfanctlfancmds[validsub].id) { 474 case EMCFANCTL_FAN_STATUS: 475 if (product_id != EMCFAN_PRODUCT_2101 && 476 product_id != EMCFAN_PRODUCT_2101R) { 477 if (product_family == EMCFAN_FAMILY_230X) { 478 start_reg = EMCFAN_230X_FAN_STATUS; 479 end_reg = EMCFAN_230X_FAN_DRIVE_STATUS; 480 } else { 481 start_reg = EMCFAN_210_346_FAN_STATUS; 482 end_reg = 0xff; 483 } 484 } else { 485 errno = EINVAL; 486 err(EXIT_FAILURE, "EMC2101 and EMC2101R do not support this subcommand"); 487 } 488 break; 489 case EMCFANCTL_FAN_DRIVE: 490 start_reg = emcfan_chip_infos[iindex].fan_drive_registers[the_fan]; 491 break; 492 case EMCFANCTL_FAN_DIVIDER: 493 start_reg = emcfan_chip_infos[iindex].fan_divider_registers[the_fan]; 494 break; 495 case EMCFANCTL_FAN_MINEXPECTED_RPM: 496 case EMCFANCTL_FAN_EDGES: 497 if (product_id != EMCFAN_PRODUCT_2101 && 498 product_id != EMCFAN_PRODUCT_2101R) { 499 if (debug) 500 fprintf(stderr,"fan subcommand minexpected_rpm / edges: the_fan=%d\n",the_fan); 501 if (product_family == EMCFAN_FAMILY_210X) { 502 the_fan = strtoi(argv[2], NULL, 0, 1, emcfan_chip_infos[iindex].num_tachs, &error); 503 if (error) { 504 fprintf(stderr,"Bad conversion for fan number: %s\n", argv[2]); 505 exit(1); 506 } 507 the_fan--; 508 switch(the_fan) { 509 case 0: 510 start_reg = EMCFAN_210_346_CONFIG_1; 511 break; 512 case 1: 513 start_reg = EMCFAN_210_346_CONFIG_2; 514 break; 515 default: 516 start_reg = 0xff; 517 break; 518 }; 519 } else { 520 switch(the_fan) { 521 case 0: 522 start_reg = EMCFAN_230X_CONFIG_1; 523 break; 524 case 1: 525 start_reg = EMCFAN_230X_CONFIG_2; 526 break; 527 case 2: 528 start_reg = EMCFAN_230X_CONFIG_3; 529 break; 530 case 3: 531 start_reg = EMCFAN_230X_CONFIG_4; 532 break; 533 case 4: 534 start_reg = EMCFAN_230X_CONFIG_5; 535 break; 536 default: 537 start_reg = 0xff; 538 break; 539 }; 540 } 541 } else { 542 errno = EINVAL; 543 err(EXIT_FAILURE, "EMC2101 and EMC2101R do not support this subcommand"); 544 } 545 break; 546 case EMCFANCTL_FAN_POLARITY: 547 if (product_id != EMCFAN_PRODUCT_2101 && 548 product_id != EMCFAN_PRODUCT_2101R) { 549 start_reg = EMCFAN_POLARITY_CONFIG; 550 } else { 551 start_reg = EMCFAN_2101_FAN_CONFIG; 552 } 553 break; 554 case EMCFANCTL_FAN_PWM_BASEFREQ: 555 if (product_id != EMCFAN_PRODUCT_2101 && 556 product_id != EMCFAN_PRODUCT_2101R) { 557 if (product_family == EMCFAN_FAMILY_210X) 558 start_reg = EMCFAN_210_346_PWM_BASEFREQ; 559 else 560 if (the_fan <= 2) 561 start_reg = EMCFAN_230X_BASE_FREQ_123; 562 else 563 start_reg = EMCFAN_230X_BASE_FREQ_45; 564 } else { 565 errno = EINVAL; 566 err(EXIT_FAILURE, "EMC2101 and EMC2101R do not support this subcommand"); 567 } 568 break; 569 case EMCFANCTL_FAN_PWM_OUTPUTTYPE: 570 if (product_family == EMCFAN_FAMILY_230X) { 571 start_reg = EMCFAN_230X_OUTPUT_CONFIG; 572 } else { 573 errno = EINVAL; 574 err(EXIT_FAILURE, "This subcommand is only supported on the EMC230X family"); 575 } 576 break; 577 default: 578 fprintf(stderr,"Unhandled subcommand to fan: %s %d\n\n", argv[3], validsub); 579 usage(); 580 exit(1); 581 break; 582 }; 583 if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_STATUS) { 584 error = output_emcfan_fan_status(fd, product_id, product_family, start_reg, end_reg, the_fan, jsonify, debug); 585 if (error != 0) { 586 fprintf(stderr, "Error fan status for fan subcommand: %d\n",error); 587 exit(1); 588 } 589 } 590 if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_DRIVE || 591 emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_DIVIDER) { 592 validsubsub = valid_cmd(emcfanctlddcmds,__arraycount(emcfanctlddcmds),argv[4]); 593 switch(emcfanctlddcmds[validsubsub].id) { 594 case EMCFANCTL_FAN_DD_READ: 595 if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_DRIVE) 596 error = output_emcfan_drive(fd, product_id, product_family, start_reg, jsonify, debug); 597 else 598 error = output_emcfan_divider(fd, product_id, product_family, start_reg, jsonify, debug); 599 if (error != 0) { 600 fprintf(stderr, "Error read drive / divider for fan subcommand: %d\n",error); 601 exit(1); 602 } 603 break; 604 case EMCFANCTL_FAN_DD_WRITE: 605 value = (uint8_t)strtoi(argv[5], NULL, 0, 0, 0xff, &error); 606 if (error) { 607 fprintf(stderr,"Could not convert value for fan subcommand write.\n"); 608 usage(); 609 exit(1); 610 } 611 error = emcfan_write_register(fd, start_reg, value, debug); 612 if (error != 0) { 613 errno = error; 614 err(EXIT_FAILURE, "write register"); 615 } 616 break; 617 default: 618 fprintf(stderr,"Unhandled subsubcommand to fan: %s %d\n\n", argv[4], validsubsub); 619 usage(); 620 exit(1); 621 break; 622 }; 623 } 624 if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_MINEXPECTED_RPM) { 625 if (start_reg == 0xff) { 626 fprintf(stderr,"fan minexpected rpm subcommand, unknown register\n"); 627 exit(1); 628 } 629 validsubsub = valid_cmd(emcfanctlddcmds,__arraycount(emcfanctlddcmds),argv[4]); 630 switch(emcfanctlddcmds[validsubsub].id) { 631 case EMCFANCTL_FAN_DD_READ: 632 error = output_emcfan_minexpected_rpm(fd, product_id, product_family, start_reg, jsonify, debug); 633 if (error != 0) { 634 fprintf(stderr, "Error read minexpected rpm subcommand: %d\n",error); 635 exit(1); 636 } 637 break; 638 case EMCFANCTL_FAN_DD_WRITE: 639 value = (int)strtoi(argv[5], NULL, 0, 0, 0xffff, &error); 640 if (error) { 641 fprintf(stderr,"Could not convert value for minexpected rpm subcommand write.\n"); 642 usage(); 643 exit(1); 644 } 645 tvalue = find_translated_bits_by_hint(fan_minexpectedrpm, __arraycount(fan_minexpectedrpm), value); 646 if (tvalue < 0) { 647 fprintf(stderr,"Error converting human value: %d %d\n",value, tvalue); 648 exit(1); 649 } 650 error = emcfan_rmw_register(fd, start_reg, value, fan_minexpectedrpm, __arraycount(fan_minexpectedrpm), tvalue, debug); 651 if (error != 0) { 652 errno = error; 653 err(EXIT_FAILURE, "read/modify/write register"); 654 } 655 break; 656 default: 657 fprintf(stderr,"Unhandled subsubcommand to minexpected rpm subcommand: %s %d\n\n", argv[4], validsubsub); 658 usage(); 659 exit(1); 660 break; 661 }; 662 } 663 if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_EDGES) { 664 if (start_reg == 0xff) { 665 fprintf(stderr,"fan edges subcommand, unknown register\n"); 666 exit(1); 667 } 668 validsubsub = valid_cmd(emcfanctlddcmds,__arraycount(emcfanctlddcmds),argv[4]); 669 switch(emcfanctlddcmds[validsubsub].id) { 670 case EMCFANCTL_FAN_DD_READ: 671 error = output_emcfan_edges(fd, product_id, product_family, start_reg, jsonify, debug); 672 if (error != 0) { 673 fprintf(stderr, "Error read edges subcommand: %d\n",error); 674 exit(1); 675 } 676 break; 677 case EMCFANCTL_FAN_DD_WRITE: 678 value = (uint8_t)strtoi(argv[5], NULL, 0, 0, 9, &error); 679 if (error) { 680 fprintf(stderr,"Could not convert value for edges subcommand write.\n"); 681 usage(); 682 exit(1); 683 } 684 tvalue = find_translated_bits_by_hint(fan_numedges, __arraycount(fan_numedges), value); 685 if (tvalue < 0) { 686 fprintf(stderr,"Error converting human value: %d\n",value); 687 exit(1); 688 } 689 error = emcfan_rmw_register(fd, start_reg, value, fan_numedges, __arraycount(fan_numedges), tvalue, debug); 690 if (error != 0) { 691 errno = error; 692 err(EXIT_FAILURE, "read/modify/write register"); 693 } 694 break; 695 default: 696 fprintf(stderr,"Unhandled subsubcommand to minexpected rpm subcommand: %s %d\n\n", argv[4], validsubsub); 697 usage(); 698 exit(1); 699 break; 700 }; 701 } 702 if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_POLARITY) { 703 if (start_reg == 0xff) { 704 fprintf(stderr,"fan polarity subcommand, unknown register\n"); 705 exit(1); 706 } 707 validsubsub = valid_cmd(emcfanctlpcmds,__arraycount(emcfanctlpcmds),argv[4]); 708 switch(emcfanctlpcmds[validsubsub].id) { 709 case EMCFANCTL_FAN_P_READ: 710 error = output_emcfan_polarity(fd, product_id, product_family, start_reg, the_fan, jsonify, debug); 711 if (error != 0) { 712 fprintf(stderr, "Error output for polarity subcommand: %d\n",error); 713 exit(1); 714 } 715 break; 716 case EMCFANCTL_FAN_P_INVERTED: 717 case EMCFANCTL_FAN_P_NONINVERTED: 718 if (product_id == EMCFAN_PRODUCT_2101 || 719 product_id == EMCFAN_PRODUCT_2101R) { 720 tvalue = find_translated_bits_by_str_instance(fan_polarity, __arraycount(fan_polarity), argv[4], 2101); 721 } else { 722 tvalue = find_translated_bits_by_str_instance(fan_polarity, __arraycount(fan_polarity), argv[4], the_fan); 723 } 724 if (tvalue < 0) { 725 fprintf(stderr,"Error converting human value: %s\n", argv[4]); 726 exit(1); 727 } 728 error = emcfan_rmw_register(fd, start_reg, 0, fan_polarity, __arraycount(fan_polarity), tvalue, debug); 729 if (error != 0) { 730 errno = error; 731 err(EXIT_FAILURE, "read/modify/write register"); 732 } 733 break; 734 default: 735 fprintf(stderr,"Unhandled subsubcommand to polarity subcommand: %s %d\n\n", argv[4], validsubsub); 736 usage(); 737 exit(1); 738 break; 739 }; 740 741 } 742 if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_PWM_BASEFREQ) { 743 if (start_reg == 0xff) { 744 fprintf(stderr,"fan base_freq subcommand, unknown register\n"); 745 exit(1); 746 } 747 validsubsub = valid_cmd(emcfanctlddcmds,__arraycount(emcfanctlddcmds),argv[4]); 748 switch(emcfanctlddcmds[validsubsub].id) { 749 case EMCFANCTL_FAN_DD_READ: 750 if (debug) 751 fprintf(stderr,"%s: the_fan=%d\n",__func__,the_fan); 752 if (product_id != EMCFAN_PRODUCT_2305) 753 error = output_emcfan_pwm_basefreq(fd, product_id, product_family, start_reg, the_fan, jsonify, debug); 754 else 755 if (the_fan <= 2) 756 error = output_emcfan_pwm_basefreq(fd, product_id, product_family, start_reg, the_fan, jsonify, debug); 757 else 758 error = output_emcfan_pwm_basefreq(fd, product_id, product_family, start_reg, 759 the_fan + 23050, jsonify, debug); 760 if (error != 0) { 761 fprintf(stderr, "Error read base_freq subcommand: %d\n",error); 762 exit(1); 763 } 764 break; 765 case EMCFANCTL_FAN_DD_WRITE: 766 value = (int)strtoi(argv[5], NULL, 0, 0, 0xffff, &error); 767 if (error) { 768 fprintf(stderr,"Could not convert value for base_freq subcommand write.\n"); 769 usage(); 770 exit(1); 771 } 772 if (product_id != EMCFAN_PRODUCT_2305) 773 tvalue = find_translated_bits_by_hint_instance(fan_pwm_basefreq, __arraycount(fan_pwm_basefreq), value, the_fan); 774 else 775 if (the_fan <= 2) 776 tvalue = find_translated_bits_by_hint_instance(fan_pwm_basefreq, __arraycount(fan_pwm_basefreq), value, the_fan); 777 else 778 tvalue = find_translated_bits_by_hint_instance(fan_pwm_basefreq, __arraycount(fan_pwm_basefreq), 779 value, the_fan + 23050); 780 if (tvalue < 0) { 781 fprintf(stderr,"Error converting human value: %d %d\n",value, tvalue); 782 exit(1); 783 } 784 error = emcfan_rmw_register(fd, start_reg, value, fan_pwm_basefreq, __arraycount(fan_pwm_basefreq), tvalue, debug); 785 if (error != 0) { 786 errno = error; 787 err(EXIT_FAILURE, "read/modify/write register"); 788 } 789 break; 790 default: 791 fprintf(stderr,"Unhandled subsubcommand to base_freq subcommand: %s %d\n\n", argv[4], validsubsub); 792 usage(); 793 exit(1); 794 break; 795 }; 796 break; 797 } 798 if (emcfanctlfancmds[validsub].id == EMCFANCTL_FAN_PWM_OUTPUTTYPE) { 799 if (start_reg == 0xff) { 800 fprintf(stderr,"fan pwm_output_type subcommand, unknown register\n"); 801 exit(1); 802 } 803 validsubsub = valid_cmd(emcfanctlotcmds,__arraycount(emcfanctlotcmds),argv[4]); 804 switch(emcfanctlotcmds[validsubsub].id) { 805 case EMCFANCTL_FAN_OT_READ: 806 error = output_emcfan_pwm_output_type(fd, product_id, product_family, start_reg, the_fan, jsonify, debug); 807 if (error != 0) { 808 fprintf(stderr, "Error output for pwm_output_type subcommand: %d\n",error); 809 exit(1); 810 } 811 break; 812 case EMCFANCTL_FAN_OT_PUSHPULL: 813 case EMCFANCTL_FAN_OT_OPENDRAIN: 814 tvalue = find_translated_bits_by_str_instance(fan_pwm_output_type, __arraycount(fan_pwm_output_type), argv[4], the_fan); 815 if (tvalue < 0) { 816 fprintf(stderr,"Error converting human value: %s\n", argv[4]); 817 exit(1); 818 } 819 error = emcfan_rmw_register(fd, start_reg, 0, fan_pwm_output_type, __arraycount(fan_pwm_output_type), tvalue, debug); 820 if (error != 0) { 821 errno = error; 822 err(EXIT_FAILURE, "read/modify/write register"); 823 } 824 break; 825 default: 826 fprintf(stderr,"Unhandled subsubcommand to pwm_output_type subcommand: %s %d\n\n", argv[4], validsubsub); 827 usage(); 828 exit(1); 829 break; 830 }; 831 832 } 833 } else { 834 fprintf(stderr,"Missing arguments to fan command\n\n"); 835 usage(); 836 exit(1); 837 } 838 break; 839 default: 840 fprintf(stderr,"Unknown handling of command: %d\n",valid); 841 exit(2); 842 break; 843 } 844 } else { 845 fprintf(stderr,"Unknown command: %s\n\n",argv[1]); 846 usage(); 847 exit(1); 848 } 849 close(fd); 850 exit(0); 851 } 852