Czy proces demona (tj. Tła) może wyszukiwać naciśnięcia klawiszy z klawiatury USB?


13

Pracuję nad osadzonym projektem Linuksa, w którym będę opracowywać program, który będzie uruchamiany automatycznie podczas uruchamiania i będzie interakcją z użytkownikiem za pomocą wyświetlacza znaków i pewnego rodzaju tablicy przycisków. Jeśli zastosujemy prostą tablicę przycisków GPIO, mogę łatwo napisać program, który będzie szukał naciśnięć klawiszy na tych liniach GPIO. Jednak jedną z naszych myśli było użycie urządzenia numerycznego USB zamiast wprowadzania danych przez użytkownika. Rozumiem, że urządzenia te zaprezentują się w systemie operacyjnym jako klawiatura USB. Jeśli pójdę tą ścieżką, czy jest jakiś sposób, aby mój program szukał danych wejściowych na tej klawiaturze USB z Linuksa, pamiętając, że nie ma wirtualnego terminalu ani wyświetlacza VGA. Czy po podłączeniu klawiatury USB istnieje element w „/ dev”, który wydaje się, że mogę otworzyć deskryptor pliku?

Odpowiedzi:


24

Urządzenia najprawdopodobniej otrzymują plik o /dev/input/nazwie, eventNgdzie N oznacza różne urządzenia, takie jak mysz, klawiatura, gniazdo, przyciski zasilania itp.

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

powinien dać ci wskazówkę.

Zobacz także:

cat /proc/bus/input/devices

Gdzie Sysfswartość jest ścieżką poniżej /sys.

Możesz przetestować np

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

Aby wdrożyć, użyj ioctl i sprawdź urządzenia + monitor.

EDYCJA 2:

DOBRZE. Rozwijam tę odpowiedź w oparciu o przyjęte założenie /dev/input/eventN.

Jednym ze sposobów może być:

  1. Podczas uruchamiania pętli wszystkie eventpliki znalezione w /dev/input/. Służy ioctl()do żądania bitów zdarzeń:

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

    następnie sprawdź, czy EV_KEYustawiono opcję -bit.

  2. Ustawiony IFF, a następnie sprawdź klucze:

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

    Np. Jeśli klawisze numeryczne są interesujące, sprawdź, czy bity dla KEY_0- KEY9i KEY_KP0do KEY_KP9.

  3. Znaleziono klucze IFF, a następnie rozpocznij monitorowanie pliku zdarzeń w wątku.

  4. Powrót do 1.

W ten sposób powinieneś monitorować wszystkie urządzenia spełniające wybrane kryteria. Nie można tylko sprawdzić, czy EV_KEYnp. Przycisk zasilania będzie miał ustawiony ten bit, ale oczywiście nie będzie miał KEY_Austawionego itd.

Widziałem fałszywe alarmy dla kluczy egzotycznych, ale dla zwykłych kluczy to powinno wystarczyć. Monitorowanie nie ma bezpośredniej szkody, np. Plik zdarzenia dla przycisku zasilania lub gniazda, ale nie będą one emitować danych zdarzeń (np. Zły kod).

Więcej szczegółów poniżej.


EDYCJA 1:

W odniesieniu do „Wyjaśnij to ostatnie zdanie…” . Przechodzenie w stosie przepływu ląduje tutaj… ale:

Szybka i brudna próbka w C. Musisz zaimplementować różne kody, aby sprawdzić, czy faktycznie otrzymujesz prawidłowe urządzenie, przetłumacz typ zdarzenia, kod i wartość. Zwykle klawiszowanie, klawiszowanie, powtarzanie klawiszy, kod klucza itp.

Nie mam czasu (i jest tu za dużo), aby dodać resztę.

Sprawdź linux/input.h, programy takie jak dumpkeys, kod jądra itp. Do mapowania kodów. Na przykładdumpkeys -l

W każdym razie:

Uruchom jako np .:

# ./testprog /dev/input/event2

Kod:

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

EDYCJA 2 (ciąg dalszy):

Zauważ, że jeśli na to spojrzysz /proc/bus/input/devices, masz literę na początku każdej linii. Tutaj Boznacza bit-mapy. To jest na przykład:

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

Każdy z tych bitów odpowiada właściwości urządzenia. Co za pomocą mapy bitowej oznacza 1, że właściwość jest obecna, jak zdefiniowano w 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

Spójrz na /drivers/input/input.{h,c}drzewo źródeł jądra. Dużo dobrego kodu. (Np. Właściwości urządzenia są wytwarzane przez tę funkcję .)

Każda z tych map właściwości może być osiągnięta przez ioctl. Na przykład, jeśli chcesz sprawdzić, jakie właściwości LED są dostępne, powiedz:

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

Spójrz na definicję struct input_devw, input.hjak ledbitsą zdefiniowane.

Aby sprawdzić status diod LED, powiedz:

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

Jeśli bit 1 ledbitjest równy 1, wówczas świeci num-lock. Jeśli bit 2 ma wartość 1, oznacza to, że klawisz Caps Lock świeci itp.

input.h ma różne definicje.


Uwagi dotyczące monitorowania zdarzeń:

Pseudo-kod do monitorowania może być czymś w kierunku:

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

Niektóre powiązane dokumenty:

  1. Documentation/input/input.txt, esp. uwaga sekcja 5.
  2. Documentation/input/event-codes.txt, opis różnych wydarzeń itp. Zwróć uwagę na to, co wspomniano np. EV_SYNoSYN_DROPPED
  3. Documentation/input ... czytaj dalej, jeśli chcesz.

2

Możesz to zrobić łatwo, odwołując się /dev/input/by-id/usb-manufacturername_*serialnumber*. Pojawiają się one jako dowiązania symboliczne, których można użyć readlink -edo określenia powiązanego urządzenia blokowego. Te linki są jednak tworzone przez, udevktóre mogą nie być obecne w środowisku osadzonym.

Lub .. Spójrz na dmesgpo podłączeniu urządzenia USB. Powinien dać ci /devwęzeł.


1
Wpisy /dev/disk/by-id/są imho tworzone przez udev- pytanie brzmi, czy jest to dostępne w tym konkretnym przypadku (platforma wbudowana).
peterph

@peterph: Masz rację. Jeśli nie użyjesz udev, pierwsza sugestia nie zadziała.
Jeight,

@Gilles: Widzę, że zredagowałeś odpowiedź i zmieniłeś ścieżkę na dane wejściowe zamiast na dysk. W takim przypadku uważam, że będzie to wejście / ścieżka, a nie dysk / identyfikator. Podejrzewam, że albo by zadziałało.
Jeight

1
Nie, by-idjest poprawne. Na przykład moja klawiatura USB jest dostępna jako /dev/input/by-id/usb-_USB_Keyboard-event-kbdi /dev/input/by-path/pci-0000:00:1d.2-usb-0:2:1.0-event-kbd.
Gilles „SO- przestań być zły”

@Jeight: Kiedy znajdę właściwy węzeł urządzenia, czy wiesz, jak mogę uzyskać dostęp do naciśnięć klawiszy?
KyleL
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.