Операционная система ЗОСРВ «Нейтрино» > Руководство разработчика > Основные принципы системной разработки > Разработка драйверов и драйверные библиотеки > Драйверные библиотеки > Библиотека разработки драйверов ввода (libinput) > Статьи и обзоры > Разработка драйвера ввода



Разработка драйвера ввода

В статье приведён обзор общего подхода к разработке драйверов ввода

Список подразделов:

Создание модуля ввода
Форматы данных
Клавиатуры
Устройства с абсолютными координатами
Формат файла калибровки
Устройства с относительными координатами
Функции-обработчики драйвера
Обязательные обработчики
Комбинированные модули устройств и протоколов
Многопоточность

Создание модуля ввода

При разработке драйвера в первую очередь следует создать новый модуль ввода. Он описывается структурой input_module_t. Детали реализации можно изучить в README-файле в публичном репозитории, а также в опубликованных примерах реальных драйверов.

Форматы данных

В зависимости от типа модуля ввода формат передаваемых в систему данных отличается. Модули устройств могут передавать любые данные, модули протоколов должны передавать данные в том формате, который ожидает ассоциированный модуль фильтра (см. input_module_t :: type):

DEVI_CLASS_KBD
Используется struct packet_kbd, описанная в <sys/devi.h>.
DEVI_CLASS_REL
Используется struct packet_rel, описанная в <sys/devi.h>.
DEVI_CLASS_ABS
Используется struct packet_abs, описанная в <sys/devi.h>.

Все перечисленные структуры имеют поле timestamp, которое может быть проинициализировано с помощью clk_get().

Клавиатуры

Модули протокола и фильтра будут ожидать скан-коды, включая флаги нажатия/отжатия клавиши. При заполнении структуры struct packet_kbd для передачи фильтру следует:

Фильтр клавиатуры считывает файл раскладки клавиатуры (.kbd) и интерпретирует получаемые им скан-коды на основе содержимого этого файла. Эти файлы обычно размещаются по адресу /usr/photon/keyboard. Их исходные описания .kdef доступны в КР по адресу $KPDA_TARGET/usr/include/keyboard и могут быть сгенерированы с помощью утилиты mkkbd.

При запуске драйвера модуль фильтра попытается подгрузить файлы раскладки согласно приоритетам:

  1. Раскладки, перечисленные в /etc/system/trap/.KEYBOARD.
  2. Раскладка, указанная в переменной окружения KBD.
  3. Раскладка en_US_101.kbd из директории $PHOTON/keyboard.
  4. Раскладка en_US_101.kbd из директории /usr/photon/keyboard.

Устройства с абсолютными координатами

Такие устройства требуют калибровки, поскольку координаты имеют аппаратно-обусловленное разрешение, которое никак не коррелирует с разрешением дисплея. Модули устройства и протокола получают сырые аппаратно-специфичные координаты, упаковывают их в struct packet_abs и отправляют абсолютному фильтру.

Фильтр транслирует координаты из пространства устройства в координаты экрана (калибровка), выбирая его согласно приоритетам:

  1. Файл, указанный через опцию -f драйвера.
  2. Файл, указанный в переменной окружения ABS.
  3. Файл /etc/system/config/calib.<hostname>.

Данный файл может быть подготовлен только вручную, с помощью утилит calib и gf-calib.

Формат файла калибровки

Формат файла текстовый:

XLxYL:XHxYH:XRL XRH YRL YRH SWAP

где:

XL
X-координата верхнего левого угла дисплея (обычно 0).
YL
Y-координата верхнего левого угла дисплея (обычно 0).
XH
X-координата нижнего правого угла дисплея (обычно разрешение по горизонтали - 1).
YH
Y-координата нижнего правого угла дисплея (обычно разрешение по вертикали - 1).
XRL
Сырая X-координата верхнего левого угла устройства.
XRH
Сырая X-координата нижнего правого угла устройства.
YRL
Сырая Y-координата верхнего левого угла устройства.
YRH
Сырая Y-координата нижнего правого угла устройства.
SWAP
Нужно ли менять местами X или Y координаты (0 - нет, 1 - да). Обычно должно иметь значение 0.

Устройства с относительными координатами

Модуль протокола получает сырые смещения координат устройства, должен запаковать их в формат struct packet_rel и передает их модулю фильтра. Фильтр применяет алгоритм ускорения (мультиплицирование смещений), конвертирует сырые данные и передает их в систему.

Функции-обработчики драйвера

Основная трудоемкость при разработке обычно сконцентрирована в реализации обработчиков, перечисленных в структуре input_module_t:

input_module_t :: init()
Вызывается однократно при загрузке драйвера.
input_module_t :: reset()
Используется для сброса состояния модуля или устройства. Может быть вызван только после создания шины событий. Драйвер может вызывать этот callback при любых неисправностях.
input_module_t :: input()
Обычно реализуется модулями протоколов как часть канала коммуникации между устройством и системой. callback предназначен для отправки данных на следующий уровень шины.
input_module_t :: output()
Используется модулями более высокого уровня для запроса передачи данных в устройство (обычно, команд).
input_module_t :: pulse()
Обычно реализуется модулями устройств для информирования вышележащих модулей о поступлении данных и возникновении прерываний.
input_module_t :: parm()
Вызывается при запуске драйвера для анализа аргументов командной строки.
input_module_t :: devctrl()
Используются модулями для отправки внутренних команд по шине данных. Также используется для реализации пользовательских devctl().
input_module_t :: shutdown()
Вызывается при завершении драйвера.

Основная последовательность callback-ов имеет вид: input_module_t :: init()input_module_t :: parm()input_module_t :: reset().

Обязательные обработчики

Для модулей устройств типовой набор следующий:

Для модулей протоколов основной callback - input_module_t :: input(), опционально также могут присутствовать input_module_t :: init(), input_module_t :: parm(), input_module_t :: devctrl().

Комбинированные модули устройств и протоколов

В основном модули устройств и протоколов объединяют в один программный объект. Кроме заполнения callback-ов, инициализации и опроса оборудования и интерпретации протокола Вам будет необходимо:

  1. Установить в поле input_module_t :: type комбинацию флагов DEVI_MODULE_TYPE_DEVICE и DEVI_MODULE_TYPE_PROTO.
  2. Не только интерпретировать сырые данные, но и упаковывать их в соответствующие struct packet_*.

Многопоточность

Поскольку фреймворк библиотеки libinput является многопоточным, обращения к модулю могут быть многократными для различных шин событий. Примером является модуль kb, который может быть ассоциирован как с PS/2 клавиатурами, так и с PS/2 мышами:

devi-hirun kbd kb ps2 kb -2

При инициализации статическая структура input_module_t будет использована лишь для первой шины, второй экземпляр будет динамическим. Проблема может возникнуть при доступе к приватным данным драйвера, особенно реализованных в виде глобальных переменных. Их следует отдельно защищать примитивами синхронизации.

Callback-функции input_module_t :: init(), input_module_t :: reset() и input_module_t :: parm() в защите не нуждаются, этим занимается библиотека. Однако, они являются таковыми в момент инициализации, а не при ручном вызове этих callback-ов. Другие callback-и не являются безопасными в принципе.




Предыдущий раздел: Библиотека разработки драйверов ввода (libinput)