22 KiB
Предостережение
Предоставленный исходный код драйвера devi-hid рекомендуется использовать только для отладочных целей при разработке HID-драйверов. Для всех остальных задач следует использовать штатный системный драйвер devi-hid. В общем случае не существует задач, которые требуется решать в этом драйвере при поддержке нового оборудования (для этого должен быть написан/модифицирован отдельный драйвер ввода или HID-драйвер).
Общая структура подсистемы ввода
┌───────────────────────────┐
│ │
│ Устройство ввода ◂───────────────────────┐
│ │ │
└─────────────▴─────────────┘ │
│ │
┌─────────────┴─────────────┐ │ Не HID-совместимое
│ │ │ устройство
│ HID-драйвер (devh-*) │ │
│ │ │
└─────────────▴─────────────┘ │
│ │
┌─────────────┴─────────────┐ ┌─────────────┴─────────────┐
│ │ │ │
│ HID-менеджер (io-hid) ◂─── * ───┤ Драйвер ввода (devi-*) ◂───▸ /dev/??? (standalone-режим)
│ │ │ │
└───────────────────────────┘ ▲ └─────────────▴─────────────┘
│ │
│ ┌─────────────▾─────────────┐
Интерфейс libhiddi ─────┘ │ │
│ Оконная оболочка Photon │
│ │
└───────────────────────────┘
Дерево исходных кодов
|- devi/
| |- egalax/ - Исходный код драйвера поддержки тачскринов egalax, подключаемых по PS/2
| |- hid/ - Исходный код драйвера HID-совместимых устройств (только для отладочных целей)
| |- include/ - Заголовочные файлы библиотек
| |- keymap/ - Библиотека трансляции символов (libkeymap)
| |- lib/ - Библиотека менеджера ресурсов драйвера (libinput)
| |- public/ - Публичные интерфейсы драйвера
| |- Makefile - Правила сборки дерева исходников
| `- common.mk - Параметры сборки драйверов и библиотек
|
`- Makefile - Правила сборки дерева исходников
Сборка драйвера
- Установить и настроить комплект разработчика для ЗОСРВ "Нейтрино" редакции 2020.
- Выполнить команду:
make
Запуск драйвера
Общая схема запуска драйвера:
devi-* <опции_драйвера> <протокол> <опции протокола> [<устройство> <опции_устройства>] [<фильтр> <опции_фильтра>]
Примеры:
devi-hirun -b kbd fd -d /dev/kbd ps2 mousedev
└───┬────┘ └───┬───┘ └───┬────┘ └────┬─────┘ └───────┬────────┘ └───┬────┘ └────┬─────┘
│ │ │ │ │ │ │
│ │ │ │ Опции устройства │ │
│ │ │ Устройство │ Устройство
│ │ Протокол Протокол
│ Опции
Драйвер
devi-hid -b touch mouse kbd
└───┬───┘ └───┬───┘ └───────┬───────┘
│ │ │
│ │ Протоколы
│ Опции
Драйвер
События ввода (events)
Маршрутизация событий:
Hardware → Device → Protocol → Filter → Photon или /dev/???
Виды событий ввода:
- Относительные - содержат информирмацию об изменении координат на некоторую величину (мышь, touchpad)
- Абсолютные - содержащие координаты в контекста собственного разрешения устройства ввода (touchscreen)
- Клавиатурные - содержат сведения об изменении состояния клавиатуры (состояние клавиш, сканкоды и статусные флаги)
(!) Устройства с абсолютным позиционированием необходимо калибровать перед использованием. Это позволяет установить соответствие между пространством координат устройства ввода и координатами дисплея.
Интерфейсы и взаимодействие
Пример чтения ctrl-структуры в клиентском приложении:
#include <sys/dcmd_input.h>
...
int main()
{
struct _keyboard_ctrl kbctrl;
int fd = open( "/dev/keyboard0", O_RDWR );
...
/* Инициализация */
...
if ( devctl( fd, _KEYBOARDGETCTRL, &kbctrl, sizeof( kbctrl ), NULL ) != EOK )
{
/* Обработка ошибок */
} else {
/* Структура контроля клавиатуры считана в переменную kbctrl. Можно приступать к ее анализу. */
}
}
Разработка драйвера
Дескриптор модуля:
struct _input_module
{
/* Module parameters */
input_module_t *up; /* Up module in the bus line */
input_module_t *down; /* Down module in the bus line */
struct Line *line; /* Bus line */
int flags; /* flags */
int type; /* type of module */
char name[12]; /* Module name */
char date[12]; /* Date of compilation */
const char *args; /* Module arguments */
void *data; /* Private module data */
/* Callbacks */
int (*init)( input_module_t *module );
int (*reset)( input_module_t *module );
int (*input)( input_module_t *module, int num, void *data );
int (*output)( input_module_t *module, void *data, int num );
int (*pulse)( message_context_t *ctx, int code, unsigned flags, void *data );
int (*parm)( input_module_t *module, int code, char *optarg );
int (*devctrl)( input_module_t *module, int event, void *data );
int (*shutdown)( input_module_t *module, int delay );
}
Инициализация драйвера:
- Заполнить структуру дескриптора модуля
- Заполнить структуру
_common_callbacks
(см. ниже): - Выполнить инициализацию библиотеки
libinput
, вызвав функцию:devi_init( (struct _common_callbacks *)&ptr );
- Выполнить всю необходимую инициализацию прочих библиотек и ресурсов
- Передать управление библиотеке
libinput
, вызвав функцию:begin( (int)argc, (char**)argv );
Структура _common_callbacks
:
struct _common_callbacks {
int nCallbacks;
int (*pre_init)();
int (*post_init)();
int (*pre_shutdown)();
int (*post_shutdown)();
}
Диаграмма последовательности инициализации:
┌──────────────────────────┐ ┌──────────────────────┐ ┌──────────────────────────┐
│ Драйвер ввода (devi-*) │ │ Библиотека libinput │ │ Модуль │
└────────────┬─────────────┘ └──────────┬───────────┘ └────────────┬─────────────┘
│ │ │
│ devi_init() │ │
├──────────────────────────▸│ │
│ │ │
│ begin() │ │
├──────────────────────────▸│ │
│ │ │
│ pre_init_callbacks() │ │
│◂──────────────────────────┤ │
│ ├──┐ │
│ │ │ Разбор опций, иницализация │
│ │ │ структур модуля │
│ │◂─┘ │
│ │ │
│ │ init callback │
│ ├───────────────────────────────▸│
│ │ │
│ │ param callback │
│ ├───────────────────────────────▸│
│ │ │
│ │ reset callback │
│ ├───────────────────────────────▸│
│ │ │
│ post_init_callbacks() │ │
│◂──────────────────────────┤ │
│ ├──┐ │
│ │ │ Иницализация интерфейсов │
│ │◂─┘ │
│ ├──┐ │
│ │ │ Иницализация пула потоков │
│ │◂─┘ │
│ ├──┐ │
│ │ │ Запуск менеджера ресурсов │
│ │◂─┘ │
│ │ │
Обработка событий (pulse):
- Вызывается в ответ на возникновение события
- Обычно реализуется в модулях устройств
- Выполняет сбор данных из устройства
- Возможные способы регистрации обработчика:
devi_register_interrupt()
,devi_register_pulse()
илиdevi_register_timer()
- Для передачи данных далее по Busline используется функция
module->up->input()
Ввод данных (input) – передача данных клиенту:
- Вызывается в моменты, когда модуль более низкого уровня готов к передаче данных
- Модуль устройства:
- Выполнить сбор данных
- Упаковать их в согласованный с модулем протокола формат
- Передать их на уровень модуля-протокола, используя функцию
module->up->input()
- Модуль протокола:
- Преобразовать данные в стандартную структуру пакета, соответствующего обслуживаемому устройству (
packet_kbd
,packet_abs
,packet_rel
) - Передать их на уровень модуля-протокола, используя функцию
module->up->input()
- Преобразовать данные в стандартную структуру пакета, соответствующего обслуживаемому устройству (
- Модуль фильтра:
- Выполнить необходимую коррекцию принятых данных
- Передать данные на реализуемые интерфейсы, используя функцию
devi_enqueue_packet()
Диаграмма последовательности ввода данных:
┌────────────┐ ┌────────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐ ┌──────────┐
│ Hardware │ │ libinput │ │ Device │ │ Protocol │ │ Filter │ │ Клиент │
└─────┬──────┘ └──────┬─────┘ └─────┬────┘ └──────┬─────┘ └─────┬────┘ └─────┬────┘
│ │ │ │ │ │
│ │ devi_interrupt_attach() │ │ │ │
│ │◂──────────────────────────┤ │ │ │
│ Прерывание │ │ │ │ │
├───────────────▸│ pulse_callback() │ │ │ │
│ ├──────────────────────────▸│ │ │ │
│ │ │ │ │ │
│ │ read() │ │ │ │
│◂───────────────────────────────────────────┤ │ │ │
│ │ │ │ │ │
│ Данные │ │ input │ │ │
├───────────────────────────────────────────▸│ callback │ input │ │
│ │ ├─────────────▸│ callback │ │
│ │ │ ├─────────────▸│ devi_enqueue_packet() │
│ │ │ │ ├────────────────────────▸│
│ │ │ │ │ │
Управление устройством (devctl):
- Реализует управление модулем и реакцию на внешние devctl() команды
- Собственные devctl() команды можно зарегистрировать, используя константы из заголовочного файла
include/const.h
- Модуль устройства:
- Если тип события известен – выполнить обработку
- Если тип события не известен – вернуть значение
-1
и установить переменную errno в значениеEINVAL
;
- Модуль протокола:
- Если тип события известен – выполнить обработку
- Если тип события не известен и имеется связанный модуль нижнего уровня – передать событие далее
Обзор API библиотеки libinput
- Работа с циклическими буферами (см. функции семейства
buff_*()
) - Системные функции:
clk_get()
devi_request_iorange()
- Передача данных на/от реализуемых интерфейсов:
devi_enqueue_packet()
- Регистрация обработчиков событий (см.
devi_register_*()
) - Доступ к данным менеджера ресурсов:
devi_get_coid()
devi_get_dispatch_handle()
/devi_set_dispatch_handle()
- Работа с менеджером io-hid:
devi_hid_init()
devi_hid_register_client()
/devi_hid_unregister_client()
devi_hid_server_connect()
/devi_hid_server_disconnect()
devi_scan_to_usb()
/devi_usb_to_scan()