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/>.
32 const int major_version_number = 1;
33 const int minor_version_number = 2;
35 const int buffer_size = 1024;
37 const char *default_configuration_file = "/etc/breezed.conf";
39 /* The time period to check the temperature. */
40 const int polling_delay = 5;
42 /* Minimum time between last change and a change down. */
43 const int minimum_delay_to_go_down = 30;
45 /* Gap between a threshold to go up and a threshold to go down to
46 reduce the oscillations. */
47 const int down_temperature_delta = 2;
52 int nb_rounds_since_last_change = 0;
57 int nb_temperature_thresholds;
58 int *temperature_thresholds = 0;
60 int nb_file_thermal = 0;
61 char **file_thermal = 0;
62 int *file_thermal_fd = 0;
64 char *configuration_file;
66 /******************************************************************/
68 char *next_word(char *buffer, char *r, int buffer_size) {
73 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
76 while((*r != '"') && (*r != '\0') &&
77 (s<buffer+buffer_size-1))
81 while((*r != '\r') && (*r != '\n') && (*r != '\0') &&
82 (*r != '\t') && (*r != ' ') && (*r != ',')) {
83 if(s == buffer + buffer_size) {
84 fprintf(stderr, "Buffer overflow in next_word.\n");
91 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
92 if((*r == '\0') || (*r=='\r') || (*r=='\n')) r = 0;
99 void set_fan_level(int fan_fd, int f, int max_fan_level) {
100 char buffer[buffer_size];
101 if(f < 0 || f > max_fan_level) f = max_fan_level;
102 sprintf(buffer, "level %d\n", f);
103 if(write(fan_fd, buffer, strlen(buffer)) < 0) {
104 fprintf(stderr, "Error in setting the fan level (%s).\n",
110 void define_thermal_files(char *definition) {
111 char token[buffer_size];
114 fprintf(stderr, "Thermal files already defined.\n");
121 s = next_word(token, s, buffer_size);
125 file_thermal = (char **) malloc(nb_file_thermal * sizeof(char *));
126 file_thermal_fd = (int *) malloc(nb_file_thermal * sizeof(int));
130 s = next_word(token, s, buffer_size);
131 file_thermal[k] = strdup(token);
136 void define_temperature_thresholds(char *definition) {
137 char token[buffer_size];
139 if(temperature_thresholds) {
140 fprintf(stderr, "Temperature thresholds already defined.\n");
144 nb_temperature_thresholds = 1;
149 s = next_word(token, s, buffer_size);
150 nb_temperature_thresholds++;
153 temperature_thresholds =
154 (int *) malloc(nb_temperature_thresholds * sizeof(int));
156 temperature_thresholds[0] = -1;
161 s = next_word(token, s, buffer_size);
162 temperature_thresholds[k] = atoi(token);
164 temperature_thresholds[k] < temperature_thresholds[k-1]) {
165 fprintf(stderr, "The temperature thresholds have to be increasing.\n");
172 void evaluate_one_configuration_line(char *line, int line_number) {
173 char token[buffer_size];
176 s = next_word(token, line, buffer_size);
178 if(strcmp(token, "thermal_files") == 0) {
180 fprintf(stderr, "Missing parameter in %s:%d\n",
181 configuration_file, line_number);
184 define_thermal_files(s);
187 else if(strcmp(token, "debug") == 0) {
191 else if(strcmp(token, "fan_file") == 0) {
193 fprintf(stderr, "Fan file already defined.\n");
197 fprintf(stderr, "Missing parameter in %s:%d\n",
198 configuration_file, line_number);
201 file_fan = strdup(s);
204 else if(strcmp(token, "temperature_thresholds") == 0) {
206 fprintf(stderr, "Missing parameter in %s:%d\n",
207 configuration_file, line_number);
210 define_temperature_thresholds(s);
213 else if(token[0] && token[0] != '#') {
214 fprintf(stderr, "Unknown keyword '%s' in %s:%d.\n",
215 token, configuration_file, line_number);
220 /******************************************************************/
222 int main(int argc, char **argv) {
223 char buffer[buffer_size];
226 configuration_file = strdup(default_configuration_file);
231 if(strcmp(argv[i], "--debug") == 0 || strcmp(argv[i], "-d") == 0) {
236 else if(strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
237 printf("Breezed v%d.%d. Written by Francois Fleuret (francois@fleuret.org).\n",
238 major_version_number, minor_version_number);
242 else if(strcmp(argv[i], "--no-configuration-file") == 0 ||
243 strcmp(argv[i], "-ncf") == 0) {
244 free(configuration_file);
245 configuration_file = 0;
249 else if(strcmp(argv[i], "--configuration-file") == 0 ||
250 strcmp(argv[i], "-cf") == 0) {
253 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
257 free(configuration_file);
258 configuration_file = strdup(argv[i]);
263 else if(strcmp(argv[i], "--thermal-files") == 0 ||
264 strcmp(argv[i], "-tf") == 0) {
267 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
270 define_thermal_files(argv[i]);
274 else if(strcmp(argv[i], "--fan-file") == 0 ||
275 strcmp(argv[i], "-ff") == 0) {
278 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
283 fprintf(stderr, "Fan file already defined.\n");
286 file_fan = strdup(argv[i]);
291 else if(strcmp(argv[i], "--temperature-thresholds") == 0 ||
292 strcmp(argv[i], "-tt") == 0) {
296 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
300 define_temperature_thresholds(argv[i]);
304 else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
306 printf("%s [--version|-v] [--help|-h] [--debug|-d]\\\n\
307 [--configuration-file|-cf <file>]\\\n\
308 [--no-configuration-file|-ncf]\\\n\
309 [--thermal-files|-tf <thermalfile1,thermalfile2,...>] \\\n\
310 [--fanfile|-ff <fan file>] \\\n\
311 [--temperature-thresholds|-tt <t1,t2,t3,t4,...>]\n\
313 --help|-h : shows this help\n\
314 --version|-v : shows the version number\n\
315 --debug|-d : prints out additional information\n\
316 --configuration-file|-cf sets the configuration file\n\
317 --no-configuration-file|-ncf do not load a configuration file\n\
318 --thermal-files|-tf : sets where to look for temperatures\n\
319 --fanfile|-ff : sets where to control the fan level\n\
320 --temperature-thresholds|-tt : sets the temperature thresholds\n\
322 This daemon polls the temperatures every 5s, takes the max and sets\n\
323 the fan level accordingly. It uses as temperatures all the numbers it\n\
324 finds in the provided \"thermal files\". Hence you can use as well\n\
325 /proc/acpi/thermal_zone/THM*/temperature or /proc/acpi/ibm/thermal.\n\
327 The fan speed is set by echoing into the provided fan file.\n\
329 To reduce oscillations, it will not reduce the fan speed less than 30s\n\
330 after the last previous change and the thresholds actually used to\n\
331 reduce the fan speed are two degrees lower than the provided\n\
332 thresholds, which are used to increase the fan speed.\n\
334 This daemon should be started through the adequate shell script in\n\
337 Options can be set either in the configuration file, or on the \n\
338 command line. Options can not be set twice.\n\
340 Version %d.%d, November 2009.\n\
342 Written by Francois Fleuret (francois@fleuret.org).\n",
344 major_version_number, minor_version_number);
350 fprintf(stderr, "Unknown argument %s.\n", argv[i]);
356 /******************************************************************/
358 if(configuration_file) {
359 char raw_line[buffer_size];
360 int start, end, eol, k;
363 file = fopen(configuration_file, "r");
366 fprintf(stderr, "Can not open `%s' for reading.\n", configuration_file);
377 while(end > start || !feof(file)) {
380 /* Look for the end of a line in what is already in the buffer */
381 while(eol < end && raw_line[eol] != '\n') eol++;
383 /* if we did not find the of a line, move what has not been
384 processed and is in the buffer to the beginning of the buffer,
385 fill the buffer with new data from the file, and look for the
388 for(k = 0; k < end - start; k++) {
389 raw_line[k] = raw_line[k + start];
394 end += fread(raw_line + end, sizeof(char), buffer_size - end, file);
395 while(eol < end && raw_line[eol] != '\n') eol++;
398 /* The end of the line is the buffer size, which means the line is
401 if(eol == buffer_size) {
402 raw_line[buffer_size - 1] = '\0';
403 fprintf(stderr, "Selector: Line too long (max is %d characters):\n",
405 fprintf(stderr, raw_line);
406 fprintf(stderr, "\n");
410 /* If we got a line, we replace the carriage return by a \0 to
413 raw_line[eol] = '\0';
415 /* here we process the line */
420 printf("%s:%d \"%s\"\n",
421 configuration_file, line_number, raw_line + start);
424 evaluate_one_configuration_line(raw_line + start, line_number);
431 /******************************************************************/
433 if(nb_temperature_thresholds == 0) {
434 fprintf(stderr, "No temperature threshold was provided.\n");
438 if(nb_file_thermal == 0) {
439 fprintf(stderr, "No thermal file was provided.\n");
444 fprintf(stderr, "No fan file was provided.\n");
448 for(i = 0; i < nb_file_thermal; i++) {
449 file_thermal_fd[i] = open(file_thermal[i], O_RDONLY);
450 if(file_thermal_fd[i] < 0) {
451 fprintf(stderr, "Can not open %s for reading (%s).\n",
452 file_thermal[i], strerror(errno));
457 file_fan_fd = open(file_fan, O_WRONLY);
459 if(file_fan_fd < 0) {
460 fprintf(stderr, "Can not open %s for writing (%s).\n",
461 file_fan, strerror(errno));
465 /******************************************************************/
468 for(t = 0; t < nb_file_thermal; t++) {
469 printf("file_thermal[%d] %s\n", t, file_thermal[t]);
472 printf("file_fan %s\n", file_fan);
474 for(t = 0; t < nb_temperature_thresholds; t++) {
475 printf("temperature_thresholds[%d] %d",
476 t, temperature_thresholds[t]);
480 /******************************************************************/
484 int temperature = -1;
487 printf("Temperature:");
490 for(i = 0; i < nb_file_thermal; i++) {
491 lseek(file_thermal_fd[i], 0, SEEK_SET);
492 int size = read(file_thermal_fd[i], buffer, buffer_size);
494 if(size > 0 && size < buffer_size) {
500 while(*s && *s != '-' && (*s < '0') || (*s > '9')) s++;
509 while(*s >= '0' && *s <= '9') {
510 t = t * 10 + (*s - '0');
519 /* So that we can deal with the new files where the
520 temperature are in 1/1000th of C */
521 if(t > 1000) t /= 1000;
523 if(t > temperature) temperature = t;
528 if(i < nb_file_thermal - 1)
535 fprintf(stderr, "Nothing to read in %d.\n", file_thermal[i]);
536 } else if(size < 0) {
537 fprintf(stderr, "Error while reading %s (%s).\n",
538 file_thermal[i], strerror(errno));
540 fprintf(stderr, "Error while reading %s (too large).\n",
547 if(temperature < 0) {
548 fprintf(stderr, "Could not read a meaningful temperature.\n");
552 nb_rounds_since_last_change++;
554 int new_level = last_level;
556 int new_level_up = nb_temperature_thresholds - 1;
557 while(new_level_up > 0 &&
558 temperature < temperature_thresholds[new_level_up]) {
562 if(new_level_up > last_level) {
563 new_level = new_level_up;
565 int new_level_down = nb_temperature_thresholds - 1;
566 while(new_level_down > 0 &&
568 temperature_thresholds[new_level_down] - down_temperature_delta)
570 if(new_level_down < last_level &&
571 nb_rounds_since_last_change * polling_delay >=
572 minimum_delay_to_go_down) {
573 new_level = new_level_down;
577 /* We set it every time, even when there is no change, to handle
578 when the level has been modified somewhere else (for instance
579 when combing back from suspend). */
581 set_fan_level(file_fan_fd, new_level, nb_temperature_thresholds - 1);
583 if(new_level != last_level) {
584 nb_rounds_since_last_change = 0;
585 last_level = new_level;
587 printf("Temperature is %dC setting the fan level to %d.",
588 temperature, new_level);
592 sleep(polling_delay);