Чи можливий процес демон (тобто фон) шукати натискання клавіш на клавіатурі USB?


13

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

Відповіді:


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 і перевіряйте пристрої + монітор.

EDIT 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тощо.

Побачили помилкові позитиви екзотичних ключів, але для звичайних ключів цього має бути достатньо. Немає прямої шкоди під час моніторингу, наприклад, файл події для кнопки живлення або гнізда, але ви не будете випромінювати події, про які йдеться (ака. Поганий код).

Більш докладно нижче.


EDIT 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;
}

EDIT 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 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 ... читайте на іншому, якщо хочете.

2

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

Або .. Подивіться dmesgпісля підключення USB-пристрою. Це має дати вам /devвузол.


1
Записи в /dev/disk/by-id/них створюються шляхом udev- питання полягає в тому, чи доступно це в цьому частковому випадку (вбудована платформа).
петерф

@peterph: Ви маєте рацію. Якщо не використовувати udev, перша пропозиція не працюватиме.
Jeight

@Gilles: Я бачу, ви відредагували відповідь і змінили шлях до введення, а не диск. У цьому випадку я вважаю, що це буде введення / по шляху, а не диск / за ідентифікатором. Я підозрюю, що будь-яка працювала.
Jeight

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.