...

пятница, 9 августа 2013 г.

[recovery mode] Еще один способ управления вентилятором в 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!

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

Критика, дополнения и улучшения приветствуются.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends: 'You Say What You Like, Because They Like What You Say' - http://www.medialens.org/index.php/alerts/alert-archive/alerts-2013/731-you-say-what-you-like-because-they-like-what-you-say.html


Комментариев нет:

Отправить комментарий