Возможно ли, чтобы процесс демона (т.е. фоновый) искал нажатия клавиш с клавиатуры USB?

13

Я работаю над проектом по встроенному Linux, где буду разрабатывать программу, которая будет автоматически запускаться при загрузке и взаимодействовать с пользователем через отображение символов и некоторый массив кнопок. Если мы перейдем к простому массиву кнопок GPIO, я легко напишу программу, которая будет искать нажатия клавиш в этих строках GPIO. Однако одна из наших мыслей заключалась в том, чтобы вместо устройства USB использовать цифровую клавиатуру для ввода данных пользователем. Насколько я понимаю, эти устройства будут представлены ОС как клавиатура USB. Если пойти по этому пути, есть ли способ, чтобы моя программа могла искать ввод на этой клавиатуре USB изнутри Linux, имея в виду, что нет виртуального терминала или дисплея VGA. Когда подключена клавиатура USB, есть ли в «/ dev» объект, для которого я могу открыть дескриптор файла?

KyleL
источник

Ответы:

24

Устройства, скорее всего, получают файл с /dev/input/именем, eventNгде N - это различные устройства, такие как мышь, клавиатура, разъем, кнопки питания и т. Д.

ls -l  /dev/input/by-{path,id}/

должен дать вам подсказку.

Также посмотрите на:

cat /proc/bus/input/devices

Где Sysfsзначение путь под /sys.

Вы можете проверить, например,

cat /dev/input/event2 # if 2 is kbd.

Для реализации используйте ioctl и проверьте устройства + монитор.

РЕДАКТИРОВАТЬ 2:

OK. Я расширяю этот ответ, основываясь на предположении, /dev/input/eventNкоторое используется.

Одним из способов может быть:

  1. При запуске цикла все eventфайлы, найденные в /dev/input/. Используйте ioctl()для запроса битов события:

    ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
    

    затем проверьте, установлен ли EV_KEY-bit.

  2. IFF установлен, затем проверьте ключи:

    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), &keybit);
    

    Например, если цифровые клавиши интересны, то проверьте, есть ли биты для KEY_0- KEY9и KEY_KP0к KEY_KP9.

  3. Найдены ключи IFF, затем запускается файл событий мониторинга в потоке.

  4. Вернуться к 1.

Таким образом, вы должны отслеживать все устройства, которые соответствуют требуемым критериям. Вы не можете только проверить, EV_KEYнапример, для кнопки питания будет установлен этот бит, но, очевидно, он не будет установлен KEY_Aи т.д.

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

Подробнее ниже.


РЕДАКТИРОВАТЬ 1:

В отношении «Объясните это последнее утверждение…» . Перебираемся в стекаповорот земли здесь ... но:

Быстрый и грязный пример на C. Вам нужно будет реализовать различный код, чтобы проверить, действительно ли вы получили правильное устройство, перевести тип события, код и значение. Как правило, нажатие клавиши, нажатие клавиши, повторение клавиши, код клавиши и т. Д.

Не было времени (и здесь слишком много), чтобы добавить остальное.

Проверьте linux/input.h, как программы dumpkeys, код ядра и т. Д. Для отображения кодов. Напримерdumpkeys -l

Так или иначе:

Запустите как например:

# ./testprog /dev/input/event2

Код:

#include <stdio.h>

#include <string.h>     /* strerror() */
#include <errno.h>      /* errno */

#include <fcntl.h>      /* open() */
#include <unistd.h>     /* close() */
#include <sys/ioctl.h>  /* ioctl() */

#include <linux/input.h>    /* EVIOCGVERSION ++ */

#define EV_BUF_SIZE 16

int main(int argc, char *argv[])
{
    int fd, sz;
    unsigned i;

    /* A few examples of information to gather */
    unsigned version;
    unsigned short id[4];                   /* or use struct input_id */
    char name[256] = "N/A";

    struct input_event ev[EV_BUF_SIZE]; /* Read up to N events ata time */

    if (argc < 2) {
        fprintf(stderr,
            "Usage: %s /dev/input/eventN\n"
            "Where X = input device number\n",
            argv[0]
        );
        return EINVAL;
    }

    if ((fd = open(argv[1], O_RDONLY)) < 0) {
        fprintf(stderr,
            "ERR %d:\n"
            "Unable to open `%s'\n"
            "%s\n",
            errno, argv[1], strerror(errno)
        );
    }
    /* Error check here as well. */
    ioctl(fd, EVIOCGVERSION, &version);
    ioctl(fd, EVIOCGID, id); 
    ioctl(fd, EVIOCGNAME(sizeof(name)), name);

    fprintf(stderr,
        "Name      : %s\n"
        "Version   : %d.%d.%d\n"
        "ID        : Bus=%04x Vendor=%04x Product=%04x Version=%04x\n"
        "----------\n"
        ,
        name,

        version >> 16,
        (version >> 8) & 0xff,
        version & 0xff,

        id[ID_BUS],
        id[ID_VENDOR],
        id[ID_PRODUCT],
        id[ID_VERSION]
    );

    /* Loop. Read event file and parse result. */
    for (;;) {
        sz = read(fd, ev, sizeof(struct input_event) * EV_BUF_SIZE);

        if (sz < (int) sizeof(struct input_event)) {
            fprintf(stderr,
                "ERR %d:\n"
                "Reading of `%s' failed\n"
                "%s\n",
                errno, argv[1], strerror(errno)
            );
            goto fine;
        }

        /* Implement code to translate type, code and value */
        for (i = 0; i < sz / sizeof(struct input_event); ++i) {
            fprintf(stderr,
                "%ld.%06ld: "
                "type=%02x "
                "code=%02x "
                "value=%02x\n",
                ev[i].time.tv_sec,
                ev[i].time.tv_usec,
                ev[i].type,
                ev[i].code,
                ev[i].value
            );
        }
    }

fine:
    close(fd);

    return errno;
}

РЕДАКТИРОВАТЬ 2 (продолжение):

Обратите внимание, что если вы посмотрите, у /proc/bus/input/devicesвас есть буква в начале каждой строки. Здесь Bозначает бит-карту. Вот например:

B: PROP=0
B: EV=120013
B: KEY=20000 200 20 0 0 0 0 500f 2100002 3803078 f900d401 feffffdf ffefffff ffffffff fffffffe
B: MSC=10
B: LED=7

Каждый из этих битов соответствует свойству устройства. Что с помощью битовой карты означает, что 1 указывает на наличие свойства, как определено в linux/input.h. :

B: PROP=0    => 0000 0000
B: EV=120013 => 0001 0010 0000 0000 0001 0011 (Event types sup. in this device.)
                   |   |               |   ||
                   |   |               |   |+-- EV_SYN (0x00)
                   |   |               |   +--- EV_KEY (0x01)
                   |   |               +------- EV_MSC (0x04)
                   |   +----------------------- EV_LED (0x11)
                   +--------------------------- EV_REP (0x14)
B: KEY=20... => OK, I'm not writing out this one as  it is a bit huge.

B: MSC=10    => 0001 0000
                   |
                   +------- MSC_SCAN
B: LED=7     => 0000 0111 , indicates what LED's are present
                      |||
                      ||+-- LED_NUML
                      |+--- LED_CAPSL
                      +---- LED_SCROLL

Взгляните на /drivers/input/input.{h,c}дерево исходных текстов ядра. Там много хорошего кода. (Например, свойства устройств создаются этой функцией .)

Каждая из этих карт свойств может быть достигнута ioctl. Например, если вы хотите проверить, какие свойства светодиодов доступны, скажите:

ioctl(fd, EVIOCGBIT(EV_LED, sizeof(ledbit)), &ledbit);

Посмотрите на определение struct input_devв input.hтом, ledbitкак определяются.

Чтобы проверить состояние светодиодов, скажите:

ioctl(fd, EVIOCGLED(sizeof(ledbit)), &ledbit);

Если бит 1 in ledbitравен 1, тогда num-lock светятся. Если бит 2 равен 1, загорается заглушка и т. Д.

input.h имеет различные определения.


Примечания, когда дело доходит до мониторинга событий:

Псевдокод для мониторинга может быть чем-то в направлении:

WHILE TRUE
    READ input_event
    IF event->type == EV_SYN THEN
        IF event->code == SYN_DROPPED THEN
            Discard all events including next EV_SYN
        ELSE
            This marks EOF current event.
        FI
    ELSE IF event->type == EV_KEY THEN
        SWITCH ev->value
            CASE 0: Key Release    (act accordingly)
            CASE 1: Key Press      (act accordingly)
            CASE 2: Key Autorepeat (act accordingly)
        END SWITCH
    FI
END WHILE

Некоторые связанные документы:

  1. Documentation/input/input.txtособенно примечание раздел 5.
  2. Documentation/input/event-codes.txt, описание различных событий и т. д. Обратите внимание на то, что упоминается, например, EV_SYNоSYN_DROPPED
  3. Documentation/input ... прочитайте об остальном, если хотите.
Runium
источник
2

Вы можете сделать это легко, ссылаясь /dev/input/by-id/usb-manufacturername_*serialnumber*. Они отображаются в виде символических ссылок, которые можно разыменовать, используя readlink -eдля определения связанного блочного устройства. Однако эти ссылки создаются тем, udevчто может отсутствовать в вашей встроенной среде.

Или .. Посмотрите на dmesgпосле подключения USB-устройства. Это должно дать вам /devузел.

Jeight
источник
1
Записи в /dev/disk/by-id/imho созданы udev- вопрос в том, доступно ли это в данном случае (встроенная платформа).
Петер
@peterph: Вы правы. Если не использовать udev, первое предложение не будет работать.
Джайт
@ Жиль: я вижу, что вы отредактировали ответ и изменили путь к вводу, а не к диску. В этом случае я считаю, что это будет ввод / путь, а не диск / идентификатор. Я подозреваю, что либо будет работать.
Джайт
1
Нет, by-idэто правильно. Например моя клавиатура USB доступна как /dev/input/by-id/usb-_USB_Keyboard-event-kbdи /dev/input/by-path/pci-0000:00:1d.2-usb-0:2:1.0-event-kbd.
Жиль "ТАК - перестань быть злым"
@Jeight: Как только я найду правильный узел устройства, вы знаете, как я могу получить доступ к нажатию клавиш?
KyleL