Out of the box, the fan is either full speed or off. This is very annoying, but there exists several practical solutions. I here report my experience and how you can safely and easily fix the issue.
My configuration is an ASUS ux32vd with first ubuntu 13.10 and now 14.04.
In a nutshell
the problem disapear after running the provided program for a few days.
1) Introduction
The
most comprehensive thread on the net about the issue is a very interesting read for technical details, but that you may want to skip in order to just get a working solution. In a nutshell it is possible to control the fan speed by writing in the memory of the embedded controller, however not doing it properly through the ACPI interface is hazardous.
Some integrated work around have been developed: among the most complete I found on the internet are
one solution based on a kernel module, and here is
another solution based on a shell script and acpi_call. They unfortunately lack documentation which can be an obstacle for the layman. I tried, used and modified the last one by Emil Lind, which is based on a shell script and the acpi_call module. I here report more information about how to properly use it, as well as my own modification.
2) What you need to know
First it has to be noted that contrary to what has been written elsewhere, what is controlled by writing to the embedded controller memory through ACPI calls is not the actual speed of the fan, but the maximal speed of the fan.
Secondly, the danger of running these programs is of not exiting them in a safe state, i.e. not to restore the automatic control of the fan and a maximal fan speed when they exit, which could theoretically bring the laptop to overheat.
If you have this in mind an ascertain that each time you use it it leaves properly you can safely use this program.
Thirdly, it has to be noted that after experimenting with this program, the problem disappeared: the fan where able to react proportionally to heat without any ad-hoc fan control loop in the background. So you can expect experimenting the same after a while.
3) How you can fix the fan
I here provide a heavily modified version of the shell program asusfanctrld of Emil Lind: it has a control loop which automatically switches between a set of presets RPM temperature thresholds. The thresholds have been determined empirically in order to minimise the switching between states and provide and overall unnoticeable and cool experience ;) The real fix is the following: for an unknown reason after a few days of use of the following program, the problem disapear by itself. It may reappear if you boot windows. In which case, running again the provided program will fix the issue.
3.1) In more details:
there are four predefined temperature thresholds and four predefined fan RPM values. When a threshold is hit from the upper of lower values, the fan speed is set to the corresponding RPM until another threshold, lower or higher, is reached. ACPI commands are written through the special device /proc/acpi/call provided by the
acpi_call module kernel module that must be installed.
3.2) Install:
- step 1 : the acpi_call module has to be downloaded, compiled and loaded (from a root shell:
insmod acpi_call.ko) prior to running the fan control loop, which is the most complicated task to be done to get a silent zenbook.
- step 2 : the following shell script as to be copied/pasted to a file, let us call it
asusfanctrlnico.sh
3.3) Usage:
The script has to be run as root, leave it running in a windows to check everything runs smoothly until you either get confident enough in it or the fan problems goes away. In order to allow the hard drive to sleep you need to comment out the file logging commands in the
main function.
It is not meant to be used by startup scripts. It however can with the appropriate kill commands added to the init.d or start-stop-daemon services (which I didn't as the problem went away after using the program for some days).
3.4) The code of asusfanctrlnico.sh :
ACPI_CALL=/proc/acpi/call
# average temperature without control:
# sleep ~ 27 - 30
# on, idle ~ 45 - 50
# internet browsing, text editing ~ 50 - 58
# temperature threshold (Celsius)
HIGH=70
MID=60
LOW=55
LOWEST=50
MARGIN=1
# corresponding fan RPM
LOWESTRPM=0x35
LOWRPM=0x50
MIDRPM=0x70
HIGHRPM=0xFF
DEBUGACPI=0
DEBUGBEHV=1
ME="$(basename $0)"
LOGFILE=asusfanlog
LOGFILETEMP=fantemplog
fatal() {
logger -id -t $ME -s "FATAL: $@"
}
info() {
logger -id -t $ME "INFO: $@ `date +"%H %M %S %s"`"
echo "INFO $@ `date +"%H %M %S %s"`" >> $LOGFILE
if [ "$DEBUGBEHV" -gt 0 ]; then
echo ""
fi
echo -en "INFO $@ `date +"%H %M %S %s"`\n"
}
debug_acpi() {
if [ $DEBUGACPI -gt 0 ]; then
echo "DEBUG: $@" >> $LOGFILE
fi
}
debug_behaviour() {
if [ $DEBUGBEHV -gt 0 ]; then
echo "DEBUG: $@" >> $LOGFILE
fi
}
if ! [ -e $ACPI_CALL ]; then
modprobe acpi_call
fi
if ! [ -e $ACPI_CALL ]; then
fatal "You must have acpi_call kernel module loaded."
fatal modprobe acpi_call
exit 1
fi
if [ "$(id -u)" != "0" ]; then
echo "Must be run as root."
exit 1
fi
set_fanspeed() {
RPM=$1
command='\_SB.PCI0.LPCB.EC0.ST98 '$1
echo "$command" > "${ACPI_CALL}"
debug_acpi $(cat "${ACPI_CALL}")
}
set_fanstate() {
current=$STATE
asked=$1
if [ "$current" != "$asked" ]; then
info "reset fan state: $current => $asked"
if [ "$asked" = "high" ]; then
set_fanspeed $HIGHRPM
elif [ "$asked" = "mid" ]; then
set_fanspeed $MIDRPM
elif [ "$asked" = "low" ]; then
set_fanspeed $LOWRPM
elif [ "$asked" = "lowest" ]; then
set_fanspeed $LOWESTRPM
else
fail "error unknown state: $asked"
fi
STATE=$asked
fi
}
set_auto() {
command='\_SB.ATKD.QMOD 0x02'
echo "$command" > "${ACPI_CALL}"
debug_acpi $(cat "${ACPI_CALL}")
command='\_SB.PCI0.LPCB.EC0.SFNV 0 0'
echo "$command" > "${ACPI_CALL}"
debug_acpi $(cat "${ACPI_CALL}")
CONFIG="auto"
STATE="auto"
}
set_manual() {
TEMP=$1
if [ $TEMP -ge $HIGH -o \( "$STATE" = "high" -a $TEMP -ge $[$MID+$MARGIN] \) ]; then
set_fanstate high
elif [ $TEMP -ge $MID -o \( "$STATE" = "mid" -a $TEMP -ge $[$LOW+$MARGIN] \) ]; then
set_fanstate mid
elif [ $TEMP -ge $LOW -o \( "$STATE" = "low" -a $TEMP -ge $[$LOWEST+$MARGIN] \) ]; then
set_fanstate low
else
set_fanstate lowest
fi
}
set_fanmode() {
asked=$1
if [ "$asked" = "auto" ]; then
set_auto
CONFIG="auto"
STATE="auto"
info "set fan mode: auto"
elif [ "$asked" = "manual" ]; then
CONFIG="manual"
STATE="auto"
info "set fan mode: manual"
else
fail "error unknown mode: $asked"
fi
}
fail(){
info "$@"
set_fanmode auto
exit 1
}
fatal_callback() {
fail "stop, fatal signal"
}
sigusr1_callback() {
set_fanmode manual
logger -id -s -t $ME "got SIGUSR1, invoking manual control of fans"
info "siguser1 mode : auto => manual"
}
sigusr2_callback() {
set_fanmode auto
logger -id -s -t $ME "got SIGUSR2, setting mode to auto"
info "siguser1 mode : manual => auto"
}
main() {
trap "fatal_callback" INT TERM EXIT KILL
trap "sigusr1_callback" SIGUSR1
trap "sigusr2_callback" SIGUSR2
info start
set_fanmode manual
if [ $DEBUGBEHV -gt 0 ]; then
echo ""
fi
echo "`date +"%s"` CONFIG $LOWEST $LOW $MID $HIGH $LOWESTRPM $LOWRPM $MIDRPM $HIGHRPM" >> $LOGFILETEMP
LASTTEMPSTATE=""
while :; do
TEMP0=$[$(</sys/devices/virtual/thermal/thermal_zone0/temp)/1000]
TEMP1=$[$(</sys/devices/virtual/thermal/thermal_zone1/temp)/1000]
TEMP=$TEMP1
if [ $DEBUGBEHV -gt 0 ]; then
echo -en "\r${TEMP0} ${TEMP1} mode=$CONFIG state=$STATE"
fi
if [ "$CONFIG" = "manual" ]; then
set_manual $TEMP
fi
TEMPSTATE="$TEMP0 $TEMP1 $CONFIG $STATE $(cat /proc/loadavg | cut -d\ -f 1,2,3)"
if [ "$TEMPSTATE" != "$LASTTEMPSTATE" ]; then
echo "`date +"%s"` $TEMPSTATE" >> $LOGFILETEMP
LASTTEMPSTATE=$TEMPSTATE
fi
sleep 1
done
}
set -e
main