This repository was archived by the owner on Aug 10, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimplefan.c
More file actions
301 lines (274 loc) · 6.78 KB
/
simplefan.c
File metadata and controls
301 lines (274 loc) · 6.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
#include <stdio.h>
#include <syslog.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#define CONF "/etc/simplefan.conf"
#define MANUAL_SET "/sys/devices/platform/applesmc.768/fan1_manual"
#define FAN_OUTPUT "/sys/devices/platform/applesmc.768/fan1_output"
#define CPUINFO "/proc/cpuinfo"
#define TEMP_PATH_PRE "/sys/devices/platform/coretemp.0/hwmon/hwmon0/temp"
#define TEMP_PATH_SUF "_input"
#define ERROR 1
#define SUCCESS 0
#define DEBUG
typedef struct
{
unsigned short old_speed;
unsigned short speed;
unsigned short max_speed;
unsigned short min_speed;
} Fan;
typedef struct
{
Fan fan;
unsigned short old_temp;
unsigned short temp;
unsigned short low_temp;
unsigned short high_temp;
unsigned short cores;
unsigned short polling_interval;
} Machine;
/* Prototypes */
void signal_handler(int);
void set_manual(int);
void log_fan_speed(unsigned short, unsigned short);
int get_core_temp(int);
unsigned short get_temp(unsigned short);
unsigned short calc_fan_speed(Machine, double A, double k);
void set_fan_speed(int);
void init(Machine*);
int main();
/* Handles any system signals */
void signal_handler(int signal)
{
switch(signal)
{
case SIGHUP:
case SIGKILL:
case SIGINT:
case SIGTERM:
set_manual(0);
#ifndef DEBUG
syslog(LOG_INFO, "Stop");
closelog();
#else
printf("Stop\n");
#endif
exit(SUCCESS);
break;
}
}
/* Logs when the program changes fan speed */
void log_fan_speed(unsigned short speed, unsigned short temp)
{
#ifndef DEBUG
syslog(LOG_INFO, "Change: fan speed %d RPM temperature %d degrees celsius", speed, temp);
#else
printf("Change: fan speed %d RPM temperature %d degrees celsius\n", speed, temp);
#endif
}
/* Query the number of cpu cores of the machine */
unsigned short query_cores()
{
int cores;
FILE *file;
if((file = fopen(CPUINFO, "r")) != NULL)
{
char found = 0;
size_t buf_size = 128;
char *buf = (char*)malloc(sizeof(char)*buf_size+1);
while(!found && getline(&buf, &buf_size, file) != -1)
{
if(strcmp(strtok(buf,"\t :\n"), "cpu") == 0 &&
strcmp(strtok(NULL,"\t :\n"), "cores") == 0)
{
sscanf(strtok(NULL,"\t :\n"), "%d", &cores);
found = 1;
}
}
free(buf);
fclose(file);
}
else
{
char *err_msg = "Error couldn't determine number of cpu cores";
#ifndef DEBUG
syslog(LOG_ERR, "%s", err_msg);
closelog();
#else
printf("%s\n", err_msg);
#endif
exit(ERROR);
}
return (unsigned short)cores;
}
/* Sets/clears manual fan control */
void set_manual(int set)
{
FILE *file;
if((file = fopen(MANUAL_SET, "w")) != NULL)
{
fprintf(file, "%d", set);
fclose(file);
}
else
{
char *err_msg = "Error couldn't set manual fan control";
#ifndef DEBUG
syslog(LOG_ERR, "%s", err_msg);
closelog();
#else
printf("%s\n", err_msg);
#endif
exit(ERROR);
}
}
/* Get the temperature of a cpu core */
int get_core_temp(int core)
{
FILE *file;
char *path = (char*)malloc(sizeof(char)*128);
int temp;
sprintf(path, "%s%d%s", TEMP_PATH_PRE, core, TEMP_PATH_SUF);
if((file = fopen(path, "r")) != NULL)
{
fscanf(file, "%d", &temp);
fclose(file);
}
else
{
char *err_msg = "Error couldn't get temperature of core #";
#ifndef DEBUG
syslog(LOG_ERR, "%s%d", err_msg, core);
closelog();
#else
printf("%s%d\n", err_msg, core);
#endif
exit(ERROR);
}
free(path);
return temp;
}
/* Get the average temperature of the cpu cores */
unsigned short get_temp(unsigned short cores)
{
int sum = 0, index;
for(index = 2; index < cores+2; index++)
sum += get_core_temp(index);
return (unsigned short)(sum / (cores * 1000));
}
/* Calculates the fan speed */
unsigned short calc_fan_speed(Machine mach, double A, double k)
{
if(mach.temp < mach.low_temp)
{
return mach.fan.min_speed;
}
else if(mach.temp > mach.high_temp)
{
return mach.fan.max_speed;
}
else
{
// Exponential
return (unsigned short)ceil(A * exp(k * mach.temp));
// Linear
//return (mach.fan.max_speed-mach.fan.min_speed)/(mach.high_temp-mach.low_temp)*(mach.temp-mach.low_temp)+mach.fan.min_speed;
}
}
/* Sets the fan speed */
void set_fan_speed(int fan_speed)
{
FILE *file;
if((file = fopen(FAN_OUTPUT, "w")) != NULL)
{
fprintf(file, "%d", fan_speed);
fclose(file);
}
else
{
char *err_msg = "Error couldn't set fan speed";
#ifndef DEBUG
syslog(LOG_ERR, "%s", err_msg);
closelog();
#else
printf("%s\n", err_msg);
#endif
exit(ERROR);
}
}
/* Configure nilfan */
void configure(Machine *mach)
{
FILE *file;
if((file = fopen(CONF, "r")) != NULL)
{
int low, high, min, max, poll;
fscanf(file, "%d %d %d %d %d",
&low, &high, &min, &max, &poll);
mach->low_temp = (unsigned short)low;
mach->high_temp = (unsigned short)high;
mach->fan.min_speed = (unsigned short)min;
mach->fan.max_speed = (unsigned short)max;
mach->polling_interval = (unsigned short)poll;
mach->fan.old_speed = 0;
mach->cores = query_cores();
fclose(file);
}
else
{
char *err_msg = "Error couldn't set fan parameters";
#ifndef DEBUG
syslog(LOG_ERR, "%s", err_msg);
closelog();
#else
printf("%s\n", err_msg);
#endif
exit(ERROR);
}
}
/* Initializes the fan control */
void init(Machine *mach)
{
signal(SIGHUP, signal_handler);
signal(SIGKILL, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
#ifndef DEBUG
openlog("nilfan", LOG_PID, LOG_DAEMON);
#endif
set_manual(1);
configure(mach);
char *log_msg = "Start";
#ifndef DEBUG
syslog(LOG_INFO, "%s", log_msg);
#else
printf("%s\n", log_msg);
#endif
}
/* Main function */
int main()
{
Machine mach;
double A, k;
init(&mach);
k = (log(mach.fan.max_speed) - log(mach.fan.min_speed)) / (mach.high_temp - mach.low_temp);
A = mach.fan.max_speed * exp(-(mach.high_temp * k));
while(1)
{
mach.temp = get_temp(mach.cores);
mach.fan.speed = calc_fan_speed(mach, A, k);
if(mach.fan.old_speed != mach.fan.speed)
{
set_fan_speed(mach.fan.speed);
log_fan_speed(mach.fan.speed, mach.temp);
mach.fan.old_speed = mach.fan.speed;
mach.old_temp = mach.temp;
}
sleep(mach.polling_interval);
}
return SUCCESS;
}