È possibile per un processo daemon (cioè in background) cercare la pressione dei tasti da una tastiera USB?


13

Sto lavorando a un progetto Linux incorporato in cui svilupperò un programma che verrà eseguito automaticamente all'avvio e interagirà con l'utente tramite un display a caratteri e una sorta di array di pulsanti. Se andiamo con un semplice array di pulsanti GPIO, posso facilmente scrivere un programma che cercherà di premere i tasti su quelle linee GPIO. Tuttavia, uno dei nostri pensieri è stato quello di utilizzare un dispositivo tastierino numerico USB invece per l'input dell'utente. La mia comprensione è che tali dispositivi si presenteranno al sistema operativo come una tastiera USB. Se seguo questo percorso, c'è un modo per il mio programma di cercare input su questa tastiera USB da Linux, tenendo presente che non ci sono terminali virtuali o display VGA. Quando è collegata una tastiera USB, c'è un'entità in '/ dev' che sembra poter aprire un descrittore di file?

Risposte:


24

I dispositivi più probabile ottenere un file in /dev/input/nome eventNdove N è i vari dispositivi come mouse, tastiera, presa, pulsanti di potenza etc.

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

dovrebbe darti un suggerimento.

Guarda anche:

cat /proc/bus/input/devices

Dove Sysfsvalore è percorso sotto /sys.

Puoi testare ad es

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

Per implementare utilizzare ioctl e controllare dispositivi + monitor.

MODIFICA 2:

OK. Mi sto espandendo su questa risposta in base al presupposto che /dev/input/eventNviene utilizzato.

Un modo potrebbe essere:

  1. Al ciclo di avvio tutti i eventfile trovati in /dev/input/. Utilizzare ioctl()per richiedere bit di eventi:

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

    quindi controlla se EV_KEY-bit è impostato.

  2. IFF impostato quindi controllare la presenza di chiavi:

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

    Ad esempio, se i tasti numerici sono interessanti, quindi verificare se i bit per KEY_0- KEY9e KEY_KP0per KEY_KP9.

  3. Le chiavi IFF trovate quindi iniziano a monitorare il file degli eventi nel thread.

  4. Torna a 1.

In questo modo dovresti riuscire a monitorare tutti i dispositivi che soddisfano i criteri desiderati. Non si può solo verificare, ad EV_KEYesempio, se il pulsante di accensione avrà questo bit impostato, ma ovviamente non sarà KEY_Aimpostato ecc.

Ho visto falsi positivi per chiavi esotiche, ma per chiavi normali questo dovrebbe essere sufficiente. Non vi è alcun danno diretto nel monitoraggio, ad esempio il file di eventi per il pulsante di accensione o un jack, ma quelli non emetteranno eventi in questione (ovvero codice errato).

Più in dettaglio di seguito.


MODIFICA 1:

Per quanto riguarda "Spiega quell'ultima affermazione ..." . Andando oltre nella terra dello stackoverflow qui ... ma:

Un esempio rapido e sporco in C. Dovrai implementare vari codici per verificare che tu abbia effettivamente il dispositivo corretto, tradurre il tipo di evento, il codice e il valore. Tipicamente key-down, key-up, key-repeat, key-code, ecc.

Non ho tempo (ed è troppo qui), per aggiungere il resto.

Dai un'occhiata linux/input.h, programmi come dumpkeys, codice del kernel ecc. Per i codici di mappatura. Per esempiodumpkeys -l

Comunque:

Esegui come ad esempio:

# ./testprog /dev/input/event2

Codice:

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

Nota che se guardi /proc/bus/input/deviceshai una lettera all'inizio di ogni riga. Qui Bsignifica bitmap. Questo è ad esempio:

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

Ciascuno di quei bit corrisponde a una proprietà del dispositivo. Che significa bitmap, 1 indica che è presente una proprietà, come definito in 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

Dai un'occhiata /drivers/input/input.{h,c}all'albero dei sorgenti del kernel. Un sacco di buon codice lì. (Ad esempio le proprietà dei dispositivi sono prodotte da questa funzione .)

Ognuna di queste mappe di proprietà può essere raggiunta da ioctl. Ad esempio, se si desidera verificare quali proprietà dei LED sono disponibili, dire:

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

Guarda la definizione di struct input_devin input.hper come ledbitsono definiti.

Per verificare lo stato dei LED, dire:

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

Se i bit 1 in ledbitsono 1, allora il blocco numerico è acceso. Se il bit 2 è 1, il blocco maiuscole è acceso ecc.

input.h ha le varie definizioni.


Note sul monitoraggio degli eventi:

Lo pseudo-codice per il monitoraggio potrebbe essere qualcosa nella direzione di:

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

Alcuni documenti correlati:

  1. Documentation/input/input.txt, esp. nota sezione 5.
  2. Documentation/input/event-codes.txt, Descrizione dei vari eventi, ecc prendere atto di ciò che è citato in esempio EV_SYNsuSYN_DROPPED
  3. Documentation/input ... leggi il resto se vuoi.

2

Puoi farlo facilmente facendo riferimento /dev/input/by-id/usb-manufacturername_*serialnumber*. Questi vengono visualizzati come collegamenti simbolici che è possibile utilizzare readlink -eper determinare il dispositivo a blocchi associato. Questi collegamenti sono comunque creati da quelli udevche potrebbero non essere presenti nel tuo ambiente incorporato.

Oppure .. Guarda dmesgdopo aver collegato il dispositivo USB. Dovrebbe darti il /devnodo.


1
Le voci in /dev/disk/by-id/sono create da udev- la domanda è se questo è disponibile in questo caso partcular (piattaforma integrata).
peterph

@peterph: hai ragione. Se non si utilizza udev, il primo suggerimento non funzionerà.
Jeight

@Gilles: vedo che hai modificato la risposta e ho cambiato il percorso di input piuttosto che il disco. In quel caso credo che sarebbe input / by-path e non disk / by-id. Sospetto che entrambi funzionerebbero.
Jeight

1
No, by-idè corretto. Ad esempio la mia tastiera USB è disponibile come /dev/input/by-id/usb-_USB_Keyboard-event-kbde /dev/input/by-path/pci-0000:00:1d.2-usb-0:2:1.0-event-kbd.
Gilles 'SO- smetti di essere malvagio' il

@Jeight: una volta trovato il nodo del dispositivo corretto, sai come posso accedere alla pressione dei tasti?
KyleL
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.