4 breezed is a fan speed control daemon for Linux computers.
6 Copyright (c) 2008, 2009 Francois Fleuret
7 Written by Francois Fleuret <francois@fleuret.org>
9 This file is part of breezed.
11 breezed is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 3 as
13 published by the Free Software Foundation.
15 breezed is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with breezed. If not, see <http://www.gnu.org/licenses/>.
30 #include <sys/types.h>
38 const int major_version_number = 1;
39 const int minor_version_number = 2;
41 const int buffer_size = 1024;
43 const char *default_configuration_file = "/etc/breezed.conf";
45 // The time period to check the temperature.
46 const int polling_delay = 5;
48 // Minimum time between last change and a change down.
49 const int minimum_delay_to_go_down = 30;
51 // Gap between a threshold to go up and a threshold to go down to
52 // reduce the oscillations.
53 const int down_temperature_delta = 2;
58 int nb_rounds_since_last_change = 0;
63 int nb_temperature_thresholds;
64 int *temperature_thresholds = 0;
66 int nb_file_thermal = 0;
67 char **file_thermal = 0;
68 int *file_thermal_fd = 0;
70 char *configuration_file;
72 //////////////////////////////////////////////////////////////////////
74 char *next_word(char *buffer, char *r, int buffer_size) {
79 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
82 while((*r != '"') && (*r != '\0') &&
83 (s<buffer+buffer_size-1))
87 while((*r != '\r') && (*r != '\n') && (*r != '\0') &&
88 (*r != '\t') && (*r != ' ') && (*r != ',')) {
89 if(s == buffer + buffer_size) {
90 fprintf(stderr, "Buffer overflow in next_word.\n");
97 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
98 if((*r == '\0') || (*r=='\r') || (*r=='\n')) r = 0;
105 void set_fan_level(int fan_fd, int f, int max_fan_level) {
106 char buffer[buffer_size];
107 if(f < 0 || f > max_fan_level) f = max_fan_level;
108 sprintf(buffer, "level %d\n", f);
109 if(write(fan_fd, buffer, strlen(buffer)) < 0) {
110 fprintf(stderr, "Error in setting the fan level (%s).\n",
116 void define_thermal_files(char *definition) {
117 char token[buffer_size];
120 fprintf(stderr, "Thermal files already defined.\n");
127 s = next_word(token, s, buffer_size);
130 file_thermal = new char *[nb_file_thermal];
131 file_thermal_fd = new int[nb_file_thermal];
135 s = next_word(token, s, buffer_size);
136 file_thermal[k] = new char[strlen(token) + 1];
137 strcpy(file_thermal[k], token);
142 void define_temperature_thresholds(char *definition) {
143 char token[buffer_size];
145 if(temperature_thresholds) {
146 fprintf(stderr, "Temperature thresholds already defined.\n");
150 nb_temperature_thresholds = 1;
155 s = next_word(token, s, buffer_size);
156 nb_temperature_thresholds++;
159 temperature_thresholds = new int[nb_temperature_thresholds];
161 temperature_thresholds[0] = -1;
166 s = next_word(token, s, buffer_size);
167 temperature_thresholds[k] = atoi(token);
169 temperature_thresholds[k] < temperature_thresholds[k-1]) {
170 fprintf(stderr, "The temperature thresholds have to be increasing.\n");
177 //////////////////////////////////////////////////////////////////////
179 int main(int argc, char **argv) {
181 char buffer[buffer_size], token[buffer_size];
183 configuration_file = new char[strlen(default_configuration_file) + 1];
184 strcpy(configuration_file, default_configuration_file);
189 if(strcmp(argv[i], "--debug") == 0 || strcmp(argv[i], "-d") == 0) {
194 else if(strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
195 printf("Breezed v%d.%d. Written by Francois Fleuret (francois@fleuret.org).\n",
196 major_version_number, minor_version_number);
200 else if(strcmp(argv[i], "--no-configuration-file") == 0 ||
201 strcmp(argv[i], "-ncf") == 0) {
202 delete[] configuration_file;
203 configuration_file = 0;
207 else if(strcmp(argv[i], "--configuration-file") == 0 ||
208 strcmp(argv[i], "-cf") == 0) {
211 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
215 delete[] configuration_file;
216 configuration_file = new char[strlen(argv[i]) + 1];
217 strcpy(configuration_file, argv[i]);
222 else if(strcmp(argv[i], "--thermal-files") == 0 ||
223 strcmp(argv[i], "-tf") == 0) {
226 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
229 define_thermal_files(argv[i]);
233 else if(strcmp(argv[i], "--fan-file") == 0 ||
234 strcmp(argv[i], "-ff") == 0) {
237 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
242 fprintf(stderr, "Fan file already defined.\n");
245 file_fan = new char[strlen(argv[i]) + 1];
246 strcpy(file_fan, argv[i]);
251 else if(strcmp(argv[i], "--temperature-thresholds") == 0 ||
252 strcmp(argv[i], "-tt") == 0) {
256 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
260 define_temperature_thresholds(argv[i]);
264 else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
266 printf("%s [--version|-v] [--help|-h] [--debug|-d]\\\n\
267 [--configuration-file|-cf <file>]\\\n\
268 [--no-configuration-file|-ncf]\\\n\
269 [--thermal-files|-tf <thermalfile1,thermalfile2,...>] \\\n\
270 [--fanfile|-ff <fan file>] \\\n\
271 [--temperature-thresholds|-tt <t1,t2,t3,t4,...>]\n\
273 --help|-h : shows this help\n\
274 --version|-v : shows the version number\n\
275 --debug|-d : prints out additional information\n\
276 --configuration-file|-cf sets the configuration file\n\
277 --no-configuration-file|-ncf do not load a configuration file\n\
278 --thermal-files|-tf : sets where to look for temperatures\n\
279 --fanfile|-ff : sets where to control the fan level\n\
280 --temperature-thresholds|-tt : sets the temperature thresholds\n\
282 This daemon polls the temperatures every 5s, takes the max and sets\n\
283 the fan level accordingly. It uses as temperatures all the numbers it\n\
284 finds in the provided \"thermal files\". Hence you can use as well\n\
285 /proc/acpi/thermal_zone/THM*/temperature or /proc/acpi/ibm/thermal.\n\
287 The fan speed is set by echoing into the provided fan file.\n\
289 To reduce oscillations, it will not reduce the fan speed less than 30s\n\
290 after the last previous change and the thresholds actually used to\n\
291 reduce the fan speed are two degrees lower than the provided\n\
292 thresholds, which are used to increase the fan speed.\n\
294 This daemon should be started through the adequate shell script in\n\
297 Options can be set either in the configuration file, or on the \n\
298 command line. Options can not be set twice.\n\
300 Version %d.%d, November 2009.\n\
302 Written by Francois Fleuret (francois@fleuret.org).\n",
304 major_version_number, minor_version_number);
310 fprintf(stderr, "Unknown argument %s.\n", argv[i]);
316 //////////////////////////////////////////////////////////////////////
318 if(configuration_file) {
319 ifstream cf(configuration_file);
322 fprintf(stderr, "Can not open %s for reading.\n", configuration_file);
331 cf.getline(buffer, buffer_size);
334 s = next_word(token, buffer, buffer_size);
336 if(strcmp(token, "thermal_files") == 0) {
338 fprintf(stderr, "Missing parameter in %s:%d\n",
339 configuration_file, line_number);
342 define_thermal_files(s);
345 else if(strcmp(token, "debug") == 0) {
349 else if(strcmp(token, "fan_file") == 0) {
351 fprintf(stderr, "Fan file already defined.\n");
355 fprintf(stderr, "Missing parameter in %s:%d\n",
356 configuration_file, line_number);
359 file_fan = new char[strlen(s) + 1];
363 else if(strcmp(token, "temperature_thresholds") == 0) {
365 fprintf(stderr, "Missing parameter in %s:%d\n",
366 configuration_file, line_number);
369 define_temperature_thresholds(s);
372 else if(token[0] && token[0] != '#') {
373 fprintf(stderr, "Unknown keyword '%s' in %s:%d.\n",
374 token, configuration_file, line_number);
380 //////////////////////////////////////////////////////////////////////
382 if(nb_temperature_thresholds == 0) {
383 fprintf(stderr, "No temperature threshold was provided.\n");
387 if(nb_file_thermal == 0) {
388 fprintf(stderr, "No thermal file was provided.\n");
393 fprintf(stderr, "No fan file was provided.\n");
397 for(int i = 0; i < nb_file_thermal; i++) {
398 file_thermal_fd[i] = open(file_thermal[i], O_RDONLY);
399 if(file_thermal_fd[i] < 0) {
400 fprintf(stderr, "Can not open %s for reading (%s).\n",
401 file_thermal[i], strerror(errno));
406 file_fan_fd = open(file_fan, O_WRONLY);
408 if(file_fan_fd < 0) {
409 fprintf(stderr, "Can not open %s for writing (%s).\n",
410 file_fan, strerror(errno));
414 //////////////////////////////////////////////////////////////////////
417 for(int t = 0; t < nb_file_thermal; t++) {
418 printf("file_thermal[%d] %s\n", t, file_thermal[t]);
421 printf("file_fan %s\n", file_fan);
423 for(int t = 0; t < nb_temperature_thresholds; t++) {
424 printf("temperature_thresholds[%d] %d",
425 t, temperature_thresholds[t]);
429 //////////////////////////////////////////////////////////////////////
433 int temperature = -1;
436 printf("Temperature:");
439 for(int i = 0; i < nb_file_thermal; i++) {
440 lseek(file_thermal_fd[i], 0, SEEK_SET);
441 int size = read(file_thermal_fd[i], buffer, buffer_size);
443 if(size > 0 && size < buffer_size) {
449 while(*s && *s != '-' && (*s < '0') || (*s > '9')) s++;
458 while(*s >= '0' && *s <= '9') {
459 t = t * 10 + (*s - '0');
468 // So that we can deal with the new files where the
469 // temperature are in 1/1000th of C
470 if(t > 1000) t /= 1000;
472 if(t > temperature) temperature = t;
477 if(i < nb_file_thermal - 1)
484 fprintf(stderr, "Nothing to read in %d.\n", file_thermal[i]);
485 } else if(size < 0) {
486 fprintf(stderr, "Error while reading %s (%s).\n",
487 file_thermal[i], strerror(errno));
489 fprintf(stderr, "Error while reading %s (too large).\n",
496 if(temperature < 0) {
497 fprintf(stderr, "Could not read a meaningful temperature.\n");
501 nb_rounds_since_last_change++;
503 int new_level = last_level;
505 int new_level_up = nb_temperature_thresholds - 1;
506 while(new_level_up > 0 &&
507 temperature < temperature_thresholds[new_level_up]) {
511 if(new_level_up > last_level) {
512 new_level = new_level_up;
514 int new_level_down = nb_temperature_thresholds - 1;
515 while(new_level_down > 0 &&
517 temperature_thresholds[new_level_down] - down_temperature_delta)
519 if(new_level_down < last_level &&
520 nb_rounds_since_last_change * polling_delay >=
521 minimum_delay_to_go_down) {
522 new_level = new_level_down;
526 // We set it every time, even when there is no change, to handle
527 // when the level has been modified somewhere else (for instance
528 // when combing back from suspend).
530 set_fan_level(file_fan_fd, new_level, nb_temperature_thresholds - 1);
532 if(new_level != last_level) {
533 nb_rounds_since_last_change = 0;
534 last_level = new_level;
536 printf("Temperature is %dC setting the fan level to %d.",
537 temperature, new_level);
541 sleep(polling_delay);