Kann ein Daemon-Prozess (dh ein Hintergrundprozess) nach Tastendrücken von einer USB-Tastatur suchen?

13

Ich arbeite an einem eingebetteten Linux-Projekt, in dem ich ein Programm entwickeln werde, das beim Booten automatisch ausgeführt wird und über eine Zeichenanzeige und eine Art Tastenfeld mit dem Benutzer interagiert. Wenn wir ein einfaches GPIO-Tastenfeld verwenden, kann ich problemlos ein Programm schreiben, das nach Tastendrücken auf diesen GPIO-Leitungen sucht. Einer unserer Gedanken war jedoch, stattdessen ein USB-Nummernblockgerät für Benutzereingaben zu verwenden. Nach meinem Verständnis werden sich diese Geräte dem Betriebssystem als USB-Tastatur präsentieren. Wenn Sie diesen Weg beschreiten, kann mein Programm unter Linux nach Eingaben auf dieser USB-Tastatur suchen. Beachten Sie dabei, dass es kein virtuelles Terminal oder keine VGA-Anzeige gibt. Wenn eine USB-Tastatur angeschlossen ist, gibt es eine Entität in '/ dev', für die ich einen Dateideskriptor öffnen kann?

KyleL
quelle

Antworten:

24

Geräte erhalten höchstwahrscheinlich eine Datei mit dem /dev/input/Namen, eventNin der N die verschiedenen Geräte wie Maus, Tastatur, Buchse, Ein- / Ausschalter usw. sind.

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

sollte dir einen Hinweis geben.

Siehe auch:

cat /proc/bus/input/devices

Wo SysfsWert ist Pfad unter /sys.

Sie können zB testen

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

Verwenden Sie zur Implementierung ioctl und überprüfen Sie Geräte + Monitor.

EDIT 2:

IN ORDNUNG. Ich erweitere diese Antwort basierend auf der Annahme/dev/input/eventN verwendet wird.

Ein Weg könnte sein:

  1. Bei der Startschleife werden alle eventDateien in gefunden /dev/input/. Verwenden Sie ioctl()diese Option , um Ereignisbits anzufordern:

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

    dann überprüfe ob EV_KEY-bit gesetzt ist.

  2. IFF gesetzt, dann nach Schlüsseln suchen:

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

    Wenn zB Zifferntasten interessant sind, dann prüfen Sie, ob Bits für KEY_0- KEY9und KEY_KP0bis vorhanden sind KEY_KP9.

  3. Gefundene IFF-Schlüssel starten dann die Überwachung der Ereignisdatei im Thread.

  4. Zurück zu 1.

Auf diese Weise sollten Sie alle Geräte überwachen können, die die gewünschten Kriterien erfüllen. Sie können nicht nur prüfen, ob EV_KEYzB beim Ein- / Ausschalten dieses Bit gesetzt ist, sondern offensichtlich auch nicht KEY_Ausw. gesetzt ist.

Ich habe False Positives für exotische Schlüssel gesehen, aber für normale Schlüssel sollte dies ausreichen. Es schadet nicht, wenn Sie zB eine Ereignisdatei für den Netzschalter oder eine Buchse überwachen, aber Sie geben keine fraglichen Ereignisse aus (auch bekannt als schlechter Code).

Mehr im Detail weiter unten.


EDIT 1:

In Bezug auf "Erkläre diese letzte Aussage ..." . Hier im Stackoverflow landen ... aber:

Ein schnelles und unsauberes Beispiel in C. Sie müssen verschiedene Codes implementieren, um zu überprüfen, ob Sie das richtige Gerät erhalten, den Ereignistyp, den Code und den Wert übersetzen. Typischerweise Tastendruck, Tastendruck, Tastenwiederholung, Tastencode usw.

Ich habe keine Zeit (und es ist zu viel hier), um den Rest hinzuzufügen.

Check out linux/input.h, Programme wie dumpkeys, Kernel-Code usw. für Mapping-Codes. Z.Bdumpkeys -l

Jedenfalls:

Ausführen als zB:

# ./testprog /dev/input/event2

Code:

#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 (Fortsetzung):

Beachten Sie, dass Sie beim Betrachten /proc/bus/input/deviceseinen Buchstaben am Anfang jeder Zeile haben. Hier Bbedeutet Bitmap. Das ist zum Beispiel:

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

Jedes dieser Bits entspricht einer Eigenschaft des Geräts. Mit Bitmap-Mitteln bedeutet 1, dass eine Eigenschaft vorhanden ist, wie in definiert 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

Schauen Sie sich /drivers/input/input.{h,c}im Kernel Source Tree um. Viel guter Code da. (ZB werden die Geräteeigenschaften von dieser Funktion erzeugt .)

Jede dieser Eigenschaftskarten kann von erhalten werden ioctl. Wenn Sie beispielsweise überprüfen möchten, welche LED-Eigenschaften verfügbar sind, gehen Sie wie folgt vor:

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

Schauen Sie sich die Definition von struct input_devin an, input.hum herauszufinden, wie sie ledbitdefiniert sind.

So überprüfen Sie den Status von LEDs:

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

Wenn Bit 1 in ledbit1 ist, leuchtet die Num-Taste. Wenn Bit 2 1 ist, leuchtet die Feststelltaste usw.

input.h hat die verschiedenen definiert.


Hinweise zur Ereignisüberwachung:

Pseudocode für die Überwachung könnte in die folgende Richtung weisen:

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

Einige verwandte Dokumente:

  1. Documentation/input/input.txt, esp. Abschnitt 5 beachten.
  2. Documentation/input/event-codes.txt, Beschreibung verschiedener Ereignisse etc. Beachten Sie, was unter zB EV_SYNüber erwähnt wirdSYN_DROPPED
  3. Documentation/input ... lesen Sie den Rest nach, wenn Sie möchten.
Runium
quelle
2

Sie können dies einfach durch Referenzieren tun /dev/input/by-id/usb-manufacturername_*serialnumber*. Diese erscheinen als symbolische Verknüpfungen, über die Sie readlink -edas zugehörige Blockgerät dereferenzieren können . Diese Links werden jedoch von erstelltudev denen möglicherweise nicht in Ihrer eingebetteten Umgebung vorhanden sind.

Oder .. Sehen Sie dmesgnach dem Anschließen des USB-Geräts. Es sollte Ihnen den /devKnoten geben.

Jeight
quelle
1
Die Einträge in /dev/disk/by-id/werden erstellt von udev- Frage ist, ob dies in diesem speziellen Fall verfügbar ist (eingebettete Plattform).
Peterph
@ Peterph: Sie sind richtig. Wenn Sie udev nicht verwenden, funktioniert der erste Vorschlag nicht.
Jeight
@ Gilles: Ich sehe, Sie haben die Antwort bearbeitet und den Pfad auf Eingabe anstatt auf Festplatte geändert. In diesem Fall wäre es meiner Meinung nach Input / By-Path und nicht Disk / By-ID. Ich vermute, dass beides funktionieren würde.
Jeight
1
Nein, by-idist richtig. Zum Beispiel ist meine USB-Tastatur als /dev/input/by-id/usb-_USB_Keyboard-event-kbdund verfügbar /dev/input/by-path/pci-0000:00:1d.2-usb-0:2:1.0-event-kbd.
Gilles 'SO- hör auf böse zu sein'
@Jeight: Wenn ich den richtigen Geräteknoten gefunden habe, weißt du, wie ich auf Tastendrücke zugreifen kann?
KyleL