blob: edb3516e41b9bc305804d066b876cd0062943792 [file] [log] [blame]
Hongwei Zhang90cb34f2019-05-29 19:06:33 -04001
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
22void 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
33static 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 */
47static 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)
59static 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.
97static 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
113static 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}
142int 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
176static 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, &reg_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}
212static 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
252int 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.
266int 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)
280int 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
293int 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
307int 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
418int 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}