1 2 /* 3 * Simple interface library for fan control operations 4 * This file provides interface functions to support pwmtachtool. 5 * Copyright (C) <2019> <American Megatrends International LLC> 6 * 7 */ 8 9 #include <stdio.h> 10 #include <unistd.h> 11 #include <sys/types.h> 12 #include <sys/stat.h> 13 #include <fcntl.h> 14 #include <sys/ioctl.h> 15 #include <errno.h> 16 #include <string.h> 17 #include "libpwmtach.h" 18 #include "pwmtach_ioctl.h" 19 #include "EINTR_wrappers.h" 20 #include <stdlib.h> 21 22 void select_sleep(time_t sec,suseconds_t usec) 23 { 24 struct timeval tv; 25 26 tv.tv_sec = sec; 27 tv.tv_usec = usec; 28 29 while(sigwrap_select(0, NULL, NULL, NULL, &tv) < 0); 30 } 31 32 //support acessing driver using sysfs device file 33 static char DevNodeFileName[50]; 34 #define HWMON_DIR "/sys/class/hwmon" 35 36 //build the pwm and tach access device node file name, and mapping pwm/tach number starting from 1. 37 #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) 38 #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") 39 #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") 40 41 //predefine FAN RPM range, must defined at some where for configuration. 42 #define RPM_MAX 38600 43 #define RPM_MIN 7500 44 #define COUNTERRES_DEF 100 45 46 /* Check hwmon if exist or not */ 47 static int pwmtach_directory_check(void) 48 { 49 int retval = 0; 50 struct stat sb; 51 if (!(stat("/sys/class/hwmon", &sb) == 0 && S_ISDIR(sb.st_mode))) 52 { 53 printf("\"/sys/class/hwmon\" not exist!\n"); 54 retval = -1; 55 } 56 return retval; 57 } 58 //Notice: dutycycle_value is one byte (0-255) 59 static int SET_PWM_DUTYCYCLE_VALUE ( pwmtach_ioctl_data *ppwmtach_arg ) 60 { 61 int retval = 0; 62 unsigned char dutycycle_value; 63 int fd; 64 char duty_num[5]; 65 66 retval = pwmtach_directory_check(); 67 if(retval != 0) 68 { 69 return retval; 70 } 71 72 dutycycle_value = ppwmtach_arg->dutycycle; 73 BUILD_PWM_NODE_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->pwmnumber); 74 retval = access(DevNodeFileName,F_OK); 75 if(retval != 0) 76 { 77 return retval; 78 } 79 80 fd = sigwrap_open(DevNodeFileName, O_WRONLY); 81 if (fd < 0) { 82 return fd; 83 } 84 85 snprintf(duty_num,5, "%d", dutycycle_value); 86 87 if ( write(fd, duty_num, strlen (duty_num)) != (ssize_t )strlen(duty_num)){ 88 printf("%s: Error write dutycycle value %d to pwm %d\n",__FUNCTION__,dutycycle_value,ppwmtach_arg->pwmnumber); 89 retval = -1; 90 } 91 (void)sigwrap_close(fd); 92 93 return retval; 94 } 95 96 //Notice: dutycycle_percentage value should be between 1 to 99. 97 static int SET_PWM_DUTYCYCLE ( pwmtach_ioctl_data *ppwmtach_arg) 98 { 99 int retval = 0; 100 unsigned char dutycycle_value; 101 102 if(ppwmtach_arg->dutycycle > 100) 103 { 104 return -1; 105 } 106 107 dutycycle_value = (ppwmtach_arg->dutycycle*255)/100; 108 ppwmtach_arg->dutycycle = dutycycle_value; 109 retval = SET_PWM_DUTYCYCLE_VALUE(ppwmtach_arg); 110 return retval; 111 } 112 113 static int GET_PWM_DUTYCYCLE ( pwmtach_ioctl_data *ppwmtach_arg ) 114 { 115 int retval = 0; 116 int fd; 117 char duty_num[5]; 118 119 retval = pwmtach_directory_check(); 120 if(retval != 0) 121 {//printf("%s,error 0\n",__FUNCTION__); 122 return retval; 123 } 124 125 BUILD_PWM_NODE_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->pwmnumber); 126 retval = access(DevNodeFileName,F_OK); 127 if(retval != 0) 128 {printf("%s,error 2,%s not exist\n",__FUNCTION__,DevNodeFileName); 129 return retval; 130 } 131 fd = sigwrap_open(DevNodeFileName, O_RDONLY); 132 if (fd < 0) {printf("%s,error 3\n",__FUNCTION__); 133 return fd; 134 } 135 read(fd, duty_num, 5); 136 ppwmtach_arg->dutycycle = atoi(duty_num); 137 printf("%s:dutycycle value %d to pwm %d\n",__FUNCTION__,ppwmtach_arg->dutycycle,ppwmtach_arg->pwmnumber); 138 (void)sigwrap_close(fd); 139 140 return retval; 141 } 142 int GET_TACH_SPEED (pwmtach_ioctl_data *ppwmtach_arg ) 143 { 144 int retval = 0; 145 int fd; 146 char data[6]; 147 148 retval = pwmtach_directory_check(); 149 if(retval != 0) 150 {printf("%s,error 0\n",__FUNCTION__); 151 return retval; 152 } 153 BUILD_TACH_NODE_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->tachnumber); 154 retval = access(DevNodeFileName,F_OK); 155 if(retval != 0) 156 {printf("%s,error 2,%s not exist\n",__FUNCTION__,DevNodeFileName); 157 return retval; 158 } 159 160 fd = sigwrap_open(DevNodeFileName, O_RDONLY); 161 if (fd < 0) {printf("%s,error 3\n",__FUNCTION__); 162 return fd; 163 } 164 memset(data, 0, 6); 165 read(fd, data, 6); 166 ppwmtach_arg->rpmvalue = atoi(data); 167 (void)sigwrap_close(fd); 168 printf("%s:rpm value %d\n",__FUNCTION__,ppwmtach_arg->rpmvalue); 169 return retval; 170 } 171 //mapping function of fan to tach 172 //using direct mapping as default 173 #define GET_TACH_NUMBER(FAN_NUMBER) FAN_NUMBER 174 //mapping fan number to pwm number 175 //using information in fan@number reg item, to look up the pwm index 176 static int GET_PWM_NUMBER(pwmtach_ioctl_data *ppwmtach_arg) 177 { 178 int retval = 0; 179 int fd; 180 int reg_val = 0; 181 182 retval = pwmtach_directory_check(); 183 if(retval != 0) 184 {printf("%s,error 0\n",__FUNCTION__); 185 return retval; 186 } 187 BUILD_FAN_REG_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->fannumber); 188 retval = access(DevNodeFileName,F_OK); 189 if(retval != 0) 190 {printf("%s,error 2,%s not exist\n",__FUNCTION__,DevNodeFileName); 191 return retval; 192 } 193 194 fd = sigwrap_open(DevNodeFileName, O_RDONLY); 195 if (fd < 0) {printf("%s,error 3\n",__FUNCTION__); 196 return fd; 197 } 198 read(fd, ®_val, sizeof(int)); 199 if(reg_val < 0) 200 { 201 retval = -1; 202 }else 203 { 204 retval = reg_val >> 24; //get the highest byte 205 printf("%s:fan %d, pwm %d, val 0x%X\n",__FUNCTION__,ppwmtach_arg->fannumber,retval,reg_val); 206 printf("%s\n",DevNodeFileName); 207 } 208 (void)sigwrap_close(fd); 209 // printf("%s:rpm value %d\n",__FUNCTION__,ppwmtach_arg->rpmvalue); 210 return retval; 211 } 212 static int pwmtach_action( pwmtach_ioctl_data* argp, int command ) 213 { 214 int retval = 0; 215 // 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); 216 switch(command) 217 { 218 case SET_DUTY_CYCLE_BY_PWM_CHANNEL: 219 retval = SET_PWM_DUTYCYCLE(argp); 220 break; 221 case SET_DUTY_CYCLE_VALUE_BY_PWM_CHANNEL: 222 case SET_DUTY_CYCLE: 223 retval = SET_PWM_DUTYCYCLE_VALUE(argp); 224 break; 225 case GET_TACH_VALUE_BY_TACH_CHANNEL: 226 retval = GET_TACH_SPEED(argp); 227 break; 228 case GET_TACH_VALUE: //used to get fan speed 229 argp->tachnumber = GET_TACH_NUMBER(argp->fannumber); 230 retval = GET_TACH_SPEED(argp); 231 break; 232 case GET_DUTY_CYCLE: 233 retval = GET_PWM_DUTYCYCLE(argp); 234 break; 235 case GET_FAN_RPM_RANGE: 236 argp->max_rpm = RPM_MAX; 237 argp->min_rpm = RPM_MIN; 238 break; 239 case INIT_PWMTACH: //assume that init complete 240 argp->pwmnumber = GET_PWM_NUMBER(argp);; //since we don't have the fan to pwm mapping, just using direct map for workarround. 241 argp->counterresvalue = COUNTERRES_DEF; //since driver don't support COUNTERRES, just using default value for workarround. 242 retval = GET_PWM_DUTYCYCLE(argp); 243 break; 244 case END_OF_FUNC_TABLE: 245 default: 246 printf("%s, Command 0x%X not support!\n",__FUNCTION__,command); 247 retval = -1; 248 } 249 return( retval ); 250 } 251 252 int get_tach_speed ( unsigned int dev_id, unsigned char tach_number, unsigned int *rpm_value ) 253 { 254 pwmtach_ioctl_data pwmtach_arg; 255 int retval = 0; 256 257 pwmtach_arg.dev_id = dev_id; 258 pwmtach_arg.tachnumber = tach_number; 259 retval = pwmtach_action( &pwmtach_arg, GET_TACH_VALUE_BY_TACH_CHANNEL); 260 if(retval != -1) 261 *rpm_value = pwmtach_arg.rpmvalue; 262 return retval; 263 } 264 265 //Notice: dutycycle_percentage value should be between 1 to 99. 266 int set_pwm_dutycycle ( unsigned int dev_id, unsigned char pwm_number, unsigned char dutycycle_percentage ) 267 { 268 pwmtach_ioctl_data pwmtach_arg; 269 int retval = 0; 270 271 pwmtach_arg.dev_id = dev_id; 272 pwmtach_arg.pwmnumber = pwm_number; 273 pwmtach_arg.dutycycle= dutycycle_percentage; 274 retval = pwmtach_action( &pwmtach_arg, SET_DUTY_CYCLE_BY_PWM_CHANNEL); 275 276 return retval; 277 } 278 279 //Notice: dutycycle_value is one byte (0-255) 280 int set_pwm_dutycycle_value ( unsigned int dev_id, unsigned char pwm_number, unsigned char dutycycle_value ) 281 { 282 pwmtach_ioctl_data pwmtach_arg; 283 int retval = 0; 284 285 pwmtach_arg.dev_id = dev_id; 286 pwmtach_arg.pwmnumber = pwm_number; 287 pwmtach_arg.dutycycle= dutycycle_value; 288 retval = pwmtach_action( &pwmtach_arg, SET_DUTY_CYCLE_VALUE_BY_PWM_CHANNEL); 289 290 return retval; 291 } 292 293 int get_pwm_dutycycle ( unsigned int dev_id, unsigned char pwm_number, unsigned char *dutycycle_percentage ) 294 { 295 pwmtach_ioctl_data pwmtach_arg; 296 int retval = 0; 297 298 pwmtach_arg.dev_id = dev_id; 299 pwmtach_arg.pwmnumber = pwm_number; 300 retval = pwmtach_action(&pwmtach_arg, GET_DUTY_CYCLE); 301 if(retval != -1) 302 *dutycycle_percentage = pwmtach_arg.dutycycle; 303 return retval; 304 305 } 306 307 int set_fan_speed ( unsigned int dev_id, unsigned char fan_number, unsigned int rpm_value ) 308 { 309 int retval = 0; 310 unsigned int retries = 20; 311 unsigned char firsttime = 1; 312 unsigned char duty_cycle_increasing = 0; 313 unsigned char reached90percent = 0; 314 unsigned char reached5percent = 0; 315 unsigned long desiredrpm = rpm_value; 316 pwmtach_ioctl_data pwmtach_arg; 317 pwmtach_data_t* indata = (pwmtach_data_t*) &pwmtach_arg; 318 319 indata->dev_id = dev_id; 320 indata->fannumber = fan_number; 321 indata->rpmvalue = rpm_value; 322 indata->counterresvalue = 0; 323 indata->dutycycle = 0; 324 indata->prevdutycycle = 0; 325 326 retval = pwmtach_action( indata, GET_FAN_RPM_RANGE); 327 if ((rpm_value < indata->min_rpm) || (rpm_value > indata->max_rpm)) 328 { 329 printf("Out of range Fan Speed value for fan.\n"); 330 return -1; 331 } 332 retval = pwmtach_action ( indata, INIT_PWMTACH ); 333 334 while (retries--) 335 { 336 /* Wait for 1 seconds */ 337 select_sleep(0,1*1000*1000); 338 if ((retval = pwmtach_action( indata, GET_TACH_VALUE )) != 0) 339 { 340 indata->dutycycle = indata->prevdutycycle; 341 retval = pwmtach_action( indata, SET_DUTY_CYCLE); 342 return -1; 343 } 344 else 345 { 346 indata->prevdutycycle = indata->dutycycle; 347 if (indata->rpmvalue > (desiredrpm + 50)) 348 { 349 if (indata->dutycycle <= ((indata->counterresvalue*10)/100)) 350 { 351 if (reached5percent == 1) 352 { 353 printf("\nSpeed is set to minimum possible speed of %d RPM.\n",indata->rpmvalue); 354 break; 355 } 356 reached5percent = 1; 357 } 358 else 359 { 360 indata->dutycycle -= ((5 * indata->counterresvalue)/100); 361 } 362 } 363 else if (indata->rpmvalue < (desiredrpm - 50)) 364 { 365 if (indata->dutycycle >= indata->counterresvalue) 366 { 367 if (reached90percent == 1) 368 { 369 printf("\nSpeed is set to maximum possible speed of %d RPM.\n",indata->rpmvalue); 370 break; 371 } 372 reached90percent = 1; 373 } 374 else 375 { 376 indata->dutycycle += ((5 * indata->counterresvalue)/100); 377 } 378 } 379 else 380 { 381 break; 382 } 383 384 retval = pwmtach_action (indata, SET_DUTY_CYCLE); 385 printf("After update: dutycycle=%d, rpmvalue=%d\n", indata->dutycycle, indata->rpmvalue); 386 387 if(indata->prevdutycycle < indata->dutycycle) 388 { /* Duty Cycle increasing */ 389 if ((firsttime == 0) && (duty_cycle_increasing == 0)) 390 { 391 indata->dutycycle = indata->prevdutycycle; 392 retval = pwmtach_action( indata, SET_DUTY_CYCLE); 393 printf("\n"); 394 return 0; 395 } 396 duty_cycle_increasing = 1; 397 } 398 else 399 { /* Duty Cycle decreasing */ 400 if ((firsttime == 0) && (duty_cycle_increasing == 1)) 401 { 402 indata->dutycycle = indata->prevdutycycle; 403 retval = pwmtach_action( indata, SET_DUTY_CYCLE); 404 printf("\n"); 405 return 0; 406 } 407 duty_cycle_increasing = 0; 408 } 409 if (firsttime == 1) 410 firsttime = 0; 411 } 412 printf("retry %d : dt=%d, ps=%d, cr=%d\n", retries, indata->dutycycle, indata->prescalervalue, indata->counterresvalue); 413 } 414 return 0; 415 416 } 417 418 int get_fan_speed ( unsigned int dev_id, unsigned char fan_number, unsigned int *rpm_value ) 419 { 420 pwmtach_ioctl_data pwmtach_arg; 421 int retval = 0; 422 423 pwmtach_arg.dev_id = dev_id; 424 pwmtach_arg.fannumber = fan_number; 425 retval = pwmtach_action( &pwmtach_arg, GET_TACH_VALUE ); 426 if(retval != -1) 427 *rpm_value = pwmtach_arg.rpmvalue; 428 return retval; 429 } 430