Еще один способ управления вентилятором в Linux (на примере Acer S3-391)

от автора

Понадобился мне однажды для работы ноутбук. Уж не помню почему, но выбор пал на Acer S3-391, тонкий, легкий, быстрый, но не лишенный недостатков. Кроме плохого экрана (который кстати не так просто заменить — у него особый коннектор, и возможно он приклеен к рамке), особенно меня раздражал шум вентилятора.
Пути решения этой проблемы я и постараюсь осветить в этой статье.

Прочитав статью Управляем вентилятором ноутбука через DSDT в Linux и не только, как и автор, я начал усердно гуглить в сторону ACPI и DSDT, даже перекомпилировал и подключил свою таблицу, но найти «ту самую» строчку кода отвечающую за работу вентилятора так и не удалось.

Тем временем шум вентилятора, меня все больше деморализировал. При чем, если на работе шум системников и кондиционера еще как-то перебивал, то дома, наедине со своими тараканами, вентилятор методично разрушал мою психику.
Решено было на время вернуться в Win7.

Как обстоят дела в Win

Для ОС от Майкрософт написано очень много программ для управлени вентилятором, все он по большей части заточены в лучшем случае под одного производителя. Что наводило на неприятные мысли.
Но тем не менее была найдена относительно универсальная программа NBFC, которая сразу заработала, требовалось лишь выставить тригеры переключения оборотов.

Какое-то время решение меня устраивало, но на душе все равно было как-то неспокойно.

Возвращение домой

После пары недель использования вынды понял что неудобно. Нужно было решение для непокоренного пингвина.
Тогда я решил все таки разобраться как же работает вышеупомянутая программа.

Решение было не то что бы совсем на поверхноости, но точно не глубоко. Точнее в мануале приложенном к софтине.

Было найдено «правильное слово» по которому нужно гуглить: Embedded Controller (EC).

как написано на rom.by
Embedded Contoller — это встроенный контроллер типа Hitachi H8 (он же — Renesas), Winbond W83L950D, предназначенный для управления платформой (как правило — мобильной) как на уровне включения и выключения, так и для обработки ACPI-событий. В задачи EC-контроллера входит обслуживание аккумулятора мобильной платформы: выбор режима его заряда, контроль разрядки. Как правило, на мобильных платформах с помощью EC-контроллера реализуется и контроллер клавиатуры.

Оказалось что состояние вентилятора так же записывается в регистры этого контроллера.
Отавалось решить 2 задачи:
1) Какие регистры отвечают за состояние вентилятора
2) Как изменять их значение

Решение

С первой задачей помогла справится все также программка NBFC. Всего-то и нужно было посмотреть значения в конфиге для своего ноутбука (ультрабука?)
А с задачей «Как?» помог справится скрипт на перле шестилетней давности, который заработал сразу и без правок.

В общем-то все можно было бы и успокоиться, но хотелось немного увтоматизировать процесс, в результате чего появилось целых 3 скрипта, возможно и можно было все решить одним, но мои познания в программировании крайне ограничены, а на перле я вообще не писал никогда, если кто подскажет как это все упростить и сделать так что бы управляющий скрипт автоматически перезапускался после сна/пробуждения устройства — буду благодарен.

Собственно сами скрипты:

Переработаный под мои нужды скрипт управления

!/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"; }	  

Логика работы програмы

#!/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 /home/user/fan_control.pl 20"); } elsif ($temp < $half) { 	system("/usr/bin/perl -w /home/user/fan_control.pl 40"); } elsif ($temp < $full) { 	system("/usr/bin/perl -w /home/user/fan_control.pl 80"); } else {        	system("/usr/bin/perl -w /home/user/fan_control.pl auto"); }  

Ляунчер

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

Просто скопируйте в /usr/local/bin/ и дайте права на выполнение

Значения оборотов и пороговых тепмератур описаны такие как удобно мне, вы можете с ними поиграться, подобрать более подходящие для вас.

Если у вас другой ноутбук, с той же проблемой вам скорее всего потребуется изменить значения записываемого регистра
В этом нам помогут конфиги написанные для уже неоднократно упоминавшейся NBFC
Если ничего найти не удалось то можно попробовать узнать значения запустив:

watch -n 1 sudo fan_control.pl regs 

Если регистры, и их значения подобраны верно просто выполняем в консоли:

sudo fan_control 

вентилятор должен изменить обороты.
Profit!

Спасибо за внимание, надеюсь материал будет кому-нибудь полезен.
Критика, дополнения и улучшения приветствуются.

ссылка на оригинал статьи http://habrahabr.ru/post/189616/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *