8 июля 2013

Пишем регулировщик подсветки дисплея ноутбука для Gnu\Linux

ПрограммированиеC
Tutorial
Из песочницы
Всем привет. Однажды мне захотелось собрать Gentoo на своем ноутбуке Samsung n143. После ночи компиляции дело дошло до сборки графической оболочки. Выбрал привычную для меня KDE4, собрал, установил. Всё оборудование и Fn-keys работали на отлично кроме Fn-клавиш регулировки уровня подсветки дисплея (и программная регулировка в KDE System Settings). Пытался решить проблему установкой пакетов samsung-backlight и samsug-tools, но ничего не получилось.
Те кому интересно как я решил эту проблему, прошу под кат.

Немного погуглив нашел способ регулировки подсветки через «setpci». Для этого необходимо сначала получить весь список PCI устройств при помощи lspci, найти «адрес» необходимого нам устройства и установить ему желаемое значение.
# lspci
00:00.0 Host bridge: Intel Corporation Mobile 4 Series Chipset Memory Controller Hub (rev 07)
00:02.0 VGA compatible controller: Intel Corporation Mobile 4 Series Chipset Integrated Graphics Controller (rev 07)
00:02.1 Display controller: Intel Corporation Mobile 4 Series Chipset Integrated Graphics Controller (rev 07)
00:1a.0 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #4 (rev 03)
00:1a.1 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #5 (rev 03)
00:1a.2 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #6 (rev 03)
00:1a.7 USB Controller: Intel Corporation 82801I (ICH9 Family) USB2 EHCI Controller #2 (rev 03)
00:1b.0 Audio device: Intel Corporation 82801I (ICH9 Family) HD Audio Controller (rev 03)
00:1c.0 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 1 (rev 03)
00:1c.2 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 3 (rev 03)
00:1c.3 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 4 (rev 03)
00:1d.0 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #1 (rev 03)
00:1d.1 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #2 (rev 03)
00:1d.2 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #3 (rev 03)
00:1d.7 USB Controller: Intel Corporation 82801I (ICH9 Family) USB2 EHCI Controller #1 (rev 03)
00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 93)
00:1f.0 ISA bridge: Intel Corporation ICH9M LPC Interface Controller (rev 03)
00:1f.2 SATA controller: Intel Corporation ICH9M/M-E SATA AHCI Controller (rev 03)
00:1f.3 SMBus: Intel Corporation 82801I (ICH9 Family) SMBus Controller (rev 03)
02:00.0 Ethernet controller: Atheros Communications Inc. AR242x / AR542x Wireless Network Adapter (PCI-Express) (rev 01)
06:00.0 Ethernet controller: Marvell Technology Group Ltd. 88E8057 PCI-E Gigabit Ethernet Controller (rev 10)

В моем случае «нужным» устройством является «00:02.0 VGA compatible controller: Intel Corporation Mobile 4 Series Chipset Integrated Graphics Controller (rev 07)». Для конфигурирования pci устройств можно воспользоваться утилитий «setpci».
Для выполнения необходимы права root:
# setpci -s 00:02.0 F4.B=<VALUE>

Где
  • VALUE — значение уровня подсветки в HEX(00..FF)
  • s — ключ позволяет указать устройство в следующем формате:"[[[[]:]]:][][.[] ]".
    Так же можно вользоваться ключем -d и указать []:[] (Для получения этих данных нужно вызвать lspci с параметром "-n")


    Ура! Подсветка менялась, это не могло не радовать. Но сразу же возниклка следующая проблема: ступенчатая регулировка уровня подсветки по Hot-Key-ям. Я решил написать маленькую программу на C, которая будет читать\писать в "файл устройства" значения уровня подсветки.

    Исходный код программы:
    #include <stdio.h>
    #include <stdlib.h>
    
    #define DEVICEPATH "/sys/devices/pci0000:00/0000:00:02.0/config" //Пусть к устройству
    #define STEP 10 //Шаг для изменения уровня подсветки
    
    int getLevel(void)
    {
       int current_level = -1;
       FILE *device_file = 0;
       device_file = fopen(DEVICEPATH,"rb");
    
       if( 0 != device_file)
       {
          if( 0 == fseek(device_file,244,0) )
          {
             current_level = fgetc(device_file);
             fclose(device_file);
          }
       }
    
       return current_level;
    }
    
    int ChangeLevel(const int value)
    {
       int functionResult = -1;
    
       if( (value > -1) && (value < 256) )
       {
          FILE *device_file = 0;
          device_file = fopen(DEVICEPATH,"r+b");
    
          if( 0 != device_file )
          {
             if( 0 == fseek(device_file,244,0) )
             {
                fputc(value,device_file);
    
                fclose(device_file);
                functionResult = 0;
             }
          }
       }
       else
       {
          printf("Error! Incorrect input value. Try 0..255\n");
       }
    
       return functionResult;
    }
    int main(int argc, char * argv[])
    {
       int current_level = 0;
    
       if( argc > 1 && argc < 3 )
       {
          if( -1 != (current_level=getLevel()) )
          {
             switch( *argv[1] )
             {
             case 'u':
                if( current_level < ( 255 - STEP ) )
                {
                   if( -1 != ChangeLevel( current_level + STEP ) )
                   {
                      printf("Ok, done\n");
                   }
                }
                else
                {
                   printf("Error setting level\n");
                }
                break;
             case 'd':
                if( current_level > (0+STEP) )
                {
                   if( -1 != ChangeLevel(current_level-STEP) )
                   {
                      printf("Ok, done\n");
                   }
                }
                else
                {
                   printf("Error setting level\n");
                }
                break;
            case 'c' :
                printf("Current brightness level: %d\n",current_level);
                break;
    
             case 's':
                ChangeLevel(atoi(argv[2]));
                break;
             default:
                printf("Invalid arguments\n");
                break;
             }
          }
          else
          {
             printf("Error checking brightness level\n");
          }
       }
       else
       {
          printf("Arguments: [u|d|c|s <0..255>]\n");
          printf("Usage:\nu - increase brightness by STEP value\n");
          printf("d - decrease by STEP\n");
          printf("c - show current level\n");
          printf("s - set brightness to value 0..255.\n   0 - backlight off | 255 - maximum level\n");
       }
    
       return 0;
    }
    


    Скомпилируем код:
    gcc main.c -o bcontroller
    

    Установим права root-а:
    chown root:root bcontroller
    chmod +s bcontroller
    

    Копируем программу в bin:
    cp ./bcontroller /usr/bin/bcontroller
    

    Как видно из исходного кода, приложение можно вызывать с разными параметрами:
    • u - Увеличить уровень подсветки на 1шаг
    • d - Уменьшить уровень подсветки на 1 шаг
    • s - Установить значение уровня подсветки в диапазоне 0..255
    • c - Просмотреть текущий уровень подсветки

    После того как мы скопировали программу в /usr/bin, привяжем её к "горячим клавишам" в KDE4. Заходим в параметры системы -> Действия, либо Параметры -> Клавиатура и мышь -> Комбинации клавиш (или Основные действия). Выбираем компонент который отвечает за регулирование подсветки, выбираем действие увеличения уровня подсветки и вписываем комманду: bcontroller u. Аналогично можно добавить и для уменьшения, выключения\включения подсветки. В GNOME можно всё это сделать через System -> Preferences ->Keyboard Shortcuts ->Add. В всплвающем меню добавляем команду и горячие клавиши.
    Спасибо за внимание!

    P.S. Спасибо за исправление ошибки, newdya
Теги:CLinuxGnu\Linuxпрограммированиеподсветкаpci-устройства
Хабы: Программирование C
+11
9,2k 64
Комментарии 10
Лучшие публикации за сутки

Минуточку внимания

Разместить