Применение embedded controller для управления устройствами

Применение embedded controller для управления устройствами

Решился на написание еще одного опуса. Слышу возмущения — опять написал ерунду, чтобы показать себя..... но я уже не в том возрасте, чтобы писать для этой цели ….. как говорится жизнь на закате и писать только ради этогого …. это несеръезно да и не перед кем мне уже заискывать....
Как то уже упоминал, что программирование не моя стихия, не мое это …. а нравился мне всегда реверсинг, но сейчас уже не до него..... а покапаться в чем то похожем хочется ….. поэтому и родилась эта статья — покапать то покапался, но подумал, что может пригодится и другим...

embedded controller - дословный перевод - встроенный контроллер, предназначен для управления отдельными устройствами компьютера, наверное, точнее будет сказать для обработки отдельных ACPI-событий.
Embedded Controller имеет свой BIOS, который может хранится или в отдельном чипе или входить в состав системного BIOS.
В настоящее время увеличивается как количество разных типов компьютеров, так и количество операционных систем (ОС). Чисто теоретически, да и практически, просто не возможно добиться полной совместимости (в части управления устройствами/ACPI-событиями) всех существующих компьютеров и всех существующих ОС . Вот здесь то и возрастает роль embedded controller при управлении различными проблемными устройствами. И вполне возможно, что количество устройств/ACPI-событий, способных осуществлять свои функции управления посредством embedded controller будет со временем увеличиваться. В настоящее время этот перечень не велик и зависит от разработчиков — в разных типах ноутбуков этот перечень отличается. В основном это вентилятор (имеется практически во всех ноутбуках), в других ноутбуках включена функция управления подсветкой монитора и клавиатуры, включение/выключение некоторых устройств, например, Bluetooth и др.
Подробное описание всех возможных функций управления embedded controller Intel приведено в документе Embedded Controller Usage in Low Power Embedded Designs
Цель данной статьи чисто ознакомительная - ознакомить пользователей Archlinux с возможносью управления определенных устройств компьютера через регистры embedded controller, все будет показно на примере вентилятора (как уже упоминал, возможность управления вентилятором имеется практически во всех типах ноутбуков).
В инете эту тему можно нагуглить, но в основном узконаправлено и все публикации посвящены большей частью скрипту acer_ec.pl , и как я понял идет переписывание одной статьи, но в разных вариациях. Основопологающие принципы практически отсутствуют, имеются отдельные скупые заметки и то на иностранных языках. Одним словом общей картины нет ......... а потому и решил все свести в одно место. Просто в свое время пришлось очень долго все лопатить, да и доходить до некоторых вещей самому.....а пользователю, не имеющему опыта работы с байтами, с разными системами исчисления, слабо знающему другие языки копаться во всем этом будет довольно затруднительно.

Но пора и приступать к экспериментам.....
Условно подход к управлению через embedded controller можно разбить на три способа — названия, конечно, не подходящие, но ничего подходящего не придумал.
1. Ручной (полностью под контролем пользователя - как говорится, все в твоих руках)
2. Полуавтоматический (практически совпадает с 1-ым способом, но для облегчения записи в регистры используется программа).
3. Автоматический (самый простой, даже не нужно загружать модуль, все за тебя продумали)

1. Ручной способ управления.

EDIT - Сейчас модуль ec_sys встроен в ядро (CONFIG_ACPI_EC_DEBUGFS=y) и загружать его не нужно, но значение параметра write_support=N, и значит для записи в регистр нужно загрузиться с параметром write_support=1, точнее, ec_sys.write_support=1
Снова изменили .... стало как и прежде
zgrep CONFIG_ACPI_EC_DEBUGFS /proc/config.gz
CONFIG_ACPI_EC_DEBUGFS=m

Управление осуществляется через модуль ec_sys , встроенный в ядро — этот модуль позволяет осуществлять операции чтения/записи в памяти (в регистры) embedded controller. Модуль в общем то предназначен для отладки - CONFIG_ACPI_EC_DEBUGFS=m , но и для наших целей он тоже хорошо подходит.
- загружаем модуль - # modprobe ec_sys
- проверяем - $ lsmod | grep ec_sys
ec_sys 16384 0
После загрузки модуля должна появиться директория /sys/kernel/debug/ec
# ls /sys/kernel/debug/ec
ec0
# ls /sys/kernel/debug/ec/ec0
gpe  io  use_global_lock
Но если просто загрузить модуль, без параметров, то в дальнейшем не возможно будет провести операции записи значений в регистры. За это отвечает параметр write_support, который по умолчанию равен 0
$ modinfo ec_sys -p
write_support:Dangerous, reboot and removal of battery may be needed. (bool)
Для того чтобы можно было выполнять операции записи необходимо загрузить модуль с данным параметром, равным 1
Для начала, если модуль был загружен, выгружаем его, а потом загружаем по новой, с нужным параметром
# modprobe -r ec_sys
# modprobe ec_sys write_support=1
Но обращаю внимание на запись в строке parm Dangerous.... вот что пишут на этот счет …..
..... ec_sys module that provides a useful debugfs interface to allow one to read + write to the EC memory. Write support is enabled with the ec_sys module parameter 'write_support' but it is generally discouraged as one may be poking data into memory may break things in an unpredictable manner, hence by default write support is disabled.
Но это просто предупреждение ….. просто нужно быть внимательным … но если и ошибетесь, то особо страшного ничего не произойдет.
Если все выполнили правильно, то можно посмотреть таблицу значений данных, записанных в регистрах embedded controller. Эти значения хранятся в бинарном файле /sys/kernel/debug/ec/ec0/io, посмотреть эти значения удобнее следующим способом
# od -Ax -t x1 /sys/kernel/debug/ec/ec0/io
000000 d4 83 59 01 18 08 20 43 00 2c 31 9f 03 ff ff 00
000010 00 3a 00 ff ff ff ff ff ff ff ff ff ff ff ff ff
000020 ff ff 00 00 00 00 14 01 00 00 00 00 00 00 4a 4a
000030 00 00 14 00 00 23 2c 00 00 00 00 00 00 00 00 00
000040 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00
000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
000080 00 00 04 01 01 01 00 14 0f 01 00 ff ff 01 00 00
000090 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 c8 00 00
0000b0 00 96 00 00 00 64 10 00 00 64 41 00 41 00 ff e0
0000c0 2e ff 00 00 ff ff ff aa aa ff ff 3f 28 00 00 00
0000d0 00 00 00 00 00 6b ff ff 02 ff ff ff ff ff ff ff
0000e0 74 0b ff 07 00 b4 00 01 01 00 04 00 ff ff ff 00
0000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000100
Значения данных в регистрах записаны в 16-ричном виде и изменяются в пределах 00 - ff (или 0 - 255 в 10-ричном исчислении). При этом чем меньше значение записанное в регистре, тем больше физическая величина реального значения контролируемого параметра (выразился непонятно, но дальше это будет понятно на примере).
Обозначение регистров — в 16-ричном виде удобно определять прямо по таблице строка - столбец, …. в 10-ричной системе по порядковому номеру элемента в таблице (16 элементов в строке)......но при записи удобнее использовать номера регистров в 10-ричной системе, так как применяется команда dd.
Строки и столбцы нумеруются, согласно таблице, 0, 1, 2, 3 ... d, e, f
Пример. Регистры для моего вентилятора (у Вас будут другие). Как они определяются, опишу позже.
2E (46) — read
2F (47) — write

В них записаны, как видно из таблицы, значения 4a (74 в 10-ричной системе исчисления). Попробуем увеличить мощность вентилятора, а точнее запишем в регистр 2F (47) значение 3a (58).
Запись в регистры осуществляется следующей командой (пояснять не буду, вроде и так все понятно)
# echo -n -e "\x3a" | sudo dd of="/sys/kernel/debug/ec/ec0/io" bs=1 seek=47 count=1 conv=notrunc 2> /dev/null
Ждем 2-3с, слышим, что звук увеличился и проверяем значение
# od -Ax -t x1 -j 47 -N 1 /sys/kernel/debug/ec/ec0/io
00002f 3a
Возвращаем обратно, т.е. устанавливаем значение 4a
# echo -n -e "\x4a" | sudo dd of="/sys/kernel/debug/ec/ec0/io" bs=1 seek=47 count=1 conv=notrunc 2> /dev/null
Слышим, что звук притих и проверяем
# od -Ax -t x1 -j 47 -N 1 /sys/kernel/debug/ec/ec0/io
00002f 4a
Все работает и, как видим, ничего не попортили ….
Можно, в принципе, производить запись и такой командой
# printf '\x3a' | sudo dd of="/sys/kernel/debug/ec/ec0/io" bs=1 seek=47 count=1 conv=notrunc 2> /dev/null

2. Полуавтоматический способ управления.
Основное отличие от 1 способа - использование готовой программы для упрощения операций чтения/записи значений регистров embedded controller . Программа работает только при наличии директории /sys/kernel/debug/ec …... т.е. загрузка модуля ec_sys с параметром write_support=1 обязательна.
Пробуем …....
1. Загружаем модуль ec_sys
# modprobe ec_sys write_support=1
2. Скачиваем исходник программы ec_access.c
wget ftp://ftp.suse.com/pub/people/trenn/sources/ec/ec_access.c
3. Компилируем
$ gcc ~/ec_access.c -o ~/ec_access
4. Читаем таблицу значений регистров
# ~/ec_access -r
     00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F
00:  52  0b  7d  01  18  08  20  43  00  2c  31  9f  03  ff  ff  00
10:  00  3c  00  ff  ff  ff  ff  ff  ff  ff  ff  ff  ff  ff  ff  ff
20:  ff  ff  00  00  00  00  14  01  00  00  00  00  00  00  4a  4a
30:  00  00  14  00  00  23  2c  00  00  00  00  00  00  00  00  00
40:  00  00  00  10  00  00  00  00  00  00  00  00  00  00  00  00
50:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
60:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
70:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
80:  00  00  04  01  01  01  00  14  0f  01  00  ff  ff  01  00  00
90:  00  01  00  00  00  00  00  00  00  00  00  00  00  00  00  00
A0:  00  00  00  00  00  00  00  00  00  00  00  00  00  c8  00  00
B0:  00  96  00  00  00  64  10  00  00  64  41  00  41  00  ff  e0
C0:  2e  ff  00  00  ff  ff  ff  aa  aa  ff  ff  3f  28  00  00  00
D0:  00  00  00  00  00  6b  ff  ff  02  ff  ff  ff  ff  ff  ff  ff
E0:  74  0b  ff  07  00  b4  00  00  01  00  04  00  ff  ff  ff  00
F0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00 
Как видим очень удобная таблица и видим в регистре 2F знакомое значение 4a. Кстати значение регистра по этой таблице вычислять удобнее.
5. Попробуем изменить значение 4a на 3a , как это проделали в способе 1, и заметим, что это проще и все делается в 16-ричном исчислении
# ~/ec_access -w 2F -v 3a
Проверяем
# od -Ax -t x1 -j 47 -N 1 /sys/kernel/debug/ec/ec0/io
00002f 3a
6. Возвращаем обратно и проверяем
# ~/ec_access -w 2F -v 4a
# od -Ax -t x1 -j 47 -N 1 /sys/kernel/debug/ec/ec0/io
00002f 4a
Описания опций программы нет, но их, при желании, можно подсмотреть в исходниках.

3. Автоматический способ управления.
При использовании этого способа нам не нужно знать, что существует какой то модуль ec_sys т.е. не нужно заботиться ни о загрузке этого модуля ни о том, что у него имеется параметр write_support .
А поэтому раз у меня этот модуль загружен, то я его выгружу
# modprobe -r ec_sys
И проверим - $ lsmod | grep ec_sys ….. пусто
Этот способ строится на скрипте acer_ec.pl , который специально разработали для ноутбука Acer. Насколько я понял в этом ноутбуке, используя embedded controller, можно управлять не только мощностью вентилятора, но, как пишут, и подсветкой монитора и др. К сожалению данный тип ноутбука мне при работе ни разу не попадал и сказать об этом ничего не могу. Но этот скрипт я применял и на других ноутбуках ….. этот скрипт не привязан конкретно к ноутбуку Acer ….
Кстати, в инете по использованию embedded controller с применением этого скрипта, очень много информации — приводить не буду, кому интересно — найдет сам.
Пробуем.....
1. Скачиваем скрипт. Откуда я его скачал, уже не помню, но нашел на GitHub , сверил с имеющимся — и разницы не нашел.
2. Читаем таблицу
# perl acer_ec.pl regs
Полный вывод не привожу — таблица похожа на таблицу способа 2, но значения регистров приведены в 10-ричном исчислении.
Привожу часть таблицы, в которой находятся регистры вентилятора
….............. 0E ….... 0F
10 …......... 255 …..255
20 …......... 75 …....74
Замечу, что 75 — 0x4b , 740x4a
3. Пробуем изменить значение 4a на 3a , как это проделывали раньше
# sudo perl acer_ec.pl := 0x2F 0x3a
REG[0x2f] == 0x4a
REG[0x2f] := 0x3a
REG[0x2f] == 0x4a
Проверяем
# perl acer_ec.pl regs
….............. 0E ….... 0F
10 …......... 255 …..255
20 …......... 58 …....58
(580x3a)
4. Возвращаем все на место и проверяем
# perl acer_ec.pl := 0x2F 0x4a
REG[0x2f] == 0x3a
REG[0x2f] := 0x4a
REG[0x2f] == 0x3a
# perl acer_ec.pl regs
….............. 0E ….... 0F
10 …......... 255 …..255
20 …......... 75 …....74

Ну вот отработали все 3 способа, все работоспособны, а вот какой использовать — каждый решает сам...... безусловно, 3 способ самый простой.

4. Ну и самое последнее — как же определить нужный регистр.
Здесь конкретных рекомендаций нет, все зависит от ситуации и нужна смекалка.
Конечно, если параметр в Linux не управляется, то нужно заниматься определением регистра в Windows. Рекомендую для этих целей неплохую программу RW-Everything.
В этой программе много всяких приблуд, но выбираем EC (embedded controller), появится таблица регистров и их значений, аналогичная показанным выше. В этой таблице непрерывно меняются значения. Например, чтобы определить регистры, ответственные за события вентилятора, любой прогой, умеющей управлять мощностью вентилятора, например, NBFC - NoteBook FanControl, увеличиваем/уменьшаем эту мощность и смотрим в каких регистрах происходит изменение (изменения будут происходит и в других регистрах, но все зависит от ваших действий и наблюдательности). Ну и, конечно, неплохо подстраховаться. Рекомендую зайти на сайт и скачать FanControlConfig для своего ноутбука — этот конфиг является составной частью замечательной проги NBFC - NoteBook FanControl (имеется и для Windows и для Linux), а главное в этом конфиге указаны нужные регистры — например, для моего ноутбуках - привожу выдержки из этого конфига
<FanControlConfig>
<UniqueId>HP ProBook 4530s</UniqueId>
<NotebookModel>HP ProBook 4530s</NotebookModel>
<ReadRegister>46</ReadRegister>
<WriteRegister>47</WriteRegister>
И как видим, это регистры 46 и 47, что в 16-ричном исчислении соответствует 2E и 2F.
Хочу отметить также, что в инете ходят готовые скрипты для управления вентилятором на основе embedded controller - управлять как известно, а потому далее необходимо только привязаться к температуре (реперные точки можно выбрать самому или взять из того же FanControlConfig для нужного ноутбука). Ссылки на скрипты не даю, найти их в инете не сложно.

В части подсветки монитора, если у кого входит в управление embedded controller, можно определить регистры и из Linux. Для чего
- загружаем модуль
# modprobe ec_sys
- запускаем команду
# watch od -Ax -t x1 /sys/kernel/debug/ec/ec0/io
и получаем динамическую, непрерывно меняющуюся таблицу значений регистров. Подсветку можно менять в Linux практически всегда, через xrandr …. и смотрим в каком регистре меняются цифирки ….
Вообщем нужен подход и определить регистр можно всегда.

На этом заканчиваю …... и так написал много лишнего ….

PS .... в этом топике подметили, что скаченный скрипт acer_ec.pl не работает - а потому выкладываю свой, возможно я его и менял, уже не помню. Но этот, выложенный, работает точно.
#!/usr/bin/perl -w

# Copyright (C) 2007  Michael Kurz     michi.kurz (at) googlemail.com
# Copyright (C) 2007  Petr Tomasek     tomasek (#) etf,cuni,cz
# Copyright (C) 2007  Carlos Corbacho  cathectic (at) gmail.com
#
# Version 0.6.1 (2007-11-08)
#
# 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;

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 write_kc
{
	if (!wait_write(0x64)) { outb($_[0], 0x64); }
	if (!wait_write(0x64)) { outb($_[1], 0x60); }
}

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();
}

sub write_temp
{
	initialize_ioports();
	write_ec($_[0],$_[1]);
	close_ioports();
}

sub testnum
{
	my $i;
	for ($i = 0; $i<256;$i++) {
		if ($_[0] eq "$i") { return 1 };
	}
	return 0;
}

my $ii;

if (!$ARGV[0]){
        print "wrong arguments!\n";
	print "usage:\n";
	print "\'acer_ec regs\' \t\t\t\tdumps all ec registers\n";
	print "\'acer_ec ledon\' \t\t\t\tswitch on 'mail LED' (WMID)\n";
	print "\'acer_ec ledoff\' \t\t\t\tswitch off 'mail LED' (WMID)\n";
	print "\'acer_ec getled\' \t\t\t\tget 'mail LED' status (WMID)\n";
	print "\'acer_ec getled2\' \t\t\t\tget 'mail LED' status(AMW0)\n";
	print "\'acer_ec getwireless\' \t\t\t\tget 'wireless' status (AMW0)\n";
	print "\'acer_ec gettouch\' \t\t\t\tis the touchpad disabled?\n";
	print "\'acer_ec setfanthresh <temp>\' \t\t\t\tset temperature threshhold to <temp>, DANGEROUS!\n";
	print "\'acer_ec getfanthresh\' \t\t\t\tget temperature threshhold\n";
	print "\'acer_ec <temp-number> <temperature>\' \tfor setting a temperature\n";
	print "where <temp-number> is from 0-7, and <temperture> is from 0-255\n";
	print "\'acer_ec ?= <reg>\' \t\tQuery register's value\n";
	print "\'acer_ec := <reg> <val>\' \tSet register's value\n";
	print "\'acer_ec +f <reg> <val>\' \tOr register's value with val (to set flags)\n";
	print "\'acer_ec -f <reg> <val>\' \tAnd register's value with ~val (to clear flags)\n";
	print "\'forcekc\' \tTry all possible values on writeable RAM of keyboard controller\n";
	print "\'kcw <cmd> <val>\' \tWrite a command and a value to the keyboard controller\n";
} elsif ($ARGV[0] eq "regs") {
	print_regs();
} elsif ($ARGV[0] eq "getled") {
	# TM2490 only (WMID)
	initialize_ioports();
	if (read_ec(0x9f)&0x01) {
		print "Mail LED on\n";
	} else {
		print "Mail LED off\n"; }
	close_ioports();
} elsif ($ARGV[0] eq "getled2") {
	# Aspire 5020 only (AMW0)
	initialize_ioports();
	if (read_ec(0x0A)&0x80) {
		print "Mail LED on\n";
	} else {
		print "Mail LED off\n"; }
	close_ioports();
} elsif ($ARGV[0] eq "getwireless") {
	# Aspire 5020 only (AMW0)
	initialize_ioports();
	if (read_ec(0x0A)&0x4) {
		print "Wireless on\n";
	} else {
		print "Wireless off\n"; }
	close_ioports();
} elsif ($ARGV[0] eq "gettouch") {
	# TM2490 only - needs testing
	initialize_ioports();
	if (read_ec(0x9e)&0x08) {
		print "touchpad disabled\n";
	} else {
		print "touchpad enabled\n"; }
	close_ioports();
} 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 "+f") {
	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, $val | $f);
        write_ec( $r, $val | $f);
	printf("REG[0x%02x] == 0x%02x\n", $r, read_ec($r));
	close_ioports();
} elsif ($ARGV[0] eq "-f") {
	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, $val & ~$f);
        write_ec( $r, $val & ~$f);
	printf("REG[0x%02x] == 0x%02x\n", $r, read_ec($r));
	close_ioports();
} elsif ($ARGV[0] eq "ledon") {
	# TM2490 only - needs testing
	initialize_ioports();
	if (!wait_write(0x64)) { outb(0x59, 0x64); }
	if (!wait_write(0x64)) { outb(0x92,   0x60); }
	close_ioports();
} elsif ($ARGV[0] eq "ledoff") {
	# TM2490 only - needs testing
	initialize_ioports();
	if (!wait_write(0x64)) { outb(0x59, 0x64); }
	if (!wait_write(0x64)) { outb(0x93,   0x60); }
	close_ioports();
} elsif ($ARGV[0] eq "getfanthresh") {
	initialize_ioports();
	$ii=read_ec(0xa9);
	close_ioports();
        print "Temperature threshhold: $ii (celsius)\n";
} elsif (($ARGV[0] eq "setfanthresh") && testnum($ARGV[1])) {
	write_temp(0xA9,$ARGV[1]);
} elsif ($ARGV[0] eq "setbright") {
	# Aspire 5020 only (AMW0)
	if ($ARGV[1] >= 0 && $ARGV[1] <= 15) {
		write_temp(0x83, $ARGV[1]);
	} else {
		print "second argument must be a number between 0 and 15\n";
	}
} elsif ($ARGV[0] eq "forcekc") {
	# Be smart - we only send the commands for writing to keyboard RAM
	initialize_ioports();
	my ($kbdata, $cont, $kbreg);
	for ($kbreg = 0x40; $kbreg <= 0x5f; $kbreg++) {
		for ($kbdata = 0; $kbdata < 256; $kbdata++) {
			write_kc($kbreg, $kbdata);

			print sprintf("%0#4x", $kbreg), ", ", sprintf("%0#4x", $kbdata), "\n";
			print "Continue? y/n: ";
			$cont = <STDIN>;
			if ($cont eq "n") {
				last;
			}
		}
	}
	close_ioports();
} elsif ($ARGV[0] eq "kcw") {
	initialize_ioports();
	write_kc($ARGV[1], $ARGV[2]);
	close_ioports();
} else {
	print "wrong arguments!\n";
}

Дополнение.
После применения скрипта, точнее перевода управлением куллера из автоматического режима в ручной, этот режим таковым и останется до выключения компьютера (сброс только при выключении, при reboot может не сработать).
Чтобы перевести куллер снова в автоматический режим, нужно ввести соответствующее значение в соответствующий регистр. Смотреть здесь для своего ноута.
Мой случай
записать в регистр 2F (регистр записи) значение FF
sudo perl acer_ec.pl := 0x2F 0xFF
Использовал для этого информацию конфига для моего ноута, а точнее строку
<FanSpeedResetValue>255</FanSpeedResetValue>
Ошибки не исчезают с опытом - они просто умнеют
основательно пишешь, продолжай в том же духе. vasek - в твоих блогах узнаю много интересного и полезного...
vasek
опять написал ерунду, чтобы показать себя...
Вот если бы все писали вот такую ерунду, насколько жить было бы легче! А статья действительно полезная. Таких статей — тех, которые по существу, всегда мало.
root@vpupkin# cat /dev/ass > /dev/head
Сделал два дополнения
1) привел работающий скрипт acer_ec.pl
2) перевод управления куллером из ручного режима в автоматический из работающей системы, без выключения компьютера.

Дополнения сделаны при обсуждении проблемы в этом топике, за что благодарен всем, принимавшим участие в обсуждении.
Ошибки не исчезают с опытом - они просто умнеют
Внес изменение - сейчас модуль ec_sys встроен в ядро
zgrep ACPI_EC /proc/config.gz
CONFIG_ACPI_EC_DEBUGFS=y
то есть модуль загружать не нужно, чтение значений регистров будет работать без загрузки модуля
Но значение параметра write_support=N
sudo cat /sys/module/ec_sys/parametyers/write_support
N
и чтобы выполнить запись в регистр нужно загрузиться с параметром write_support=1, точнее - ec_sys.write_support=1
Ошибки не исчезают с опытом - они просто умнеют

Верхний- до повышени оборотов
Нижний- после повышения..
Как найти эти чертовы регистры чтения и записи?
Вроде как бы все нормально, но кулер останавливается только в районе ниже 40 градусов. Т.е. постоянно потихонечку тарахтит постоянно.
Или может попробовать через DSDT, что вроде как даже полегче.. Только вот у себя секцию с вентилятором найти не могу
Morisson
Или может попробовать через DSDT, что вроде как даже полегче.. Только вот у себя секцию с вентилятором найти не могу
Если править DSDT, то нужно понять как устроено управление мощностью вентилятора. Есть несколько способов, например, динамическое изменение (как правило в случае отдельно установленного вентилятора), а есть и ступенчатое, как правило при установке одного вентилятора. У меня, например, стоит 3 спупени - при достижении верхней границы диапазона/ступени возрастает мощность вентилятора. Эти ступени можно грубо определить на слух.
Эти же ступени должны быть описаны и в DSDT, например, в моем случае верхняя граница одной из ступени 52 град.С (34 в 16-ричной системе)
Method (_TMP, 0, Serialized)  // _TMP: Temperature
            {
                Return (GDTP (0x34, 0x00))
            }
Там же должны быть описаны trip_point температурные точки (посмотреть их можно в /sys/class/thermal/thermal_zone*).
Как то так - но давно не разбирался с этим, а потому многое уже и забылось, могу в чем то и ошибаться.

PS - но можно еще применить и прогу для управления мощностью вентилятором, которая позволяет изменять значения температуры (как то приводил в одном из топиков)
Ошибки не исчезают с опытом - они просто умнеют
Morisson
Верхний- до повышени оборотов
Нижний- после повышения..
Посмотрел мельком и удивился - очень много измененных значений регистров. С одной стороны это и хорошо - многие параметры заведены на контролер.
Смотреть доволно утомительно (плохо читается картинка), но если смотреть значения, которые изменились на много, то можно выделить следующие регистры
PS - не учитывал регистры, значения в которых увеличились (чем меньше значение в регистре, тем больше значение параметра).
 xx  -  yy (строка - столбец), в скобках значения до и после.
10 -  03 (245/53)
C0 - 02 (139/111)
D0 - 02 (136/65)
C0 - 06 (225/74)
Но, имхо, вряд ли это то, что нужно. Рекомендую следующее
Сначала определиться с типом управления мощностью вентилятора (если ступенчато, то хорошо заметно на слух)
Лучше использовать не скрипт acer_ec.pl , а на прямую (системные значения) - лучше картинка и сразу в 16-ричном исчислении (у меня и hexdump и od прописаны в sudoers)
sudo od -Ax -t x1 /sys/kernel/debug/ec/ec0/io
Фиксируем вывод при низкой температуре, увеличиваем температуру градусов до 60-65 и снова фиксируем и еще раз увеличиваем температуру градусов до 80 и снова фиксируем.
Отбираем регистры, значения в которых существенно уменьшились.
А вот дальше можно и пощупать эти регистры в плотную, например, используя следующий скрипт (хоть замеры и идут в разное время, но кореляция хорошо заметна)
cat ~/TTT/TEST/bash/ec.sh
#!/bin/bash
#
while true ; do
VAL_REG=$(sudo od -Ax -t x1 /sys/kernel/debug/ec/ec0/io | awk '{print $16}' | awk '(NR == 3)')
Tsys=$(awk '{ print $1/1000 }' /sys/class/thermal/thermal_zone6/temp)
echo "$VAL_REG  ...  $Tsys"
sleep 1
done
#
exit 0
PS - awk '{print $16}' | awk '(NR == 3)' - проверяется регистр 2E, расположенный в 16 столбце и в 3 строке
Температуру повышал, используя утилиту stress - запускаю в одном терминале скрипт показаний регистра, в другом утилиту stress секунд на 15-20 (пример запуска приведен в stress --help)
Пример вывода скрипта (1-ая колонка - показания регистра, 2-ая колонка показания температуры)
4b  ...  53
4a  ...  67
4a  ...  68
4a  ...  68
4a  ...  69
44  ...  70
42  ...  70
41  ...  70
3e  ...  71
3c  ...  72
3b  ...  72
3c  ...  72
3c  ...  73
3b  ...  62

PS - я 1-ый раз смотрел из винды (есть одна неплохая прога), меняя обороты вентилятора с помощью проги (для fan) - заметил несколько регистров. А дальше уже проверял их в Linux.

EDIT 1 - можно еще попробовать поставить из AUR nbfc - контроль/управление вентилятором на основе этих самых регистров. Когда то давно пытался с ней поэкспериментировать, но до конца так дело и не дошло, проще оказалось ручками, если знаешь регистры. Но вот не помню, есть ли там база данных этих регистров для разных ноутбуков или же вписываешь сам.
yay -Ss nbfc
aur/nbfc-git 1.6.3.r9.g4699d17-1 (+2 0.55%)
Cross-platform fan control service for notebooks (Development version)
aur/nbfc 1.6.3-1 (+6 0.78%)
Cross-platform fan control service for notebooks (STABLE & BETA releases)
Ошибки не исчезают с опытом - они просто умнеют
 
Зарегистрироваться или войдите чтобы оставить сообщение.