В статье приведён обзор общего подхода к разработке HID-драйверов
При разработке драйвера в первую очередь следует создать модуль драйвера. Он описывается структурой io_hid_dll_entry_t.
Пример:
io_hid_dll_entry_t io_hid_dll_entry = {"devh-sample",2,sample_init,sample_stop};
Вызывается однократно при запуске драйвера. io-hid отправляет запрос INIT с аргументами: дескриптор модуля драйвера (ioh_dll), дескриптор структуры диспетчера (dpp), дескриптор структуры вызовов (ioh) и переданные аргументы (options). Следует инициализировать глобальные указатели для первых трех аргументов, т.к. их использование пригодится в дальнешей реализации драйвера. Так же необходимо сделать:
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешную инициализацию драйвера, а код ошибки указывает на возможные проблемы при выполнении тела функции.
Полное описание прототипа функции и кодов возврата описано на странице io_hid_dll_entry_t :: init().
Пример:
static unsigned char smaple_rdesc[] = {0x05, 0x01,0x09, 0x01,0xa1, 0x01,0x09, 0x01,/* ... */0xc0,0xc0};typedef struct _sample_ctrl {/* Другие поля *//* ... */void *dll_hdl;io_hid_self_t *ioh;} sample_ctrl_t;typedef struct _sample {/* Другие поля дескриптора отчета *//* ... */int hid_hdl;} sample_t;sample_ctrl_t SAMPLECtrl;int sample_get_report_descriptor (sample_t* sample){/* Структуры и переменные, которые необходимы для регистрации устройства в io-hid */hidd_device_ident_t device_ident;io_hid_registrant_t dev;int sample_hdl;int result = EOK;/* Получение информации для заполения hidd_device_ident_t *//* ... */device_ident.vendor_id = /* ... */ ;device_ident.product_id = /* ... */ ;device_ident.version = /* ... */ ;dev.flags = 0;/* Инициализация информации об устройстве */dev.device_ident = &device_ident;/* Инициализация строкового дескриптора */dev.desc = sample_rdesc;dev.dlen = sizeof (sample_rdesc);/* Инициализация дескриптора отчета */dev.user_hdl = sample;/* Инициализация дескриптора интерфейсa драйвера */dev.funcs = &sample_funcs;/* Регистрация дескриптора модуля драйвера в io-hid */if ( (result = (*SAMPLECtrl.ioh->reg) (SAMPLECtrl.dll_hdl, &dev, &sample_hdl)) == EOK )sample->hid_hdl = sample_hdl;return (result);}char *sample_opts[] = {/* Поддерживаемые опции */,NULL};int sample_start_driver (char *options){int status = EOK;while (options && *options != '\0') {switch (getsubopt (&options, sample_opts, &value)) {/* Обработка опций *//* ... */}}return (status);}void *sample_event_handler_polling (sample_t* sample){/* Обработка прерываний / приема данных *//* ... */if ( (*SAMPLECtrl.ioh->get_buffer)(sample->hid_hdl, &sample->rbuffer) != EOK ) {}/* Отправление отчета с обработанными данными */(*SAMPLECtrl.ioh->send_report) (sample->hid_hdl, (_uint8 *) sample->rbuffer, (_uint32) packet_len);}int sample_init (void *dll_hdl, dispatch_t *dpp, io_hid_self_t *ioh, char *options){/* Инициализация дескрипторов */SAMPLECtrl.dll_hdl = dll_hdl;SAMPLECtrl.ioh = ioh;sample_t sample;if ( sample_start_driver (options) != EOK ) {fprintf (stderr, "error starting SAMPLE HID class driver\n");return (EXIT_FAILURE);}sample_get_report_descriptor (&sample_t);/* Инициализация потока обработки прерываний / приема данных */if ( pthread_create( NULL, NULL, (void *)sample_event_handler_polling, &sample ) != EOK ) {fprintf (stderr, "error starting SAMPLE HID class driver\n");return (EXIT_FAILURE);}return (EOK);}
Вызывается при завершении работы драйвера. Следует осовободить выделенные в io_hid_dll_entry_t :: init() ресурсы. Принимает в качестве аргумента дескриптор модуля драйвера (dll_hdl).
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешную остановку работы драйвера, а код ошибки указывает на возможные проблемы при выполнении тела функции.
Полное описание прототипа функции и кодов возврата описано на странице io_hid_dll_entry_t :: shutdown().
Пример:
int sample_stop (void *dll_hdl){/* Освобождение ресурсов *//* ... */return (EOK);}
Дескриптор интерфейса драйвера представляет собой набор функций-обработчиков, которые могут быть выполнены на устройстве по обращению клиента. Фактическая реализация этих функций будет зависеть от контекста и конкретных требований драйвера или HID-библиотеки, с которыми вы работаете.
Полное описание прототипа интерфейса описано на странице io_hid_registrant_funcs_t.
Пример:
#include <sys/io-hid.h>io_hid_registrant_funcs_t sample_funcs = {_IO_HID_REG_NFUNCS,sample_client_attach,sample_client_detach,sample_rbuffer_alloc,sample_rbuffer_free,sample_report_read,sample_report_write,sample_get_idle,sample_set_idle,sample_get_protocol,sample_set_protocol,sample_get_string,sample_get_indexed_string,sample_reset,};
Вызывается однократно при загрузке клиента драйвера. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl) и дескриптор отчета (user). В данной функции следует описать проверку успешной регистрации модуля драйвера вызовом get_buffer
в io_hid_self_t и при необходимости выполнить действия для проверки активности устройства.
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное подключение устройства, а код ошибки указывает на возможные проблемы при выполнении тела функции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: client_attach().
Пример:
int sample_check_device (void){/* ... */}int sample_client_attach (int reg_hdl, void *user){sample_t *sample = user;int status;/* ... *//* Проверка, что инициализация дескриптора модуля драйвера была успешно завершена */if ( (*SAMPLECtrl.ioh->get_buffer) (sample->hid_hdl, &sample->rbuffer) != EOK ) {}/* ... *//* Девайс специфичные дополнения для проверки, что устройство подключено */if ( sample_check_device() != EOK ) {return (EXIT_FAILURE);}return (EOK);}
Вызывается при завершении работы клиента драйвера. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl) и дескриптор отчета (user). Следует освободить выделенные ресурсы.
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное отключение клиента от модуля драйвера, а код ошибки указывает на возможные проблемы при выполнении тела функции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: client_detach().
Пример:
int sample_client_detach (int reg_hdl, void *user){sample_t *sample = user;/* Освобождение ресурсов *//* ... */return (EOK);}
Выделяет память под буфер отчета указанного размера и возвращает указатель на него. Принимает в качестве аргументов: размер (size) и указатель буфера отчета (bptr).
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное выделение памяти, а код ошибки указывает на возможные проблемы при выполнении операции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: rbuffer_alloc().
Пример:
int sample_rbuffer_alloc (_uint16 size, void **bptr){if ( (*bptr = malloc( size )) == NULL )return (ENOMEM);memset ( *bptr, 0xff, size );return (EOK);}
Освобождает выделенную пямять под буфер отчета. Принимает в качестве аргумента указатель на буфера отчета (bptr).
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное освобождение памяти, а код ошибки указывает на возможные проблемы при выполнении операции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: rbuffer_free().
Пример:
int sample_rbuffer_free (void *bptr){free (bptr);return (EOK);}
Позволяет получить клиенту отчет с устройства. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl), дескриптор отчета (user), идентификатор отчета (rid), тип отчета (rtype), указатель на дескриптор отчета (rdata) и размер отчета (rlen). Функция предоставляет интерфейс для чтения отчетов с устройства, поддерживающего HID протокол. Она принимает идентификатор зарегистрированный дескриптор модуля драйвера, дескриптор отчета, идентификатор и тип отчета, а также буфер и его размер для хранения прочитанных данных.
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное чтение, а код ошибки указывает на возможные проблемы при выполнении операции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: report_read().
Пример:
int sample_report_read (int reg_hdl, void *user, _uint8 rid, _uint16 rtype, void *rdata, _uint16 rlen){/* Получение отчета в соответствии с идентификатором и типом *//* ... */return (EOK);}
Позволяет отправить клиенту отчет на устройство, устанавливая состояние элементов управления вводом, выводом или функциями. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl), дескриптор отчета (user), идентификатор отчета (rid), тип отчета (rtype), указатель на дескриптор отчета (rdata) и размер отчета (rlen). Необходимо реализовать обработку отчетов.
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешную отправку отчета, а код ошибки указывает на возможные проблемы при выполнении операции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: report_write().
Пример:
int sample_report_write (int reg_hdl, void *user, _uint8 rid, _uint16 rtype, void *rdata, _uint16 rlen){sample_t *sample = user;sample_data_packet_t *data = (sample_data_packet_t *) rdata;switch (data->cmd) {/* Обработка полученного отчета *//* ... */default:fprintf (stderr, "Received an unknown command from somewhere\n");break;}return (EOK);}
Считывает текущую частоту простоя для конкретного входного отчета. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl), дескриптор отчета (user), идентификатор отчета (rid) и частоту опроса (idle_rate). Необходимо присвоить соответствующую частоту простоя входному аргументу idle_rate для rid отчета. Установить @ idle_rate равной 0, если не используется PROTOCOL_BOOT.
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное чтение, а код ошибки указывает на возможные проблемы при выполнении тела функции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: get_idle().
Пример:
int sample_get_idle (int reg_hdl, void *user, _uint8 rid, _uint16 *idle_rate){/* Обработка идетификатора отчета и установка соответствующей частоты опроса *//* ... */return (EOK);}
Задает частоту опроса отчета с идетификатором rid. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl), дескриптор отчета (user), идентификатор отчета (rid) и частоту опроса (idle_rate). Используется для ограничения передачи отчетов клиенту.
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное выполнение, а код ошибки указывает на возможные проблемы при выполнении тела функции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: set_idle().
Пример:
int sample_set_idle (int reg_hdl, void *user, _uint8 rid, _uint16 idle_rate){/* Обработка идетификатора отчета и получение соответствующей частоты опроса *//* ... */return (EOK);}
Возвращает поддерживаемый драйвером протокол. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl), дескриптор отчета (user) и идентификатор протокола (protocol).
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное выполнение, а код ошибки указывает на возможные проблемы при выполнении тела функции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: get_protocol().
Пример:
int sample_get_protocol (int reg_hdl, void *user, _uint8 *protocol){/* Присвоить идентификатор протокола */protocol = SAMPLECtrl.protocol;return (EOK);}
Устанавливает протокол используемый клиентом. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl), дескриптор отчета (user) и идентификатор протокола (protocol).
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное выполнение, а код ошибки указывает на возможные проблемы при выполнении тела функции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: set_protocol().
Пример:
int sample_set_protocol (int reg_hdl, void *user, _uint8 protocol){/* Присвоить полученный идентификатор протокола */SAMPLECtrl.protocol = protocol;return (EOK);}
Получает строковый дескриптор от устройства. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl), дескриптор отчета (user), тип строкового дескриптора (request) и указатель на строковый дескрпитор (string). Входной аргумент request используется для запроса конкретного строкового дескриптора, а в string сохраняется результат: строковый дескриптор устройства.
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное выполнение, а код ошибки указывает на возможные проблемы при выполнении тела функции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: string().
Пример:
int sample_get_string (int reg_hdl, void *user, _uint16 request, void **string){sample_t *sample = user;sampled_device_descriptor_t *device_desc;/* Получение строкового дескриптора от устройства *//* ... *//* Обработка какой строковый дескриптор надо вернуть */switch (request) {case 1:*string = /* ... */ ;break;/* ... */default:return (ENOENT);}return (*string ? EOK : ENOENT);}
Устанавливает строковый дескриптор устройства. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl), дескриптор отчета (user), индекс строкового дескриптора (index) и указатель на строковый дескрпитор (string). Входной аргумент index указывает, какой строковый дескриптор требуется установить в string.
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное выполнение, а код ошибки указывает на возможные проблемы при выполнении тела функции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: indexed_string().
Пример:
int sample_get_indexed_string (int reg_hdl, void *user, _uint16 index, void **string){sample_t *sample = user;/* Получение строкового дескриптора устройства */*string = sampled_string (sample->device, index, 0);return (*string ? EOK : ENOENT);}
Используется для сброса состояния модуля или устройства. Принимает в качестве аргументов: зарегистрированный дескриптор модуля драйвера (reg_hdl) и дескриптор отчета (user). Драйвер может вызывать этот callback при любых неисправностях.
Результат вызова функции отображается возвращаемым значением, где EOK
указывает на успешное выполнение, а код ошибки указывает на возможные проблемы при выполнении тела функции.
![]() | Можно ограничиться возвратом кода EOK , не выполняя полную реализацию функции обработчика. |
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: reset().
Пример:
int sample_device_reset (sample_t *sample){int status = EOK;/* Выполнить сброс устройства и состояния драйвера *//* ... */return (status);}int sample_reset (int reg_hdl, void *user){sample_t *sample = user;return (sample_device_reset (sample));}
Реализация необязательна.
Полное описание прототипа функции и кодов возврата описано на странице io_hid_registrant_funcs_t :: reserved().
Предыдущий раздел: Библиотека разработки HID-драйверов