Драйверы входных событий

Предыдущая  Содержание  Следующая V*D*V

Интерфейс событий, экспортируемый подсистемой ввода, эволюционировал в стандарт, который понимают многие графические оконные системы. Для общения с устройствами ввода драйверы событий предлагают аппаратно-независимую абстракцию, так же как интерфейс кадрового буфера (этот вопрос рассматривается в Главе 12, "Драйверы Видео") представляет собой общий механизм для взаимодействия с устройствами отображения. Драйверы событий в тандеме с драйверами кадрового буфера изолируют графические пользовательские интерфейсы (GUI) от капризов базового оборудования.

Интерфейс Evdev

Evdev - это универсальный драйвер событий ввода. Каждый пакет события, создаваемый evdev, имеет следующий формат, определённый в include/linux/input.h:

 

struct input_event {

    struct timeval time; /* Метка времени */

    __u16 type;  /* Тип события */

    __u16 code;  /* Код события */

    __s32 value; /* Значение события */

};

 

Чтобы узнать, как использовать evdev, давайте реализуем драйвер устройства ввода для виртуальной мыши.

Пример устройства: Виртуальная мышь

Вот как работает наша виртуальная мышь: приложение (coord.c) эмулирует движения мыши и отправляет информацию о координатах в драйвер виртуальной мыши (vms.c) через узел в sysfs, /sys/devices/platform/vms/coordinates. Драйвер виртуальной мыши (для краткости, драйвер vms) отправляет эти движения наверх через evdev. Детали показаны на Рисунке 7.2.

 

Рисунок 7.2. Драйвер ввода для виртуальной мыши.

Рисунок 7.2. Драйвер ввода для виртуальной мыши.

 

Мышь общего назначения (General-purpose mouse, gpm) представляет собой сервер, который позволяет использовать мышь в текстовом режиме без помощи со стороны Х сервера. GPM понимает события evdev, так что драйвер vms может взаимодействовать с ним непосредственно. После того, как вы соберёте всё вместе, вы сможете увидеть курсор, танцующий на вашем экране, что соответствует движениям виртуальной мыши, передаваемым coord.c.

 

Распечатка 7.1 содержит программу coord.c, которая постоянно генерирует случайные координаты X и Y. Мышь, в отличие от джойстиков или сенсорных панелей, передаёт относительные координаты, и это то, что делает coord.c. Драйвер vms показан в Распечатке 7.2.

 

Распечатка 7.1. Приложение, эмулирующее движения мыши (coord.c)

 

Код:

#include <fcntl.h>

 

int

main(int argc, char *argv[])

{

    int sim_fd;

    int x, y;

    char buffer[10];

 

    /* Открываем узел с координатами в sysfs */

    sim_fd = open("/sys/devices/platform/vms/coordinates", O_RDWR);

    if (sim_fd < 0) {

        perror("Couldn't open vms coordinate file\n");

        exit(-1);

    }

    while (1) {

        /* Генерируем случайные относительные координаты */

        x = random()%20;

        y = random()%20;

        if (x%2) x = -x; if (y%2) y = -y;

 

        /* Отправляем съэмулированные координаты драйверу виртуальной мыши */

        sprintf(buffer, "%d %d %d", x, y, 0);

        write(sim_fd, buffer, strlen(buffer));

        fsync(sim_fd);

        sleep(1);

    }

 

    close(sim_fd);

}

 

Распечатка 7.2. Драйвер ввода для виртуальной мыши (vms.c)

 

Код:

#include <linux/fs.h>

#include <asm/uaccess.h>

#include <linux/pci.h>

#include <linux/input.h>

#include <linux/platform_device.h>

 

struct input_dev *vms_input_dev;        /* Представление устройства ввода */

static struct platform_device *vms_dev; /* Структура устройства */

 

/* Метод для ввода из sysfs съэмулированных координат в драйвер виртуальной мыши */

static ssize_t

write_vms(struct device *dev,

          struct device_attribute *attr,

          const char *buffer, size_t count)

{

    int x,y;

    sscanf(buffer, "%d%d", &x, &y);

 

    /* Сообщаем относительные координаты через интерфейс событий */

    input_report_rel(vms_input_dev, REL_X, x);

    input_report_rel(vms_input_dev, REL_Y, y);

    input_sync(vms_input_dev);

    return count;

}

 

/* Подключаем метод записи sysfs */

DEVICE_ATTR(coordinates, 0644, NULL, write_vms);

 

/* Дескриптор атрибутов */

static struct attribute *vms_attrs[] = {

    &dev_attr_coordinates.attr,

    NULL

};

 

/* Группа атрибутов */

static struct attribute_group vms_attr_group = {

    .attrs = vms_attrs,

};

 

/* Инициализация драйвера */

int __init

vms_init(void)

{

 

    /* Регистрируем устройство платформы */

    vms_dev = platform_device_register_simple("vms", -1, NULL, 0);

    if (IS_ERR(vms_dev)) {

        printk("vms_init: error\n");

        return PTR_ERR(vms_dev);

    }

 

    /* Создаём в sysfs узел для чтения съэмулированных координат */

    sysfs_create_group(&vms_dev->dev.kobj, &vms_attr_group);

 

    /* Создаём структуру данных устройства ввода */

    vms_input_dev = input_allocate_device();

    if (!vms_input_dev) {

        printk("Bad input_allocate_device()\n");

        return -ENOMEM;

    }

 

    /* Сообщаем, что эта виртуальная мышь будет генерировать относительные координаты */

    set_bit(EV_REL, vms_input_dev->evbit);

    set_bit(REL_X, vms_input_dev->relbit);

    set_bit(REL_Y, vms_input_dev->relbit);

 

    /* Регистрируемся в подсистеме ввода */

    input_register_device(vms_input_dev);

 

    printk("Virtual Mouse Driver Initialized.\n");

    return 0;

}

 

/* Выход из драйвера */

void

vms_cleanup(void)

{

 

    /* Отменяем регистрацию в подсистеме ввода */

    input_unregister_device(vms_input_dev);

 

    /* Освобождаем узел в sysfs */

    sysfs_remove_group(&vms_dev->dev.kobj, &vms_attr_group);

 

    /* Отменяем регистрацию драйвера */

    platform_device_unregister(vms_dev);

 

    return;

}

 

module_init(vms_init);

module_exit(vms_cleanup);

 

Давайте внимательнее посмотрим на Распечатку 7.2. Во время инициализации драйвер vms регистрирует себя в качестве драйвера устройства ввода. Для этого он сначала выделяет память для структуры input_dev с использованием API ядра, input_allocate_device():

 

vms_input_dev = input_allocate_device();

 

Затем он объявляет, что виртуальная мышь генерирует события с относительными координатами:

 

set_bit(EV_REL, vms_input_dev->evbit); /* Типом события является EV_REL */

 

Далее он декларирует коды событий, которые создаёт виртуальная мышь:

 

set_bit(REL_X, vms_input_dev->relbit); /* Относительное движение по 'X' */

set_bit(REL_Y, vms_input_dev->relbit); /* Относительное движение по 'Y' */

 

Если ваша виртуальная мышь также способна генерировать нажатия на кнопки мыши, вы должны добавить к этому vms_init():

 

set_bit(EV_KEY, vms_input_dev->evbit); /* Типом события является EV_KEY */

set_bit(BTN_0, vms_input_dev->keybit); /* Кодом события является BTN_0 */

 

И, наконец, регистрация:

 

input_register_device(vms_input_dev);

 

write_vms() является методом sysfs store(), который связан с /sys/devices/platform/vms/coordinates. Когда coord.c пишет в этот файл пару X/Y, write_vms() выполняет следующие действия:

 

input_report_rel(vms_input_dev, REL_X, x);

input_report_rel(vms_input_dev, REL_Y, y);

input_sync(vms_input_dev);

 

Первый оператор генерирует событие REL_X, или относительное движение устройства по оси X. Второй создаёт событие REL_Y, или относительное движение по оси Y. input_sync() показывает, что это событие является полным, так что подсистема ввода собирает эти два события в один пакет evdev и отправляет его за дверь через /dev/input/eventX, где X представляет собой номер интерфейса, присвоенный драйверу vms. Приложение, читающее этот файл, получит пакеты событий в описанном ранее формате input_event.  Чтобы попросить gpm подключиться к этому интерфейсу событий и, соответственно, погонять курсор по экрану, сделайте следующее:

 

bash> gpm -m /dev/input/eventX -t evdev

 

Драйвер контроллера сенсорного экрана ADS7846 и драйвер акселерометра, обсуждаемые позднее соответственно в разделах "Сенсорные контроллеры" и "Акселерометры", также являются пользователями evdev.

Дополнительная информация об интерфейсе событий

Драйвер vms использует общий интерфейс событий evdev, но устройства ввода, такие как клавиатуры, мыши и контроллеры касаний, имеют специальные драйверы событий. Мы рассмотрим их, когда будем обсуждать соответствующие драйверы устройств.

 

Чтобы написать свой собственный драйвер событий и экспортировать его в пользовательское пространство с помощью /dev/input/mydev, вы должны заполнить структуру, называемую input_handler, и зарегистрировать её в ядре ввода следующим образом:

 

Код:

static struct input_handler my_event_handler = {

    .event = mydev_event,   /* Обработка сообщений о событиях, посылаемых драйверами устройств ввода,

                               которые пользуются услугами этого драйвера событий */

    .fops = &mydev_fops,    /* Методы для управления /dev/input/mydev */

    .minor = MYDEV_MINOR_BASE, /* Младший номер /dev/input/mydev */

    .name = "mydev",        /* Имя драйвера событий */

    .id_table = mydev_ids,  /* Этот драйвер событий может обрабатывать запросы с такими ID-ами */

    .connect = mydev_connect, /* Вызывается, если есть совпадение ID */

    .disconnect = mydev_disconnect, /* Вызывается для отмены регистрации драйвера */

};

 

/* Инициализация драйвера */

static int __init

mydev_init(void)

{

    /* ... */

 

    input_register_handler(&my_event_handler);

 

    /* ... */

    return 0;

}

 

Для полноценного примера посмотрите на реализацию mousedev (drivers/input/mousedev.c) .

 

Предыдущая  Содержание  Следующая