📜 ⬆️ ⬇️

Another way to control the fan in Linux (for example, Acer S3-391)

It took me one day to work a laptop. I do not remember why, but the choice fell on the Acer S3-391, thin, light, fast, but not without flaws. In addition to the bad screen (which, by the way, is not so easy to replace - it has a special connector, and perhaps it is glued to the frame), the fan noise irritated me especially.
I will try to highlight the solutions to this problem in this article.

After reading the article Managing a laptop fan through DSDT in Linux and not only , like the author, I began to diligently google in the direction of ACPI and DSDT, even recompiled and connected my table, but I could not find the “same” line of code responsible for the fan’s work .


')
Meanwhile, the fan noise, I was increasingly demoralized. With that, if at work the noise of the system units and the air conditioner was somehow interrupted, then at home, alone with his cockroaches, the fan methodically destroyed my psyche.
It was decided to return to Win7 for a while.

How are things in Win

For the OS from Microsoft, a lot of programs are written to control the fan, everything is mostly sharpened at best from a single vendor. What led to unpleasant thoughts.
But nevertheless, a relatively universal NBFC program was found, which immediately started working, it was only required to set the speed switch triggers.

For a while, the decision suited me, but my heart was still somehow restless.

Homecoming

After a couple of weeks of use, I realized that it was uncomfortable. We needed a solution for an unconquered penguin.
Then I decided to figure out how the aforementioned program works.

The decision was not something that would be entirely on the surface, but certainly not deep. More precisely in the manual attached to the software.

It was found the "correct word" for which you need to google: Embedded Controller (EC) .

as written on rom.by
Embedded Contoller is a built-in Hitachi H8 type controller (also known as Renesas), Winbond W83L950D, designed to control the platform (usually mobile) both at the on and off level and for processing ACPI events. The tasks of the EC-controller include servicing the battery of the mobile platform: choosing the mode of its charge, controlling the discharge. As a rule, on mobile platforms, the keyboard controller is implemented using an EC controller.

It turned out that the state of the fan is also recorded in the registers of this controller.
It was done to solve 2 problems:
1) Which registers are responsible for the state of the fan
2) How to change their meaning

Decision

The NBFC program will also cope with the first task. Just needed to look at the values ​​in the config for your laptop (ultrabook?)
And the “How?” Task was helped by a script on a pearl six years ago , which earned immediately and without edits.

In general, everything could calm down, but I wanted to slightly automate the process, as a result, as many as 3 scripts appeared, it was possible and possible to solve everything by one thing, but my programming knowledge is extremely limited, and I never wrote on Perl at all, if someone tells me how to simplify everything and make the control script restart automatically after a device’s sleep / wake up, I will be grateful.

Actually the scripts themselves:

Redesigned management script for my needs
!/usr/bin/perl -w # Copyright (C) 2013 George Butskivsky butskivsky (at) gmail.com # # Version 0.1 (09-aug-2013) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 3 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. require 5.004; use strict; use Fcntl; use POSIX; use File::Basename; my $fan_control_reg = 0x93; my $fan_manual_mode = 0x14; my $fan_auto_mode = 0x04; my $fan_speed_reg = 0x94; my $fan_speed_val_10 = 0xe6; # 10% of power my $fan_speed_val_20 = 0xc8; my $fan_speed_val_40 = 0x96; my $fan_speed_val_50 = 0x7e; my $fan_speed_val_60 = 0x64; my $fan_speed_val_80 = 0x32; sub initialize_ioports { sysopen (IOPORTS, "/dev/port", O_RDWR) or die "/dev/port: $!\n"; binmode IOPORTS; } sub close_ioports { close (IOPORTS) or print "Warning: $!\n"; } sub inb { my ($res,$nrchars); sysseek IOPORTS, $_[0], 0 or return -1; $nrchars = sysread IOPORTS, $res, 1; return -1 if not defined $nrchars or $nrchars != 1; $res = unpack "C",$res ; return $res; } # $_[0]: value to write # $_[1]: port to write # Returns: -1 on failure, 0 on success. sub outb { if ($_[0] > 0xff) { my ($package, $filename, $line, $sub) = caller(1); print "\n*** Called outb with value=$_[1] from line $line\n", "*** (in $sub). PLEASE REPORT!\n", "*** Terminating.\n"; exit(-1); } my $towrite = pack "C", $_[0]; sysseek IOPORTS, $_[1], 0 or return -1; my $nrchars = syswrite IOPORTS, $towrite, 1; return -1 if not defined $nrchars or $nrchars != 1; return 0; } sub wait_write { my $i = 0; while ((inb($_[0]) & 0x02) && ($i < 10000)) { sleep(0.01); $i++; } return -($i == 10000); } sub wait_read { my $i = 0; while (!(inb($_[0]) & 0x01) && ($i < 10000)) { sleep(0.01); $i++; } return -($i == 10000); } sub wait_write_ec { wait_write(0x66); } sub wait_read_ec { wait_read(0x66); } sub send_ec { if (!wait_write_ec()) { outb($_[0], 0x66); } if (!wait_write_ec()) { outb($_[1], 0x62); } } sub write_ec { if (!wait_write_ec()) { outb(0x81, 0x66 ); } if (!wait_write_ec()) { outb($_[0], 0x62); } if (!wait_write_ec()) { outb($_[1], 0x62); } } sub read_ec { if (!wait_write_ec()) { outb(0x80, 0x66 ); } if (!wait_write_ec()) { outb($_[0], 0x62); } if (!wait_read_ec()) { inb(0x62); } } sub print_regs { initialize_ioports(); my @arr = ("00","10","20","30","40","50","60","70","80","90","A0","B0","C0","D0","E0","F0", ""); my $i = 0; my $t = 0; print "\n \t00\t01\t02\t03\t04\t05\t06\t07\t|\t08\t09\t0A\t0B\t0C\t0D\t0E\t0F\n"; print " \t__\t__\t__\t__\t__\t__\t__\t__\t|\t__\t__\t__\t__\t__\t__\t__\t__\n"; print "00 |\t"; for ($i = 0; $i < 256; $i++) { $t = read_ec($i); print $t; print "\t"; if ((($i + 1) % 8) == 0){ if ((($i + 1) % 16) == 0) { if ($i != 255) { print "\n$arr[(($i-(($i + 1) % 16)) / 16) + 1] |\t"; } } else { print "|\t"; } } } print "\n"; close_ioports(); } if (!$ARGV[0]){ print "wrong arguments!\n"; print "usage:\n"; print "\'fan_control regs\' \t\t\t\tdumps all ec registers\n"; print "\'fan_control ?= <reg>\' \t\tQuery register's value\n"; print "\'fan_control := <reg> <val>\' \tSet register's value\n"; print "\'fan_control 10|20|40|50|60|80\' \tSet fan speed value in percents\n"; print "\'fan_control auto|manual\' \tSet fan policy\n"; print "\'fan_control getspeed\' \tGet current speed fan value in dec format (255-0) lesser is louder\n"; } elsif ($ARGV[0] eq "regs") { print_regs(); } elsif ($ARGV[0] eq "?=") { initialize_ioports(); my $r = hex($ARGV[1]); printf("REG[0x%02x] == 0x%02x\n", $r, read_ec($r)); close_ioports(); } elsif ($ARGV[0] eq ":=") { initialize_ioports(); my $r = hex($ARGV[1]); my $f = hex($ARGV[2]); my $val = read_ec($r); printf("REG[0x%02x] == 0x%02x\n", $r, $val); printf("REG[0x%02x] := 0x%02x\n", $r, $f); write_ec( $r, $f); printf("REG[0x%02x] == 0x%02x\n", $r, read_ec($r)); close_ioports(); } elsif ($ARGV[0] eq "10") { initialize_ioports(); write_ec( $fan_speed_reg, $fan_speed_val_10); close_ioports(); } elsif ($ARGV[0] eq "20") { initialize_ioports(); write_ec( $fan_speed_reg, $fan_speed_val_20); close_ioports(); } elsif ($ARGV[0] eq "40") { initialize_ioports(); write_ec( $fan_speed_reg, $fan_speed_val_40); close_ioports(); } elsif ($ARGV[0] eq "50") { initialize_ioports(); write_ec( $fan_speed_reg, $fan_speed_val_50); close_ioports(); } elsif ($ARGV[0] eq "60") { initialize_ioports(); write_ec( $fan_speed_reg, $fan_speed_val_60); close_ioports(); } elsif ($ARGV[0] eq "80") { initialize_ioports(); write_ec( $fan_speed_reg, $fan_speed_val_80); close_ioports(); } elsif ($ARGV[0] eq "manual") { initialize_ioports(); write_ec( $fan_control_reg, $fan_manual_mode); close_ioports(); } elsif ($ARGV[0] eq "auto") { initialize_ioports(); #write_ec( 0x93, 0x04); write_ec( $fan_control_reg, $fan_auto_mode); close_ioports(); } elsif ($ARGV[0] eq "getspeed") { initialize_ioports(); my $speed = read_ec($fan_speed_reg); my $dec_speed = sprintf("%d", $speed); printf("fan speed == %d\n", $dec_speed); close_ioports(); } else { print "wrong arguments!\n"; } 



The logic of the program
 #!/usr/bin/perl -w $temp = `cat /sys/class/thermal/thermal_zone0/temp`; $silent = int(60000); $half = int(65000); $full = int(75000); if ($temp < $silent) { system("/usr/bin/perl -w /usr/local/bin/fan_control.pl 20"); } elsif ($temp < $half) { system("/usr/bin/perl -w /usr/local/bin/fan_control.pl 40"); } elsif ($temp < $full) { system("/usr/bin/perl -w /usr/local/bin/fan_control.pl 80"); } else { system("/usr/bin/perl -w /usr/local/bin/fan_control.pl auto"); } 



Launcher
 #!/usr/bin/bash /usr/local/bin/fan_control.pl manual while [ true ] do /usr/local/bin/fan_control_logic.pl sleep 5 done 



Just copy to / usr / local / bin / and grant execute permissions.

The values ​​of revolutions and threshold temperatures are described such as it is convenient for me, you can play with them, choose the most suitable for you.

If you have another laptop, with the same problem you will most likely need to change the values ​​of the register being written
This will help us configs written for the already mentioned NBFC
If nothing was found, you can try to find out the values ​​by running:

 watch -n 1 sudo fan_control.pl regs 


If the registers and their values ​​are selected correctly, simply execute in the console:

 sudo fan_control 

fan must change momentum.
Profit!

Thank you for your attention, I hope the material will be useful to someone.
Criticism, additions and improvements are welcome.

Source: https://habr.com/ru/post/189616/


All Articles