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

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

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

решение1

Устройства, скорее всего, получают файл с /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:

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

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

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

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

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

  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_devin input.h, чтобы узнать, как ledbitоно определяется.

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

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

Если бит 1 ledbitравен 1, то горит Num-Lock. Если бит 2 равен 1, то горит Caps Lock и т. д.

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...прочитайте остальное, если хотите.

решение2

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

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

Связанный контент