root/lm-sensors/trunk/prog/pwm/fancontrol.pl @ 4295

Revision 4295, 10.2 KB (checked in by khali, 6 years ago)

fancontrol.pl:
* Disable debugging by default
* Add support for non-i2c drivers
* More tolerant config file parsing (for some reason pwmconfig

adds unneeded spaces, it should probably be fixed but it's always
better to be tolrant nevertheless)

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/perl -wT
2# $Id$
3#
4# Perl script for temperature dependent fan speed control.
5#
6# Copyright 2004 dean takemori <deant@hawaii.rr.com>
7#
8# This is a reimplementation in perl of Marius Reiner's bash script for
9# fan speed control.  It has advantages in that it can daemonize itself
10# and needn't spawn subprocesses for grep, sleep etc.  Much of the structure
11# of the bash script is preserved to make mirroring changes easier, so
12# this is seriously non-idiomatic perl but at the same time it should not
13# be considered a a direct bash to perl translation.
14#
15# Usage: fancontrol [CONFIGFILE]
16#
17# For configuration instructions and warnings please see fancontrol.txt,
18# which can be found in the doc/ directory or at the website mentioned
19# elsewhere.
20#
21# This script is derived from Marius Reiner's bash version, so it is
22# hereby placed under the GPL.
23#
24#    Copyright 2003 Marius Reiner <marius.reiner@hdev.de>
25#
26#    This program is free software; you can redistribute it and/or modify
27#    it under the terms of the GNU General Public License as published by
28#    the Free Software Foundation; either version 2 of the License, or
29#    (at your option) any later version.
30#
31#    This program is distributed in the hope that it will be useful,
32#    but WITHOUT ANY WARRANTY; without even the implied warranty of
33#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34#    GNU General Public License for more details.
35#
36#    You should have received a copy of the GNU General Public License
37#    along with this program; if not, write to the Free Software
38#    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
39#
40
41use warnings;
42use strict;
43use IO::Handle;
44use Getopt::Std;
45use POSIX;
46
47$ENV{PATH} = "/bin:/usr/bin";
48
49##### Configuration #####
50use constant DEBUG => 0;
51use constant MAX   => 255;
52
53use constant PIDFILE  => '/var/run/fancontrol.pid';
54use constant CONFFILE => '/etc/fancontrol';
55use constant LOGFILE  => '/var/log/fancontrol/fancontrol.log';
56use constant ERRFILE  => '/var/log/fancontrol/fancontrol.err';
57### End Configuration ###
58
59our $interval;
60our $pwmo;
61our @afcpwm;
62our @afctemp;
63our @afcfan;
64our @afcmaxtemp;
65our @afcmintemp;
66our @afcminstart;
67our @afcminstop;
68
69sub loadconfig($);
70sub pwmdisable($);
71sub pwmenable($);
72sub restorefans();
73sub calc(@);
74sub UpdateFanSpeeds();
75END { restorefans(); }
76
77our $opt_d;
78getopts('d');
79
80my $config = shift;
81if (defined($config))
82   { loadconfig($config); }
83else
84   { loadconfig(CONFFILE); }
85
86### Daemonize
87if ( defined($opt_d) && ($opt_d == 1) )
88   {
89     my $pid = fork;
90     POSIX::_exit(0) if $pid;
91
92     unless (defined($pid))
93       { die("Couldn't fork: $!"); }
94
95     open(*STDERR, '>', ERRFILE);
96     IO::Handle::autoflush(*STDERR);
97     open(*STDOUT, '>>', LOGFILE);
98     IO::Handle::autoflush(*STDOUT);
99
100     unless (POSIX::setsid())
101       { die("Couldn't open new session: $!"); }
102
103   }
104
105### Pidfile
106if (open(FILE, ">" . PIDFILE))
107   {
108     print(FILE "$$\n");
109     close(FILE);
110   }
111else
112   { print(PIDFILE . ": $!\n"); }
113
114
115### What kind of interface?
116our $sysfs = 0;
117our $dir = '/proc/sys/dev/sensors';
118if (!(-d $dir))
119   {
120     if ($afcpwm[0] =~ m/^hwmon\d/)
121       {
122         $dir = '/sys/class/hwmon';
123       }
124     else
125       {
126         $dir = '/sys/bus/i2c/devices';
127       }
128
129     if (!(-d $dir))
130       { die("No sensors found! (are the necessary modules loaded?) :
131$!\n"); }
132     else
133       {
134         $sysfs = 1;
135       }
136   }
137
138### Trap signals
139$SIG{TERM} = \&restorefans;
140$SIG{HUP}  = \&restorefans;
141$SIG{INT}  = \&restorefans;
142$SIG{QUIT} = \&restorefans;
143
144### Enable PWM
145print("Enabling PWM on fans...\n");
146my $fcvcount = 0;
147while ($fcvcount < $#afcpwm+1)
148   {
149     $pwmo = $afcpwm[$fcvcount];
150     unless (pwmenable($pwmo))
151       { die("Error enabling PWM on $dir/$pwmo : $!\n"); }
152     $fcvcount++;
153   }
154
155print("Starting automatic fan control...\n");
156
157while(1)
158   {
159     UpdateFanSpeeds();
160     sleep($interval);
161   }
162
1631;
164
165################################################################
166sub loadconfig($)
167{
168   my $file = shift;
169
170   print("Loading configuration from $file ...\n");
171
172   unless ( (-e $file) && (-r $file) )
173     { die("Unable to read config file $file: $!"); }
174
175   open(F, $file);
176
177   our ($interval, $fctemps, $fcfans, $mintemp, $maxtemp, $minstart, 
178$minstop);
179   while($_ = <F>)
180     {
181       if ($_ =~ /^\s+$/)                { next; }
182       elsif ($_ =~ /^INTERVAL=\s*(.*)$/) { $interval = $1; next; }
183       elsif ($_ =~ /^FCTEMPS=\s*(.*)$/)  { $fctemps = $1;  next; }
184       elsif ($_ =~ /^FCFANS=\s*(.*)$/)   { $fcfans = $1;   next; }
185       elsif ($_ =~ /^MINTEMP=\s*(.*)$/)  { $mintemp = $1;  next; }
186       elsif ($_ =~ /^MAXTEMP=\s*(.*)$/)  { $maxtemp = $1;  next; }
187       elsif ($_ =~ /^MINSTART=\s*(.*)$/) { $minstart = $1; next; }
188       elsif ($_ =~ /^MINSTOP=\s*(.*)$/)  { $minstop = $1;  next; }
189     }
190   close(F);
191
192   unless (defined($interval))
193     { die("Some settings missing ..."); }
194
195   print("\nCommon settings:\n");
196   print("  INTERVAL=$interval\n");
197
198   my $fcvcount = 0;
199   foreach my $fcv (split(/\s+/, $fctemps))
200     {
201       ($afcpwm[$fcvcount], $afctemp[$fcvcount]) = split(/=/, $fcv);
202
203       $fcfans   =~ s/^\S*=(\S+)\s*//;  $afcfan[$fcvcount]      = $1;
204       $mintemp  =~ s/^\S*=(\S+)\s*//;  $afcmintemp[$fcvcount]  = $1;
205       $maxtemp  =~ s/^\S*=(\S+)\s*//;  $afcmaxtemp[$fcvcount]  = $1;
206       $minstart =~ s/^\S*=(\S+)\s*//;  $afcminstart[$fcvcount] = $1;
207       $minstop  =~ s/^\S*=(\S+)\s*//;  $afcminstop[$fcvcount]  = $1;
208
209       print("\nSettings for $afcpwm[$fcvcount]:\n");
210       print("  Depends on $afctemp[$fcvcount]\n");
211       print("  Controls $afcfan[$fcvcount]\n");
212       print("  MINTEMP  = $afcmintemp[$fcvcount]\n");
213       print("  MAXTEMP  = $afcmaxtemp[$fcvcount]\n");
214       print("  MINSTART = $afcminstart[$fcvcount]\n");
215       print("  MINSTOP  = $afcminstop[$fcvcount]\n");
216
217       $fcvcount++;
218     }
219}
220
221
222################################################################
223sub pwmdisable($)
224{
225   my $p = shift;
226
227   if ($sysfs == 1)
228     {
229       if (open(F, ">$dir/$p"))
230         {
231           print(F MAX . '\n');
232           close(F);
233         }
234       else
235         { die("$dir/$p : $!"); }
236
237       my $enable = "$dir/$p/pwm/pwm_enable";
238       if (-f $enable)
239         {
240           if (open(F, ">$enable"))
241             {
242               print(F '0');
243               close(F);
244             }
245           else
246             { die("$dir/$p/pwm/pwm_enable : $!"); }
247         }
248     }
249   else
250     {
251       if (open(F, ">$dir/$p"))
252         {
253           print(F MAX . ' 0');
254           close(F);
255         }
256       else
257         { die("$dir/$p : $!"); }
258     }
259   return(1);
260}
261
262
263#################################################################
264sub pwmenable($)
265{
266   my $p = shift;
267
268   if ($sysfs == 1)
269     {
270       my $enable = "$dir/$p/pwm/pwm_enable";
271       if (-f $enable)
272         {
273           if (open(F, ">$enable"))
274             {
275               print(F "1\n");
276               close(F);
277             }
278           else
279             { die("$dir/$p : $!\n"); }
280         }
281     }
282   else
283     {
284       if (open(F, ">$dir/$p"))
285         {
286           print(F MAX . " 1\n");
287           close(F);
288         }
289       else
290         { die("$dir/$p : $!\n"); }
291     }
292   return(1);
293}
294
295
296################################################################
297sub restorefans()
298{
299   $SIG{TERM} = 'IGNORE';
300   $SIG{HUP}  = 'IGNORE';
301   $SIG{INT}  = 'IGNORE';
302   $SIG{QUIT} = 'IGNORE';
303
304   print("Aborting, restoring fans...\n");
305   my $fcvcount = 0;
306
307   while ( $fcvcount < $#afcpwm+1)
308     {
309       my $pwmo = $afcpwm[$fcvcount];
310       &pwmdisable($afcpwm[$fcvcount]);
311       $fcvcount++;
312     }
313   print("Verify fans have returned to full speed\n");
314   POSIX:_exit(-1);
315}
316
317
318############################################################
319sub UpdateFanSpeeds()
320{
321   my $fcvcount = 0;
322
323   while ($fcvcount < $#afcpwm+1)
324     {
325       my $pwmo  = $afcpwm[$fcvcount];
326       my $tsens = $afctemp[$fcvcount];
327       my $fan   = $afcfan[$fcvcount];
328       my $mint  = $afcmintemp[$fcvcount];
329       my $maxt  = $afcmaxtemp[$fcvcount];
330       my $minsa = $afcminstart[$fcvcount];
331       my $minso = $afcminstop[$fcvcount];
332
333       ### tval
334       my $tval = 0;
335       if (open(F, "$dir/$tsens"))
336         {
337           $tval = <F>;
338           close(F);
339         }
340       else
341         { die("Error reading temperature from $dir/$tsens"); }
342       $tval =~ /([.\d]+)\s*$/;
343       $tval = int($1);
344       if ($sysfs == 1)
345         { $tval /= 1000; }
346
347       ### pwmpval
348       my $pwmpval = 0;
349       if (open(F, "$dir/$pwmo"))
350         {
351           $pwmpval = <F>;
352           close(F);
353         }
354       else
355         { die("Error reading PWM value from $dir/$pwmo"); }
356       ($pwmpval) = split(/\s/, $pwmpval);
357
358       ### fanval
359       my $fanval = 0;
360       if (open(F, "$dir/$fan"))
361         {
362           $fanval = <F>;
363           close(F);
364         }
365       else
366         { die("Error reading Fan value from $dir/$fan"); }
367       $fanval =~ /(\d+)\s$/;
368       $fanval = $1;
369
370       ### DEBUG
371       if (DEBUG == 1)
372         {
373           print("pwmo=$pwmo\n");
374           print("tsens=$tsens\n");
375           print("fan=$fan\n");
376           print("mint=$mint\n");
377           print("maxt=$maxt\n");
378           print("minsa=$minsa\n");
379           print("minso=$minso\n");
380           print("tval=$tval\n");
381           print("pwmpval=$pwmpval\n");
382           print("fanval=$fanval\n");
383           print("\n");
384         }
385
386       my $pwmval;
387       if ($tval <= $mint)
388         { $pwmval = 0; }
389       elsif ($tval >= $maxt)
390         { $pwmval = MAX; }
391       else
392         {
393           $pwmval = eval ( ($tval - $mint) / ($maxt - $mint) )**2 ;
394           $pwmval *= (255 - $minso);
395           $pwmval += $minso;
396           $pwmval = int($pwmval);
397           if ( ($pwmval == 0) || ($fanval == 0) )
398             {
399               if (open(F, ">$dir/$pwmo"))
400                 {
401                   print(F "$minsa\n");
402                   close(F);
403                 }
404               else
405                 { die("Error writing PWM value to $dir/$pwmo : $!\n"); }
406               sleep 1;
407             }
408         }
409
410       if (open(F, ">$dir/$pwmo"))
411         {
412           print(F "$pwmval\n");
413           close(F);
414         }
415       else
416         { die("Error writing PWM value to $dir/$pwmo : $!\n"); }
417
418       if (DEBUG == 1)
419         { print("new pwmval = $pwmval\n"); }
420
421       $fcvcount++;
422     }
423
424}
425
4261;
427
428
Note: See TracBrowser for help on using the browser.