Difference between revisions of "Talk:Patch for controlling fan speed"
(Windows XP port...) |
(added script that's able to daemonize via command line option) |
||
| Line 38: | Line 38: | ||
--[[User:Spiney|Spiney]] | --[[User:Spiney|Spiney]] | ||
| + | ---- | ||
| + | |||
| + | == Updated script for unpatched kernels == | ||
| + | |||
| + | A couple of command line options added, plus this version is able to daemonize and writes a pid file (to a custom location if needed). It's for an unpatched version of the kernel, since I think that's the way to go, but it should be easy to merge into the other version as well since it doesn't change any internals. | ||
| + | |||
| + | Any feedback appreciated. | ||
| + | |||
| + | Here we go: | ||
| + | |||
| + | <pre> | ||
| + | #!/bin/bash | ||
| + | |||
| + | # This script dynamically controls fan speed on some ThinkPad models | ||
| + | # according to user-defined temperature thresholds. It implements its | ||
| + | # own decision algorithm, overriding the ThinkPad embedded | ||
| + | # controller. It also implements a workaround for the fan noise pulse | ||
| + | # experienced every few seconds on some ThinkPads. | ||
| + | # | ||
| + | # WARNING: This script relies on undocumented hardware features and | ||
| + | # overrides nominal hardware behavior. It may thus cause arbitrary | ||
| + | # damage to your laptop or data. Watch your temperatures! | ||
| + | # | ||
| + | # This file is placed in the public domain and may be freely distributed. | ||
| + | |||
| + | LEVELS=( 0 2 4 7) # Fan speed levels | ||
| + | UP_TEMPS=( 52 60 68 ) # Speed increase trip points | ||
| + | DOWN_TEMPS=( 48 56 64 ) # Speed decrease trip points | ||
| + | |||
| + | ANTIPULSE=( 0 1 0 0) # Prevent fan pulsing noise at this level | ||
| + | # (this also prevents fan speed updates) | ||
| + | |||
| + | IBM_ACPI=/proc/acpi/ibm | ||
| + | PID_FILE=/var/run/tp-fancontrol.pid | ||
| + | INTERVAL=3 | ||
| + | VERBOSE=true | ||
| + | DRY_RUN=false | ||
| + | DAEMONIZE=false | ||
| + | |||
| + | usage() { | ||
| + | echo "Usage: $0 [OPTION]..." | ||
| + | echo | ||
| + | echo "Available options:" | ||
| + | echo " -t test mode" | ||
| + | echo " -q quiet mode" | ||
| + | echo " -d daemon mode, go into background, implies -q" | ||
| + | echo " -p pid file location for daemon mode, default: $PID_FILE" | ||
| + | } | ||
| + | |||
| + | set -- `getopt -u -n $0 qtdp:h "$@"` | ||
| + | |||
| + | while true; do | ||
| + | case "$1" in | ||
| + | -t) | ||
| + | DRY_RUN=true | ||
| + | echo "$0: Dry run, will not change fan state." | ||
| + | shift | ||
| + | ;; | ||
| + | -q) # quiet mode | ||
| + | VERBOSE=false | ||
| + | shift | ||
| + | ;; | ||
| + | -d) # go into background and daemonize | ||
| + | DAEMONIZE=true | ||
| + | shift | ||
| + | ;; | ||
| + | -p) # different pidfile | ||
| + | PID_FILE="$2" | ||
| + | shift 2 | ||
| + | ;; | ||
| + | -h) # short help | ||
| + | usage | ||
| + | exit 1 | ||
| + | ;; | ||
| + | --) # no more options | ||
| + | shift | ||
| + | break | ||
| + | ;; | ||
| + | esac | ||
| + | done | ||
| + | |||
| + | if $DRY_RUN; then | ||
| + | VERBOSE=true | ||
| + | DAEMONIZE=false | ||
| + | fi | ||
| + | |||
| + | if $DAEMONIZE ; then | ||
| + | ( | ||
| + | exec $0 -q -p $PID_FILE 0>&- 1>&- 2>&- | ||
| + | ) & | ||
| + | echo $! > $PID_FILE | ||
| + | exit 0 | ||
| + | fi | ||
| + | |||
| + | # Enable the fan in default mode if anything goes wrong: | ||
| + | set -e -E -u | ||
| + | $DRY_RUN || trap "rm -f $PID_FILE 2> /dev/null; echo enable > $IBM_ACPI/fan; exit 0" EXIT HUP INT ABRT QUIT SEGV TERM | ||
| + | |||
| + | thermometer() { # output list of temperatures | ||
| + | read X Y < $IBM_ACPI/thermal | ||
| + | if ! [[ "$X" == "temperatures:" ]]; then | ||
| + | echo "$0: Bad temperatures: $X $Y" >&2 | ||
| + | exit 1 | ||
| + | fi | ||
| + | echo "$Y"; | ||
| + | } | ||
| + | |||
| + | speedometer() { # output fan speed RPM | ||
| + | cat $IBM_ACPI/fan | sed '/^speed/!d; s/speed:[ \t]*//' | ||
| + | } | ||
| + | |||
| + | setlevel() { # set fan speed level | ||
| + | $DRY_RUN || echo 0x2F $1 > $IBM_ACPI/ecdump | ||
| + | } | ||
| + | |||
| + | IDX=0 | ||
| + | MAX_IDX=$(( ${#LEVELS[@]} - 1 )) | ||
| + | SETTLE=0 | ||
| + | |||
| + | while true; do | ||
| + | TEMPS=`thermometer` | ||
| + | $VERBOSE && SPEED=`speedometer` | ||
| + | |||
| + | # Calculate new level | ||
| + | NEWIDX=$IDX | ||
| + | DOWN=$(( IDX > 0 )) | ||
| + | for TEMP in $TEMPS; do | ||
| + | # Increase speed as much as needed | ||
| + | while [[ $NEWIDX -lt $MAX_IDX ]] && | ||
| + | [[ $TEMP -ge ${UP_TEMPS[$NEWIDX]} ]]; do | ||
| + | (( NEWIDX ++ )) | ||
| + | DOWN=0 | ||
| + | done | ||
| + | # Allow decrease (by one index)? | ||
| + | if [[ $DOWN == 1 ]] && | ||
| + | [[ $TEMP -gt ${DOWN_TEMPS[$(( IDX - 1 ))]} ]]; then | ||
| + | DOWN=0 | ||
| + | fi | ||
| + | done | ||
| + | if [[ $DOWN == 1 ]]; then | ||
| + | NEWIDX=$(( IDX - 1 )) | ||
| + | fi | ||
| + | |||
| + | # Transition | ||
| + | OLDLEVEL=${LEVELS[$IDX]} | ||
| + | NEWLEVEL=${LEVELS[$NEWIDX]} | ||
| + | $VERBOSE && echo "tpfan: Temps: $TEMPS Fan: $SPEED Level: $OLDLEVEL->$NEWLEVEL" | ||
| + | setlevel $NEWLEVEL | ||
| + | |||
| + | sleep $INTERVAL | ||
| + | |||
| + | # If needed, apply anti-pulsing hack after a settle-down period: | ||
| + | if [[ ${ANTIPULSE[${NEWIDX}]} == 1 ]]; then | ||
| + | if [[ $NEWLEVEL == $OLDLEVEL ]]; then | ||
| + | if [[ $SETTLE -ge 0 ]]; then | ||
| + | (( SETTLE -= INTERVAL )) | ||
| + | else | ||
| + | setlevel 0x40 # disengaged | ||
| + | sleep 0.5 | ||
| + | fi | ||
| + | else | ||
| + | SETTLE=6 | ||
| + | fi | ||
| + | fi | ||
| + | |||
| + | IDX=$NEWIDX | ||
| + | done | ||
| + | </pre> | ||
| + | |||
| + | As usual, I disclaim all warranty for this script, and release it to the public domain (meaning you may use and further distribute it under any terms you wish, including incorporating it into other software). | ||
| + | |||
| + | --[[User:Spiney|Spiney]], Nov 7 2005, 19:46 (CET) | ||
---- | ---- | ||
Revision as of 20:52, 7 November 2005
Contents
Windows XP port
How would I port this patch to Windows XP?
--Jason
You can't. But you can write a Windows device driver based on the specs and ibm-acpi.
--Thinker 18:54, 7 Nov 2005 (CET)
gkrellm support
I can confirm that it works on Thinkpad T43 here. However after applying the patch, the fan speed monitor of gkrellm 2.2.7 cannot read value correctly. Maybe we gkrellm is reading the second line for speed but instead find the line for level, so it got confused? Would it be possible to interchange the lines so that speed still appears in the second line and level appears in the third instead? I'm no coder, just a suggestion to improve the patch.
--Jiang
I'd say it's a bug in gkrellm. It should parse the line header rather than relying on line numbers. But feel free to change (and test) the patch if you wish.
--Thinker 05:14, 26 Oct 2005 (CEST)
patch to keep gkrell working against 2.6.14
As in "works for me on a T43p", use with caution at your own risk. And thanks to thinker for the original patch, very nice work.
(See article for the actual patch)
--Spiney
Looks excellent, why not add it to the article page? Also, care to provide a license (preferably public domain like my patch) so the kernel guys can handle it? Speaking of which, the kernel people seem to like their patches generated via "diff -up vanilla-kernel-2.6.14 patched-kernel-2.6.14".
--Thinker 22:04, 1 Nov 2005 (CET)
Done, using the -p option for diff and "borrowing" your sentence for licensing purposes.
--Spiney
Updated script for unpatched kernels
A couple of command line options added, plus this version is able to daemonize and writes a pid file (to a custom location if needed). It's for an unpatched version of the kernel, since I think that's the way to go, but it should be easy to merge into the other version as well since it doesn't change any internals.
Any feedback appreciated.
Here we go:
#!/bin/bash
# This script dynamically controls fan speed on some ThinkPad models
# according to user-defined temperature thresholds. It implements its
# own decision algorithm, overriding the ThinkPad embedded
# controller. It also implements a workaround for the fan noise pulse
# experienced every few seconds on some ThinkPads.
#
# WARNING: This script relies on undocumented hardware features and
# overrides nominal hardware behavior. It may thus cause arbitrary
# damage to your laptop or data. Watch your temperatures!
#
# This file is placed in the public domain and may be freely distributed.
LEVELS=( 0 2 4 7) # Fan speed levels
UP_TEMPS=( 52 60 68 ) # Speed increase trip points
DOWN_TEMPS=( 48 56 64 ) # Speed decrease trip points
ANTIPULSE=( 0 1 0 0) # Prevent fan pulsing noise at this level
# (this also prevents fan speed updates)
IBM_ACPI=/proc/acpi/ibm
PID_FILE=/var/run/tp-fancontrol.pid
INTERVAL=3
VERBOSE=true
DRY_RUN=false
DAEMONIZE=false
usage() {
echo "Usage: $0 [OPTION]..."
echo
echo "Available options:"
echo " -t test mode"
echo " -q quiet mode"
echo " -d daemon mode, go into background, implies -q"
echo " -p pid file location for daemon mode, default: $PID_FILE"
}
set -- `getopt -u -n $0 qtdp:h "$@"`
while true; do
case "$1" in
-t)
DRY_RUN=true
echo "$0: Dry run, will not change fan state."
shift
;;
-q) # quiet mode
VERBOSE=false
shift
;;
-d) # go into background and daemonize
DAEMONIZE=true
shift
;;
-p) # different pidfile
PID_FILE="$2"
shift 2
;;
-h) # short help
usage
exit 1
;;
--) # no more options
shift
break
;;
esac
done
if $DRY_RUN; then
VERBOSE=true
DAEMONIZE=false
fi
if $DAEMONIZE ; then
(
exec $0 -q -p $PID_FILE 0>&- 1>&- 2>&-
) &
echo $! > $PID_FILE
exit 0
fi
# Enable the fan in default mode if anything goes wrong:
set -e -E -u
$DRY_RUN || trap "rm -f $PID_FILE 2> /dev/null; echo enable > $IBM_ACPI/fan; exit 0" EXIT HUP INT ABRT QUIT SEGV TERM
thermometer() { # output list of temperatures
read X Y < $IBM_ACPI/thermal
if ! [[ "$X" == "temperatures:" ]]; then
echo "$0: Bad temperatures: $X $Y" >&2
exit 1
fi
echo "$Y";
}
speedometer() { # output fan speed RPM
cat $IBM_ACPI/fan | sed '/^speed/!d; s/speed:[ \t]*//'
}
setlevel() { # set fan speed level
$DRY_RUN || echo 0x2F $1 > $IBM_ACPI/ecdump
}
IDX=0
MAX_IDX=$(( ${#LEVELS[@]} - 1 ))
SETTLE=0
while true; do
TEMPS=`thermometer`
$VERBOSE && SPEED=`speedometer`
# Calculate new level
NEWIDX=$IDX
DOWN=$(( IDX > 0 ))
for TEMP in $TEMPS; do
# Increase speed as much as needed
while [[ $NEWIDX -lt $MAX_IDX ]] &&
[[ $TEMP -ge ${UP_TEMPS[$NEWIDX]} ]]; do
(( NEWIDX ++ ))
DOWN=0
done
# Allow decrease (by one index)?
if [[ $DOWN == 1 ]] &&
[[ $TEMP -gt ${DOWN_TEMPS[$(( IDX - 1 ))]} ]]; then
DOWN=0
fi
done
if [[ $DOWN == 1 ]]; then
NEWIDX=$(( IDX - 1 ))
fi
# Transition
OLDLEVEL=${LEVELS[$IDX]}
NEWLEVEL=${LEVELS[$NEWIDX]}
$VERBOSE && echo "tpfan: Temps: $TEMPS Fan: $SPEED Level: $OLDLEVEL->$NEWLEVEL"
setlevel $NEWLEVEL
sleep $INTERVAL
# If needed, apply anti-pulsing hack after a settle-down period:
if [[ ${ANTIPULSE[${NEWIDX}]} == 1 ]]; then
if [[ $NEWLEVEL == $OLDLEVEL ]]; then
if [[ $SETTLE -ge 0 ]]; then
(( SETTLE -= INTERVAL ))
else
setlevel 0x40 # disengaged
sleep 0.5
fi
else
SETTLE=6
fi
fi
IDX=$NEWIDX
done
As usual, I disclaim all warranty for this script, and release it to the public domain (meaning you may use and further distribute it under any terms you wish, including incorporating it into other software).
--Spiney, Nov 7 2005, 19:46 (CET)