4 breezed is a fan speed control daemon for Linux computers.
6 Copyright (c) 2008 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 = 0;
42 const int buffer_size = 1024;
44 const char *default_confguration_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 = 0;
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;
154 s = next_word(token, s, buffer_size);
155 nb_temperature_thresholds++;
158 temperature_thresholds = new int[nb_temperature_thresholds];
163 s = next_word(token, s, buffer_size);
164 temperature_thresholds[k] = atoi(token);
166 temperature_thresholds[k] < temperature_thresholds[k-1]) {
167 cerr << "The temperature thresholds have to be increasing."
174 temperature_thresholds[0] = -1;
177 //////////////////////////////////////////////////////////////////////
179 int main(int argc, char **argv) {
181 char buffer[buffer_size], token[buffer_size];
183 configuration_file = new char[strlen(default_confguration_file) + 1];
184 strcpy(configuration_file, default_confguration_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) {
196 << "v" << major_version_number
197 << "." << minor_version_number
199 << "Written by Francois Fleuret (francois@fleuret.org).\n";
203 else if(strcmp(argv[i], "--no-configuration-file") == 0 ||
204 strcmp(argv[i], "-ncf") == 0) {
205 delete[] configuration_file;
206 configuration_file = 0;
210 else if(strcmp(argv[i], "--configuration-file") == 0 ||
211 strcmp(argv[i], "-cf") == 0) {
214 cerr << "Missing parameter for " << argv[i - 1] << endl;
218 delete[] configuration_file;
219 configuration_file = new char[strlen(argv[i]) + 1];
220 strcpy(configuration_file, argv[i]);
225 else if(strcmp(argv[i], "--thermal-files") == 0 ||
226 strcmp(argv[i], "-tf") == 0) {
229 cerr << "Missing parameter for " << argv[i - 1] << endl;
232 define_thermal_files(argv[i]);
236 else if(strcmp(argv[i], "--fan-file") == 0 ||
237 strcmp(argv[i], "-ff") == 0) {
240 cerr << "Missing parameter for " << argv[i - 1] << endl;
245 cerr << "Fan file already defined." << endl;
248 file_fan = new char[strlen(argv[i]) + 1];
249 strcpy(file_fan, argv[i]);
254 else if(strcmp(argv[i], "--temperature-thresholds") == 0 ||
255 strcmp(argv[i], "-tt") == 0) {
259 cerr << "Missing parameter for " << argv[i - 1] << endl;
263 define_temperature_thresholds(argv[i]);
267 else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
269 cout << argv[0] << " [--version|-v] [--help|-h] [--debug|-d]\\\n\
270 [--configuration-file|-cf <file>]\\\n\
271 [--no-configuration-file|-ncf]\\\n\
272 [--thermal-files|-tf <thermalfile1,thermalfile2,...>] \\\n\
273 [--fanfile|-ff <fan file>] \\\n\
274 [--temperature-thresholds|-tt <t1,t2,t3,t4,...>]\n\
276 --help|-h : shows this help\n\
277 --version|-v : shows the version number\n\
278 --debug|-d : prints out additional information\n\
279 --configuration-file|-cf sets the configuration file\n\
280 --no-configuration-file|-ncf do not load a configuration file\n\
281 --thermal-files|-tf : sets where to look for temperatures\n\
282 --fanfile|-ff : sets where to control the fan level\n\
283 --temperature-thresholds|-tt : sets the temperature thresholds\n\
285 This daemon polls the temperatures every 5s, takes the max and sets\n\
286 the fan level accordingly. It uses as temperatures all the numbers it\n\
287 finds in the provided \"thermal files\". Hence you can use as well\n\
288 /proc/acpi/thermal_zone/THM*/temperature or /proc/acpi/ibm/thermal.\n\
290 The fan speed is set by echoing into the provided fan file.\n\
292 To reduce oscillations, it will not reduce the fan speed less than 30s\n\
293 after the last previous change and the thresholds actually used to\n\
294 reduce the fan speed are two degrees lower than the provided\n\
295 thresholds, which are used to increase the fan speed.\n\
297 This daemon should be started through the adequate shell script in\n\
300 Options can be set either in the configuration file, or on the \n\
301 command line. Options can not be set twice.\n\
303 Version " << major_version_number << "."
304 << minor_version_number << ", December 2008.\n\
306 Written by Francois Fleuret (francois@fleuret.org).\n";
312 cerr << "Unknown argument " << argv[i] << "." << endl;
318 //////////////////////////////////////////////////////////////////////
320 if(configuration_file) {
321 ifstream cf(configuration_file);
323 cerr << "Can not open " << configuration_file << " for reading." << endl;
332 cf.getline(buffer, buffer_size);
335 s = next_word(token, buffer, buffer_size);
337 if(strcmp(token, "thermal_files") == 0) {
339 cerr << "Missing parameter in "
340 << configuration_file << ":" << line_number << endl;
343 define_thermal_files(s);
346 else if(strcmp(token, "debug") == 0) {
350 else if(strcmp(token, "fan_file") == 0) {
352 cerr << "Fan file already defined." << endl;
356 cerr << "Missing parameter in "
357 << configuration_file << ":" << line_number << endl;
360 file_fan = new char[strlen(s) + 1];
364 else if(strcmp(token, "temperature_thresholds") == 0) {
366 cerr << "Missing parameter in "
367 << configuration_file << ":" << line_number << endl;
370 define_temperature_thresholds(s);
373 else if(token[0] && token[0] != '#') {
374 cerr << "Unknown keyword '" << token << "' in "
375 << configuration_file << ":" << line_number << endl;
381 //////////////////////////////////////////////////////////////////////
383 if(nb_temperature_thresholds == 0) {
384 cerr << "No temperature threshold was provided." << endl;
388 if(nb_file_thermal == 0) {
389 cerr << "No thermal file was provided." << endl;
394 cerr << "No fan file was provided." << endl;
398 for(int i = 0; i < nb_file_thermal; i++) {
399 file_thermal_fd[i] = open(file_thermal[i], O_RDONLY);
400 if(file_thermal_fd[i] < 0) {
401 cerr << "Can not open " << file_thermal[i]
402 << " for reading (" << strerror(errno) << ")."
408 file_fan_fd = open(file_fan, O_WRONLY);
410 if(file_fan_fd < 0) {
411 cerr << "Can not open " << file_fan
412 << " for writing (" << strerror(errno) << ")."
417 //////////////////////////////////////////////////////////////////////
420 for(int t = 0; t < nb_file_thermal; t++) {
421 cout << "file_thermal[" << t << "] " << file_thermal[t] << endl;
424 cout << "file_fan " << file_fan << endl;
426 for(int t = 0; t < nb_temperature_thresholds; t++) {
427 cout << "temperature_thresholds[" << t << "] "
428 << temperature_thresholds[t] << endl;
432 //////////////////////////////////////////////////////////////////////
436 int temperature = -1;
439 cout << "Temperature:";
442 for(int i = 0; i < nb_file_thermal; i++) {
443 lseek(file_thermal_fd[i], 0, SEEK_SET);
444 int size = read(file_thermal_fd[i], buffer, buffer_size);
446 if(size > 0 && size < buffer_size) {
452 while(*s && *s != '-' && (*s < '0') || (*s > '9')) s++;
461 while(*s >= '0' && *s <= '9') {
462 t = t * 10 + (*s - '0');
471 if(t > temperature) temperature = t;
476 if(i < nb_file_thermal - 1)
483 cerr << "Nothing to read in " << file_thermal[i] << endl;
484 } else if(size < 0) {
485 cerr << "Error while reading " << file_thermal[i]
486 << " (" << strerror(errno) << ")." << endl;
488 cerr << "Error while reading " << file_thermal[i]
489 << " (too large)." << endl;
495 if(temperature < 0) {
496 cerr << "Could not read a meaningful temperature." << endl;
500 nb_rounds_since_last_change++;
502 int new_level = last_level;
504 int new_level_up = nb_temperature_thresholds - 1;
505 while(new_level_up > 0 &&
506 temperature < temperature_thresholds[new_level_up]) {
510 if(new_level_up > last_level) {
511 new_level = new_level_up;
513 int new_level_down = nb_temperature_thresholds - 1;
514 while(new_level_down > 0 &&
516 temperature_thresholds[new_level_down] - down_temperature_delta)
518 if(new_level_down < last_level &&
519 nb_rounds_since_last_change * polling_delay >=
520 minimum_delay_to_go_down) {
521 new_level = new_level_down;
525 // We set it every time, even when there is no change, to handle
526 // when the level has been modified somewhere else (for instance
527 // when combing back from suspend).
529 set_fan_level(file_fan_fd, new_level, nb_temperature_thresholds - 1);
531 if(new_level != last_level) {
532 nb_rounds_since_last_change = 0;
533 last_level = new_level;
535 cout << "Temperature is " << temperature << "C,"
536 << " setting the fan level to " << new_level
541 sleep(polling_delay);