/* * Simple interface library for fan control operations * This file provides interface functions to support pwmtachtool. * Copyright (C) <2019> * */ #include #include #include #include #include #include #include #include #include "libpwmtach.h" #include "pwmtach_ioctl.h" #include "EINTR_wrappers.h" #include void select_sleep(time_t sec,suseconds_t usec) { struct timeval tv; tv.tv_sec = sec; tv.tv_usec = usec; while(sigwrap_select(0, NULL, NULL, NULL, &tv) < 0); } //support acessing driver using sysfs device file static char DevNodeFileName[50]; #define HWMON_DIR "/sys/class/hwmon" //build the pwm and tach access device node file name, and mapping pwm/tach number starting from 1. #define BUILD_PWM_NODE_NAME(buffer,DEV_ID,PWM_NUM) snprintf(buffer, sizeof(buffer), "%s%d%s%d", HWMON_DIR "/hwmon",DEV_ID, "/pwm", PWM_NUM+1) #define BUILD_TACH_NODE_NAME(buffer,DEV_ID,TACH_NUM) snprintf(buffer, sizeof(buffer), "%s%d%s%d%s", HWMON_DIR "/hwmon",DEV_ID, "/fan", TACH_NUM+1,"_input") #define BUILD_FAN_REG_NAME(buffer,DEV_ID,FAN_NUM) snprintf(buffer, sizeof(buffer), "%s%d%s%d%s", HWMON_DIR "/hwmon",DEV_ID, "/of_node/fan@", FAN_NUM,"/reg") //predefine FAN RPM range, must defined at some where for configuration. #define RPM_MAX 38600 #define RPM_MIN 7500 #define COUNTERRES_DEF 100 /* Check hwmon if exist or not */ static int pwmtach_directory_check(void) { int retval = 0; struct stat sb; if (!(stat("/sys/class/hwmon", &sb) == 0 && S_ISDIR(sb.st_mode))) { printf("\"/sys/class/hwmon\" not exist!\n"); retval = -1; } return retval; } //Notice: dutycycle_value is one byte (0-255) static int SET_PWM_DUTYCYCLE_VALUE ( pwmtach_ioctl_data *ppwmtach_arg ) { int retval = 0; unsigned char dutycycle_value; int fd; char duty_num[5]; retval = pwmtach_directory_check(); if(retval != 0) { return retval; } dutycycle_value = ppwmtach_arg->dutycycle; BUILD_PWM_NODE_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->pwmnumber); retval = access(DevNodeFileName,F_OK); if(retval != 0) { return retval; } fd = sigwrap_open(DevNodeFileName, O_WRONLY); if (fd < 0) { return fd; } snprintf(duty_num,5, "%d", dutycycle_value); if ( write(fd, duty_num, strlen (duty_num)) != (ssize_t )strlen(duty_num)){ printf("%s: Error write dutycycle value %d to pwm %d\n",__FUNCTION__,dutycycle_value,ppwmtach_arg->pwmnumber); retval = -1; } (void)sigwrap_close(fd); return retval; } //Notice: dutycycle_percentage value should be between 1 to 99. static int SET_PWM_DUTYCYCLE ( pwmtach_ioctl_data *ppwmtach_arg) { int retval = 0; unsigned char dutycycle_value; if(ppwmtach_arg->dutycycle > 100) { return -1; } dutycycle_value = (ppwmtach_arg->dutycycle*255)/100; ppwmtach_arg->dutycycle = dutycycle_value; retval = SET_PWM_DUTYCYCLE_VALUE(ppwmtach_arg); return retval; } static int GET_PWM_DUTYCYCLE ( pwmtach_ioctl_data *ppwmtach_arg ) { int retval = 0; int fd; char duty_num[5]; retval = pwmtach_directory_check(); if(retval != 0) {//printf("%s,error 0\n",__FUNCTION__); return retval; } BUILD_PWM_NODE_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->pwmnumber); retval = access(DevNodeFileName,F_OK); if(retval != 0) {printf("%s,error 2,%s not exist\n",__FUNCTION__,DevNodeFileName); return retval; } fd = sigwrap_open(DevNodeFileName, O_RDONLY); if (fd < 0) {printf("%s,error 3\n",__FUNCTION__); return fd; } read(fd, duty_num, 5); ppwmtach_arg->dutycycle = atoi(duty_num); printf("%s:dutycycle value %d to pwm %d\n",__FUNCTION__,ppwmtach_arg->dutycycle,ppwmtach_arg->pwmnumber); (void)sigwrap_close(fd); return retval; } int GET_TACH_SPEED (pwmtach_ioctl_data *ppwmtach_arg ) { int retval = 0; int fd; char data[6]; retval = pwmtach_directory_check(); if(retval != 0) {printf("%s,error 0\n",__FUNCTION__); return retval; } BUILD_TACH_NODE_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->tachnumber); retval = access(DevNodeFileName,F_OK); if(retval != 0) {printf("%s,error 2,%s not exist\n",__FUNCTION__,DevNodeFileName); return retval; } fd = sigwrap_open(DevNodeFileName, O_RDONLY); if (fd < 0) {printf("%s,error 3\n",__FUNCTION__); return fd; } memset(data, 0, 6); read(fd, data, 6); ppwmtach_arg->rpmvalue = atoi(data); (void)sigwrap_close(fd); printf("%s:rpm value %d\n",__FUNCTION__,ppwmtach_arg->rpmvalue); return retval; } //mapping function of fan to tach //using direct mapping as default #define GET_TACH_NUMBER(FAN_NUMBER) FAN_NUMBER //mapping fan number to pwm number //using information in fan@number reg item, to look up the pwm index static int GET_PWM_NUMBER(pwmtach_ioctl_data *ppwmtach_arg) { int retval = 0; int fd; int reg_val = 0; retval = pwmtach_directory_check(); if(retval != 0) {printf("%s,error 0\n",__FUNCTION__); return retval; } BUILD_FAN_REG_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->fannumber); retval = access(DevNodeFileName,F_OK); if(retval != 0) {printf("%s,error 2,%s not exist\n",__FUNCTION__,DevNodeFileName); return retval; } fd = sigwrap_open(DevNodeFileName, O_RDONLY); if (fd < 0) {printf("%s,error 3\n",__FUNCTION__); return fd; } read(fd, ®_val, sizeof(int)); if(reg_val < 0) { retval = -1; }else { retval = reg_val >> 24; //get the highest byte printf("%s:fan %d, pwm %d, val 0x%X\n",__FUNCTION__,ppwmtach_arg->fannumber,retval,reg_val); printf("%s\n",DevNodeFileName); } (void)sigwrap_close(fd); // printf("%s:rpm value %d\n",__FUNCTION__,ppwmtach_arg->rpmvalue); return retval; } static int pwmtach_action( pwmtach_ioctl_data* argp, int command ) { int retval = 0; // printf("%s, Command 0x%X:Dev:%d,Pwm:0x%X,Fan:0x%x,Tach:0x%X\n",__FUNCTION__,command,argp->dev_id,argp->pwmnumber,argp->fannumber,argp->tachnumber); switch(command) { case SET_DUTY_CYCLE_BY_PWM_CHANNEL: retval = SET_PWM_DUTYCYCLE(argp); break; case SET_DUTY_CYCLE_VALUE_BY_PWM_CHANNEL: case SET_DUTY_CYCLE: retval = SET_PWM_DUTYCYCLE_VALUE(argp); break; case GET_TACH_VALUE_BY_TACH_CHANNEL: retval = GET_TACH_SPEED(argp); break; case GET_TACH_VALUE: //used to get fan speed argp->tachnumber = GET_TACH_NUMBER(argp->fannumber); retval = GET_TACH_SPEED(argp); break; case GET_DUTY_CYCLE: retval = GET_PWM_DUTYCYCLE(argp); break; case GET_FAN_RPM_RANGE: argp->max_rpm = RPM_MAX; argp->min_rpm = RPM_MIN; break; case INIT_PWMTACH: //assume that init complete argp->pwmnumber = GET_PWM_NUMBER(argp);; //since we don't have the fan to pwm mapping, just using direct map for workarround. argp->counterresvalue = COUNTERRES_DEF; //since driver don't support COUNTERRES, just using default value for workarround. retval = GET_PWM_DUTYCYCLE(argp); break; case END_OF_FUNC_TABLE: default: printf("%s, Command 0x%X not support!\n",__FUNCTION__,command); retval = -1; } return( retval ); } int get_tach_speed ( unsigned int dev_id, unsigned char tach_number, unsigned int *rpm_value ) { pwmtach_ioctl_data pwmtach_arg; int retval = 0; pwmtach_arg.dev_id = dev_id; pwmtach_arg.tachnumber = tach_number; retval = pwmtach_action( &pwmtach_arg, GET_TACH_VALUE_BY_TACH_CHANNEL); if(retval != -1) *rpm_value = pwmtach_arg.rpmvalue; return retval; } //Notice: dutycycle_percentage value should be between 1 to 99. int set_pwm_dutycycle ( unsigned int dev_id, unsigned char pwm_number, unsigned char dutycycle_percentage ) { pwmtach_ioctl_data pwmtach_arg; int retval = 0; pwmtach_arg.dev_id = dev_id; pwmtach_arg.pwmnumber = pwm_number; pwmtach_arg.dutycycle= dutycycle_percentage; retval = pwmtach_action( &pwmtach_arg, SET_DUTY_CYCLE_BY_PWM_CHANNEL); return retval; } //Notice: dutycycle_value is one byte (0-255) int set_pwm_dutycycle_value ( unsigned int dev_id, unsigned char pwm_number, unsigned char dutycycle_value ) { pwmtach_ioctl_data pwmtach_arg; int retval = 0; pwmtach_arg.dev_id = dev_id; pwmtach_arg.pwmnumber = pwm_number; pwmtach_arg.dutycycle= dutycycle_value; retval = pwmtach_action( &pwmtach_arg, SET_DUTY_CYCLE_VALUE_BY_PWM_CHANNEL); return retval; } int get_pwm_dutycycle ( unsigned int dev_id, unsigned char pwm_number, unsigned char *dutycycle_percentage ) { pwmtach_ioctl_data pwmtach_arg; int retval = 0; pwmtach_arg.dev_id = dev_id; pwmtach_arg.pwmnumber = pwm_number; retval = pwmtach_action(&pwmtach_arg, GET_DUTY_CYCLE); if(retval != -1) *dutycycle_percentage = pwmtach_arg.dutycycle; return retval; } int set_fan_speed ( unsigned int dev_id, unsigned char fan_number, unsigned int rpm_value ) { int retval = 0; unsigned int retries = 20; unsigned char firsttime = 1; unsigned char duty_cycle_increasing = 0; unsigned char reached90percent = 0; unsigned char reached5percent = 0; unsigned long desiredrpm = rpm_value; pwmtach_ioctl_data pwmtach_arg; pwmtach_data_t* indata = (pwmtach_data_t*) &pwmtach_arg; indata->dev_id = dev_id; indata->fannumber = fan_number; indata->rpmvalue = rpm_value; indata->counterresvalue = 0; indata->dutycycle = 0; indata->prevdutycycle = 0; retval = pwmtach_action( indata, GET_FAN_RPM_RANGE); if ((rpm_value < indata->min_rpm) || (rpm_value > indata->max_rpm)) { printf("Out of range Fan Speed value for fan.\n"); return -1; } retval = pwmtach_action ( indata, INIT_PWMTACH ); while (retries--) { /* Wait for 1 seconds */ select_sleep(0,1*1000*1000); if ((retval = pwmtach_action( indata, GET_TACH_VALUE )) != 0) { indata->dutycycle = indata->prevdutycycle; retval = pwmtach_action( indata, SET_DUTY_CYCLE); return -1; } else { indata->prevdutycycle = indata->dutycycle; if (indata->rpmvalue > (desiredrpm + 50)) { if (indata->dutycycle <= ((indata->counterresvalue*10)/100)) { if (reached5percent == 1) { printf("\nSpeed is set to minimum possible speed of %d RPM.\n",indata->rpmvalue); break; } reached5percent = 1; } else { indata->dutycycle -= ((5 * indata->counterresvalue)/100); } } else if (indata->rpmvalue < (desiredrpm - 50)) { if (indata->dutycycle >= indata->counterresvalue) { if (reached90percent == 1) { printf("\nSpeed is set to maximum possible speed of %d RPM.\n",indata->rpmvalue); break; } reached90percent = 1; } else { indata->dutycycle += ((5 * indata->counterresvalue)/100); } } else { break; } retval = pwmtach_action (indata, SET_DUTY_CYCLE); printf("After update: dutycycle=%d, rpmvalue=%d\n", indata->dutycycle, indata->rpmvalue); if(indata->prevdutycycle < indata->dutycycle) { /* Duty Cycle increasing */ if ((firsttime == 0) && (duty_cycle_increasing == 0)) { indata->dutycycle = indata->prevdutycycle; retval = pwmtach_action( indata, SET_DUTY_CYCLE); printf("\n"); return 0; } duty_cycle_increasing = 1; } else { /* Duty Cycle decreasing */ if ((firsttime == 0) && (duty_cycle_increasing == 1)) { indata->dutycycle = indata->prevdutycycle; retval = pwmtach_action( indata, SET_DUTY_CYCLE); printf("\n"); return 0; } duty_cycle_increasing = 0; } if (firsttime == 1) firsttime = 0; } printf("retry %d : dt=%d, ps=%d, cr=%d\n", retries, indata->dutycycle, indata->prescalervalue, indata->counterresvalue); } return 0; } int get_fan_speed ( unsigned int dev_id, unsigned char fan_number, unsigned int *rpm_value ) { pwmtach_ioctl_data pwmtach_arg; int retval = 0; pwmtach_arg.dev_id = dev_id; pwmtach_arg.fannumber = fan_number; retval = pwmtach_action( &pwmtach_arg, GET_TACH_VALUE ); if(retval != -1) *rpm_value = pwmtach_arg.rpmvalue; return retval; }