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 #define BUFFER_SIZE 4096
36 const int major_version_number = 1;
37 const int minor_version_number = 2;
39 const int buffer_size = 1024;
41 const char *default_configuration_file = "/etc/breezed.conf";
43 // The time period to check the temperature.
44 const int polling_delay = 5;
46 // Minimum time between last change and a change down.
47 const int minimum_delay_to_go_down = 30;
49 // Gap between a threshold to go up and a threshold to go down to
50 // reduce the oscillations.
51 const int down_temperature_delta = 2;
56 int nb_rounds_since_last_change = 0;
61 int nb_temperature_thresholds;
62 int *temperature_thresholds = 0;
64 int nb_file_thermal = 0;
65 char **file_thermal = 0;
66 int *file_thermal_fd = 0;
68 char *configuration_file;
70 //////////////////////////////////////////////////////////////////////
72 char *next_word(char *buffer, char *r, int buffer_size) {
77 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
80 while((*r != '"') && (*r != '\0') &&
81 (s<buffer+buffer_size-1))
85 while((*r != '\r') && (*r != '\n') && (*r != '\0') &&
86 (*r != '\t') && (*r != ' ') && (*r != ',')) {
87 if(s == buffer + buffer_size) {
88 fprintf(stderr, "Buffer overflow in next_word.\n");
95 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
96 if((*r == '\0') || (*r=='\r') || (*r=='\n')) r = 0;
103 void set_fan_level(int fan_fd, int f, int max_fan_level) {
104 char buffer[buffer_size];
105 if(f < 0 || f > max_fan_level) f = max_fan_level;
106 sprintf(buffer, "level %d\n", f);
107 if(write(fan_fd, buffer, strlen(buffer)) < 0) {
108 fprintf(stderr, "Error in setting the fan level (%s).\n",
114 void define_thermal_files(char *definition) {
115 char token[buffer_size];
118 fprintf(stderr, "Thermal files already defined.\n");
125 s = next_word(token, s, buffer_size);
128 file_thermal = new char *[nb_file_thermal];
129 file_thermal_fd = new int[nb_file_thermal];
133 s = next_word(token, s, buffer_size);
134 file_thermal[k] = new char[strlen(token) + 1];
135 strcpy(file_thermal[k], token);
140 void define_temperature_thresholds(char *definition) {
141 char token[buffer_size];
143 if(temperature_thresholds) {
144 fprintf(stderr, "Temperature thresholds already defined.\n");
148 nb_temperature_thresholds = 1;
153 s = next_word(token, s, buffer_size);
154 nb_temperature_thresholds++;
157 temperature_thresholds = new int[nb_temperature_thresholds];
159 temperature_thresholds[0] = -1;
164 s = next_word(token, s, buffer_size);
165 temperature_thresholds[k] = atoi(token);
167 temperature_thresholds[k] < temperature_thresholds[k-1]) {
168 fprintf(stderr, "The temperature thresholds have to be increasing.\n");
175 //////////////////////////////////////////////////////////////////////
177 int main(int argc, char **argv) {
179 char buffer[buffer_size], token[buffer_size];
181 configuration_file = new char[strlen(default_configuration_file) + 1];
182 strcpy(configuration_file, default_configuration_file);
187 if(strcmp(argv[i], "--debug") == 0 || strcmp(argv[i], "-d") == 0) {
192 else if(strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
193 printf("Breezed v%d.%d. Written by Francois Fleuret (francois@fleuret.org).\n",
194 major_version_number, minor_version_number);
198 else if(strcmp(argv[i], "--no-configuration-file") == 0 ||
199 strcmp(argv[i], "-ncf") == 0) {
200 delete[] configuration_file;
201 configuration_file = 0;
205 else if(strcmp(argv[i], "--configuration-file") == 0 ||
206 strcmp(argv[i], "-cf") == 0) {
209 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
213 delete[] configuration_file;
214 configuration_file = new char[strlen(argv[i]) + 1];
215 strcpy(configuration_file, argv[i]);
220 else if(strcmp(argv[i], "--thermal-files") == 0 ||
221 strcmp(argv[i], "-tf") == 0) {
224 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
227 define_thermal_files(argv[i]);
231 else if(strcmp(argv[i], "--fan-file") == 0 ||
232 strcmp(argv[i], "-ff") == 0) {
235 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
240 fprintf(stderr, "Fan file already defined.\n");
243 file_fan = new char[strlen(argv[i]) + 1];
244 strcpy(file_fan, argv[i]);
249 else if(strcmp(argv[i], "--temperature-thresholds") == 0 ||
250 strcmp(argv[i], "-tt") == 0) {
254 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
258 define_temperature_thresholds(argv[i]);
262 else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
264 printf("%s [--version|-v] [--help|-h] [--debug|-d]\\\n\
265 [--configuration-file|-cf <file>]\\\n\
266 [--no-configuration-file|-ncf]\\\n\
267 [--thermal-files|-tf <thermalfile1,thermalfile2,...>] \\\n\
268 [--fanfile|-ff <fan file>] \\\n\
269 [--temperature-thresholds|-tt <t1,t2,t3,t4,...>]\n\
271 --help|-h : shows this help\n\
272 --version|-v : shows the version number\n\
273 --debug|-d : prints out additional information\n\
274 --configuration-file|-cf sets the configuration file\n\
275 --no-configuration-file|-ncf do not load a configuration file\n\
276 --thermal-files|-tf : sets where to look for temperatures\n\
277 --fanfile|-ff : sets where to control the fan level\n\
278 --temperature-thresholds|-tt : sets the temperature thresholds\n\
280 This daemon polls the temperatures every 5s, takes the max and sets\n\
281 the fan level accordingly. It uses as temperatures all the numbers it\n\
282 finds in the provided \"thermal files\". Hence you can use as well\n\
283 /proc/acpi/thermal_zone/THM*/temperature or /proc/acpi/ibm/thermal.\n\
285 The fan speed is set by echoing into the provided fan file.\n\
287 To reduce oscillations, it will not reduce the fan speed less than 30s\n\
288 after the last previous change and the thresholds actually used to\n\
289 reduce the fan speed are two degrees lower than the provided\n\
290 thresholds, which are used to increase the fan speed.\n\
292 This daemon should be started through the adequate shell script in\n\
295 Options can be set either in the configuration file, or on the \n\
296 command line. Options can not be set twice.\n\
298 Version %d.%d, November 2009.\n\
300 Written by Francois Fleuret (francois@fleuret.org).\n",
302 major_version_number, minor_version_number);
308 fprintf(stderr, "Unknown argument %s.\n", argv[i]);
314 //////////////////////////////////////////////////////////////////////
316 if(configuration_file) {
317 char raw_line[BUFFER_SIZE];
318 int start, end, eol, k;
321 file = fopen(configuration_file, "r");
324 fprintf(stderr, "Can not open `%s' for reading.\n", configuration_file);
335 while(end > start || !feof(file)) {
338 /* Look for the end of a line in what is already in the buffer */
339 while(eol < end && raw_line[eol] != '\n') eol++;
341 /* if we did not find the of a line, move what has not been
342 processed and is in the buffer to the beginning of the buffer,
343 fill the buffer with new data from the file, and look for the
346 for(k = 0; k < end - start; k++) {
347 raw_line[k] = raw_line[k + start];
352 end += fread(raw_line + end, sizeof(char), BUFFER_SIZE - end, file);
353 while(eol < end && raw_line[eol] != '\n') eol++;
356 /* The end of the line is the buffer size, which means the line is
359 if(eol == BUFFER_SIZE) {
360 raw_line[BUFFER_SIZE - 1] = '\0';
361 fprintf(stderr, "Selector: Line too long (max is %d characters):\n",
363 fprintf(stderr, raw_line);
364 fprintf(stderr, "\n");
368 /* If we got a line, we replace the carriage return by a \0 to
371 raw_line[eol] = '\0';
373 /* here we process the line */
378 printf("%s:%d \"%s\"\n",
379 configuration_file, line_number, raw_line + start);
382 s = next_word(token, raw_line + start, buffer_size);
384 if(strcmp(token, "thermal_files") == 0) {
386 fprintf(stderr, "Missing parameter in %s:%d\n",
387 configuration_file, line_number);
390 define_thermal_files(s);
393 else if(strcmp(token, "debug") == 0) {
397 else if(strcmp(token, "fan_file") == 0) {
399 fprintf(stderr, "Fan file already defined.\n");
403 fprintf(stderr, "Missing parameter in %s:%d\n",
404 configuration_file, line_number);
407 file_fan = new char[strlen(s) + 1];
411 else if(strcmp(token, "temperature_thresholds") == 0) {
413 fprintf(stderr, "Missing parameter in %s:%d\n",
414 configuration_file, line_number);
417 define_temperature_thresholds(s);
420 else if(token[0] && token[0] != '#') {
421 fprintf(stderr, "Unknown keyword '%s' in %s:%d.\n",
422 token, configuration_file, 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(int 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(int 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(int 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(int 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);