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/>.
31 #include <sys/types.h>
39 const int major_version_number = 1;
40 const int minor_version_number = 2;
42 const int buffer_size = 1024;
44 const char *default_configuration_file = "/etc/breezed.conf";
46 // The time period to check the temperature.
47 const int polling_delay = 5;
49 // Minimum time between last change and a change down.
50 const int minimum_delay_to_go_down = 30;
52 // Gap between a threshold to go up and a threshold to go down to
53 // reduce the oscillations.
54 const int down_temperature_delta = 2;
59 int nb_rounds_since_last_change = 0;
64 int nb_temperature_thresholds;
65 int *temperature_thresholds = 0;
67 int nb_file_thermal = 0;
68 char **file_thermal = 0;
69 int *file_thermal_fd = 0;
71 char *configuration_file;
73 //////////////////////////////////////////////////////////////////////
75 char *next_word(char *buffer, char *r, int buffer_size) {
80 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
83 while((*r != '"') && (*r != '\0') &&
84 (s<buffer+buffer_size-1))
88 while((*r != '\r') && (*r != '\n') && (*r != '\0') &&
89 (*r != '\t') && (*r != ' ') && (*r != ',')) {
90 if(s == buffer + buffer_size) {
91 cerr << "Buffer overflow in next_word." << endl;
98 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
99 if((*r == '\0') || (*r=='\r') || (*r=='\n')) r = 0;
106 void set_fan_level(int fan_fd, int f, int max_fan_level) {
107 char buffer[buffer_size];
108 if(f < 0 || f > max_fan_level) f = max_fan_level;
109 sprintf(buffer, "level %d\n", f);
110 if(write(fan_fd, buffer, strlen(buffer)) < 0) {
111 cerr << "Error in setting the fan level (" << strerror(errno) << ")."
117 void define_thermal_files(char *definition) {
118 char token[buffer_size];
121 cerr << "Thermal files already defined." << endl;
128 s = next_word(token, s, buffer_size);
131 file_thermal = new char *[nb_file_thermal];
132 file_thermal_fd = new int[nb_file_thermal];
136 s = next_word(token, s, buffer_size);
137 file_thermal[k] = new char[strlen(token) + 1];
138 strcpy(file_thermal[k], token);
143 void define_temperature_thresholds(char *definition) {
144 char token[buffer_size];
146 if(temperature_thresholds) {
147 cerr << "Temperature thresholds already defined." << endl;
151 nb_temperature_thresholds = 1;
156 s = next_word(token, s, buffer_size);
157 nb_temperature_thresholds++;
160 temperature_thresholds = new int[nb_temperature_thresholds];
162 temperature_thresholds[0] = -1;
167 s = next_word(token, s, buffer_size);
168 temperature_thresholds[k] = atoi(token);
170 temperature_thresholds[k] < temperature_thresholds[k-1]) {
171 cerr << "The temperature thresholds have to be increasing."
179 //////////////////////////////////////////////////////////////////////
181 int main(int argc, char **argv) {
183 char buffer[buffer_size], token[buffer_size];
185 configuration_file = new char[strlen(default_configuration_file) + 1];
186 strcpy(configuration_file, default_configuration_file);
191 if(strcmp(argv[i], "--debug") == 0 || strcmp(argv[i], "-d") == 0) {
196 else if(strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
198 << "v" << major_version_number
199 << "." << minor_version_number
201 << "Written by Francois Fleuret (francois@fleuret.org).\n";
205 else if(strcmp(argv[i], "--no-configuration-file") == 0 ||
206 strcmp(argv[i], "-ncf") == 0) {
207 delete[] configuration_file;
208 configuration_file = 0;
212 else if(strcmp(argv[i], "--configuration-file") == 0 ||
213 strcmp(argv[i], "-cf") == 0) {
216 cerr << "Missing parameter for " << argv[i - 1] << endl;
220 delete[] configuration_file;
221 configuration_file = new char[strlen(argv[i]) + 1];
222 strcpy(configuration_file, argv[i]);
227 else if(strcmp(argv[i], "--thermal-files") == 0 ||
228 strcmp(argv[i], "-tf") == 0) {
231 cerr << "Missing parameter for " << argv[i - 1] << endl;
234 define_thermal_files(argv[i]);
238 else if(strcmp(argv[i], "--fan-file") == 0 ||
239 strcmp(argv[i], "-ff") == 0) {
242 cerr << "Missing parameter for " << argv[i - 1] << endl;
247 cerr << "Fan file already defined." << endl;
250 file_fan = new char[strlen(argv[i]) + 1];
251 strcpy(file_fan, argv[i]);
256 else if(strcmp(argv[i], "--temperature-thresholds") == 0 ||
257 strcmp(argv[i], "-tt") == 0) {
261 cerr << "Missing parameter for " << argv[i - 1] << endl;
265 define_temperature_thresholds(argv[i]);
269 else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
271 cout << argv[0] << " [--version|-v] [--help|-h] [--debug|-d]\\\n\
272 [--configuration-file|-cf <file>]\\\n\
273 [--no-configuration-file|-ncf]\\\n\
274 [--thermal-files|-tf <thermalfile1,thermalfile2,...>] \\\n\
275 [--fanfile|-ff <fan file>] \\\n\
276 [--temperature-thresholds|-tt <t1,t2,t3,t4,...>]\n\
278 --help|-h : shows this help\n\
279 --version|-v : shows the version number\n\
280 --debug|-d : prints out additional information\n\
281 --configuration-file|-cf sets the configuration file\n\
282 --no-configuration-file|-ncf do not load a configuration file\n\
283 --thermal-files|-tf : sets where to look for temperatures\n\
284 --fanfile|-ff : sets where to control the fan level\n\
285 --temperature-thresholds|-tt : sets the temperature thresholds\n\
287 This daemon polls the temperatures every 5s, takes the max and sets\n\
288 the fan level accordingly. It uses as temperatures all the numbers it\n\
289 finds in the provided \"thermal files\". Hence you can use as well\n\
290 /proc/acpi/thermal_zone/THM*/temperature or /proc/acpi/ibm/thermal.\n\
292 The fan speed is set by echoing into the provided fan file.\n\
294 To reduce oscillations, it will not reduce the fan speed less than 30s\n\
295 after the last previous change and the thresholds actually used to\n\
296 reduce the fan speed are two degrees lower than the provided\n\
297 thresholds, which are used to increase the fan speed.\n\
299 This daemon should be started through the adequate shell script in\n\
302 Options can be set either in the configuration file, or on the \n\
303 command line. Options can not be set twice.\n\
305 Version " << major_version_number << "."
306 << minor_version_number << ", December 2008.\n\
308 Written by Francois Fleuret (francois@fleuret.org).\n";
314 cerr << "Unknown argument " << argv[i] << "." << endl;
320 //////////////////////////////////////////////////////////////////////
322 if(configuration_file) {
323 ifstream cf(configuration_file);
326 cerr << "Can not open " << configuration_file << " for reading." << endl;
335 cf.getline(buffer, buffer_size);
338 s = next_word(token, buffer, buffer_size);
340 if(strcmp(token, "thermal_files") == 0) {
342 cerr << "Missing parameter in "
343 << configuration_file << ":" << line_number << endl;
346 define_thermal_files(s);
349 else if(strcmp(token, "debug") == 0) {
353 else if(strcmp(token, "fan_file") == 0) {
355 cerr << "Fan file already defined." << endl;
359 cerr << "Missing parameter in "
360 << configuration_file << ":" << line_number << endl;
363 file_fan = new char[strlen(s) + 1];
367 else if(strcmp(token, "temperature_thresholds") == 0) {
369 cerr << "Missing parameter in "
370 << configuration_file << ":" << line_number << endl;
373 define_temperature_thresholds(s);
376 else if(token[0] && token[0] != '#') {
377 cerr << "Unknown keyword '" << token << "' in "
378 << configuration_file << ":" << line_number << endl;
384 //////////////////////////////////////////////////////////////////////
386 if(nb_temperature_thresholds == 0) {
387 cerr << "No temperature threshold was provided." << endl;
391 if(nb_file_thermal == 0) {
392 cerr << "No thermal file was provided." << endl;
397 cerr << "No fan file was provided." << endl;
401 for(int i = 0; i < nb_file_thermal; i++) {
402 file_thermal_fd[i] = open(file_thermal[i], O_RDONLY);
403 if(file_thermal_fd[i] < 0) {
404 cerr << "Can not open " << file_thermal[i]
405 << " for reading (" << strerror(errno) << ")."
411 file_fan_fd = open(file_fan, O_WRONLY);
413 if(file_fan_fd < 0) {
414 cerr << "Can not open " << file_fan
415 << " for writing (" << strerror(errno) << ")."
420 //////////////////////////////////////////////////////////////////////
423 for(int t = 0; t < nb_file_thermal; t++) {
424 cout << "file_thermal[" << t << "] " << file_thermal[t] << endl;
427 cout << "file_fan " << file_fan << endl;
429 for(int t = 0; t < nb_temperature_thresholds; t++) {
430 cout << "temperature_thresholds[" << t << "] "
431 << temperature_thresholds[t] << endl;
435 //////////////////////////////////////////////////////////////////////
439 int temperature = -1;
442 cout << "Temperature:";
445 for(int i = 0; i < nb_file_thermal; i++) {
446 lseek(file_thermal_fd[i], 0, SEEK_SET);
447 int size = read(file_thermal_fd[i], buffer, buffer_size);
449 if(size > 0 && size < buffer_size) {
455 while(*s && *s != '-' && (*s < '0') || (*s > '9')) s++;
464 while(*s >= '0' && *s <= '9') {
465 t = t * 10 + (*s - '0');
474 // So that we can deal with the new files where the
475 // temperature are in 1/1000th of C
476 if(t > 1000) t /= 1000;
478 if(t > temperature) temperature = t;
483 if(i < nb_file_thermal - 1)
490 cerr << "Nothing to read in " << file_thermal[i] << endl;
491 } else if(size < 0) {
492 cerr << "Error while reading " << file_thermal[i]
493 << " (" << strerror(errno) << ")." << endl;
495 cerr << "Error while reading " << file_thermal[i]
496 << " (too large)." << endl;
502 if(temperature < 0) {
503 cerr << "Could not read a meaningful temperature." << endl;
507 nb_rounds_since_last_change++;
509 int new_level = last_level;
511 int new_level_up = nb_temperature_thresholds - 1;
512 while(new_level_up > 0 &&
513 temperature < temperature_thresholds[new_level_up]) {
517 if(new_level_up > last_level) {
518 new_level = new_level_up;
520 int new_level_down = nb_temperature_thresholds - 1;
521 while(new_level_down > 0 &&
523 temperature_thresholds[new_level_down] - down_temperature_delta)
525 if(new_level_down < last_level &&
526 nb_rounds_since_last_change * polling_delay >=
527 minimum_delay_to_go_down) {
528 new_level = new_level_down;
532 // We set it every time, even when there is no change, to handle
533 // when the level has been modified somewhere else (for instance
534 // when combing back from suspend).
536 set_fan_level(file_fan_fd, new_level, nb_temperature_thresholds - 1);
538 if(new_level != last_level) {
539 nb_rounds_since_last_change = 0;
540 last_level = new_level;
542 cout << "Temperature is " << temperature << "C,"
543 << " setting the fan level to " << new_level
548 sleep(polling_delay);