Еще один способ управления вентилятором в 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 ?= \' \t\tQuery register's value\n"; print "\'fan_control := \' \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
#!/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"); >
#!/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
Если регистры, и их значения подобраны верно просто выполняем в консоли:
вентилятор должен изменить обороты.
Profit!
Спасибо за внимание, надеюсь материал будет кому-нибудь полезен.
Критика, дополнения и улучшения приветствуются.
Управление кулером в системе Linux
Оборудование
Эта функциональность зависит как от вашего оборудования, так и от программного обеспечения. Если ваше оборудование не поддерживает управление скоростью вращения вентилятора или не отображает их в ОС, очень вероятно, что вы не сможете использовать это решение. Если это так, но программное обеспечение (как ядро) не знает, как его контролировать, вам не повезло.
Но, если все в порядке то мы может приступить к тому, чтобы рассказать вам как управлять кулером в Linux.
Установка LM-Sensors
Или с помощью команды для установки LM-Sensors в системе Ubuntu или Debian:
sudo apt-get install lm-sensors
sudo yim installlm-sensors
Для Gentoo нужно использовать эту команду:
sudo emerge -av lm-sensors
Настройка lm-sensors
Управление кулером в Linux является очень простым. Но, перед эти нужно правильно установить и настроить программное обеспечение.
- В терминальных типах датчики sudo — обнаруживают и отвечают ДА всем YES / no questions.
- В конце обнаружения датчиков отобразится список модулей, которые необходимо загрузить.
Введите «Yes», чтобы обнаружить датчики, вставить эти модули в / etc / modules или отредактировать / etc / modules самостоятельно.
Запустите sudo service module-init-tools restart. Это действие прочитает изменения, внесенные вами в / etc / modules на шаге 3, и вставьте новые модули в ядро.
Примечание. Если вы используете Ubuntu 13.04 или выше, эта команда 3-го шага должна быть заменена запуском sudo service kmod start.
Установка Fancontrol
Управление оборотами вентилятора вашего кулера происходит с помощью специальной программы. Эта программа очень просто устанавливается. Делается это также через командную строку как и в способе выше. Чтобы установить программу для операционной системы Ubuntu или Debian, нужно использовать эту команду:
sudo apt-get install fancontrol
Настройка Fancontrol
В терминале типа sudo pwmconfig. Этот скрипт остановит каждый вентилятор в течение 5 секунд, чтобы узнать, какие вентиляторы можно контролировать, с помощью ручного управления PWM. После того, как скрипт будет проходить через все вентиляторы, вы можете настроить, какой вентилятор соответствует температуре.
В моем случае я установил интервал до 2 секунд. Если говорить про управление кулером в Linux то можно отметить автозапуск настроек. Об этом мы поговорим в следующем пункте нашего руководства.
Настройка службы fancontrol
Запустите sudo service fancontrol start. Это также приведет к автоматическому запуску службы fancontrol при запуске системы. Это сделает управление кулером в Linux полностью автоматизированным после указанных настроек.
Ручное управление оборотами кулера
Если вы хотите организовать управление оборотами кулера через терминал вручную, каждый раз вводя команду — то, мы расскажем как это сделать. Для начала введите команду для отключения Fancontrol:
sudo systemctl stop fancontrol
Далее просмартиваем список доступных устройств и выбираем нужное:
$ ls /sys/class/hwmon/hwmon1/device/ | grep pwm
Здесь будет значение pwm1 или pwm2 — это файлы кулеров. Далее, нужно разрешить ручное управление вентилятором Linux, например для pwm2:
root $ echo 1 >>/sys/class/hwmon/hwmon1/device/pwm2_enable
Включаем кулер на всю мощь с помощью этой команды:
root $ echo 255 >> /sys/class/hwmon/hwmon1/device/pwm2
а теперь давайте сведем обороты в стандартный режим:
root $ echo 2 >> /sys/class/hwmon/hwmon1/device/pwm1
Управление кулером в Linux является очень простым. Мы рассказали как это можно сделать с помощью программ самостоятельно через терминал. Но, если у вас есть вопросы — задавайте их в комментарии. Перед написание обязательно укажите каким дистрибутивом вы пользуетесь.