input/README.md

22 KiB
Raw Permalink Blame History

Предостережение

Предоставленный исходный код драйвера 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             - Правила сборки дерева исходников

Сборка драйвера

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