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

Всем привет. Однажды мне захотелось собрать 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

Средняя зарплата в IT

111 000 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 7 000 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 10

    0
    Раз уж половина статьи — это код, можно немножко побрюзжу?

    Легко же облагородить код: return сразу вместо ифа, обрамляющего весь код внутри main (я про if( -1 != (current_level=getLevel()) ) ), getopt + switch для проверки опций. Выглядеть будет намного понятней, более логично, и кривые опции нельзя будет запихнуть.
      0
      Спасибо за замечение, заменил на switch. А getopt честно говоря никогда не пользовался, стыдно! Спасибо за подсказку, избавили меня от велосипедов.
      0
      чем не понравился xbacklight?
        0
        Проблему решал давно и видать «гугл не завел меня к нему». Спасибо за подсказку, попробую с ним поиграть.
        0
        А почему С, а не скрипт на баше?
          0
          Дело в том, что на момент решения проблемы я был не очень силен в Bash-е, тратить время на быстрый курс по нему занял бы у меня больше времени чем написание программы на C.
          0
          Молодец, автор. Не смотря на критику других комментаторов, считаю, что иметь дополнительный путь для регулировки подсветки экрана нелишне.
          Помню, как у меня xbacklight глючил — иногда просто переставал работать. Возможно, этот способ поможет мне, если этот глюк опять всплывёт.
            0
            А зачем указывать в строке режима открытия файла символ b? Программа же все равно будет использоваться только в Linux…

            device_file = fopen(DEVICEPATH,"rb");
            
              0
              В данном случае не принципиально
              0
              KOLANICH подкинул идею написать регулятор подсветки по уровню освещенности, который можно получить при помощи веб-камеры.
              Как будет свободное время попробую написать.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое