¿Es posible que un proceso demonio (es decir, en segundo plano) busque pulsaciones de teclas desde un teclado USB?

¿Es posible que un proceso demonio (es decir, en segundo plano) busque pulsaciones de teclas desde un teclado USB?

Estoy trabajando en un proyecto de Linux integrado en el que desarrollaré un programa que se ejecutará automáticamente al iniciar e interactuará con el usuario a través de una pantalla de caracteres y algún tipo de matriz de botones. Si optamos por una matriz de botones GPIO simple, puedo escribir fácilmente un programa que busque pulsaciones de teclas en esas líneas GPIO. Sin embargo, una de nuestras ideas fue utilizar un dispositivo de teclado numérico USB en lugar de la entrada del usuario. Tengo entendido que esos dispositivos se presentarán ante el sistema operativo como un teclado USB. Si sigo este camino, ¿hay alguna manera de que mi programa busque entradas en este teclado USB desde Linux, teniendo en cuenta que no hay terminal virtual ni pantalla VGA? Cuando se conecta un teclado USB, ¿hay alguna entidad en '/dev' que aparece para la que puedo abrir un descriptor de archivo?

Respuesta1

Lo más probable es que los dispositivos obtengan un archivo con /dev/input/un nombre eventNdonde N son los distintos dispositivos, como mouse, teclado, conector, botones de encendido, etc.

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

debería darte una pista.

Mira también:

cat /proc/bus/input/devices

Donde Sysfsel valor es la ruta debajo /sys.

Puedes probar por ejemplo

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

Para implementar use ioctl y verifique dispositivos + monitor.

EDITAR 2:

DE ACUERDO. Estoy ampliando esta respuesta según la suposición /dev/input/eventNque se utiliza.

Una forma podría ser:

  1. Al iniciar el ciclo, todos eventlos archivos encontrados en /dev/input/. Úselo ioctl()para solicitar bits de eventos:

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

    luego verifique si EV_KEY-bit está configurado.

  2. Configure IFF y luego verifique las claves:

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

    Por ejemplo, si las claves numéricas son interesantes, compruebe si los bits para KEY_0- KEY9y KEY_KP0para KEY_KP9.

  3. Se encuentran las claves IFF y luego comienza a monitorear el archivo de eventos en el hilo.

  4. Volver a 1.

De esta manera debería poder monitorear todos los dispositivos que cumplan con los criterios deseados. No solo puede verificar que, EV_KEYpor ejemplo, el botón de encendido tenga este bit configurado, sino que obviamente no tendrá KEY_Aconfigurado etc.

He visto falsos positivos para claves exóticas, pero parallaves normalesesto debería ser suficiente. No hay ningún daño directo al monitorear, por ejemplo, el archivo de eventos para el botón de encendido o un conector, pero esos no emitirán los eventos en cuestión (también conocido como código incorrecto).

Más en detalle a continuación.


EDITAR 1:

En lo que respecta a"Explica esa última afirmación...". Pasando endesbordamiento de pilaaterrizar aquí… pero:

Una muestra rápida y sucia en C. Tendrá que implementar varios códigos para verificar que realmente obtenga el dispositivo correcto, traduzca el tipo de evento, el código y el valor. Por lo general, tecla presionada, tecla arriba, repetición de tecla, código de tecla, etc.

No tengo tiempo (y aquí es demasiado) para añadir el resto.

Consulte linux/input.hprogramas como dumpkeysel código del kernel, etc. para obtener códigos de mapeo. P.ejdumpkeys -l

De todos modos:

Ejecutar como por ejemplo:

# ./testprog /dev/input/event2

Código:

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

EDITAR 2 (continuación):

Tenga en cuenta que si observa, /proc/bus/input/devicestiene una letra al comienzo de cada línea. Aquí Bsignifica mapa de bits. Eso es por ejemplo:

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

Cada uno de esos bits corresponde a una propiedad del dispositivo. Lo cual, por medio de mapa de bits, 1 indica que una propiedad está presente, como se define en 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

Eche un vistazo /drivers/input/input.{h,c}al árbol de fuentes del kernel. Hay mucho código bueno allí. (Por ejemplo, las propiedades de los dispositivos se producen medianteesta función.)

Cada uno de estos mapas de propiedades se puede obtener mediante ioctl. Por ejemplo, si desea comprobar qué propiedades de LED están disponibles, diga:

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

Mire la definición de struct input_devin input.hpara saber cómo ledbitse definen.

Para verificar el estado de los LED, diga:

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

Si el bit 1 ledbites 1, entonces el bloqueo numérico está encendido. Si el bit 2 es 1, entonces el bloqueo de mayúsculas está encendido, etc.

input.htiene varias definiciones.


Notas sobre el seguimiento de eventos:

El pseudocódigo para el monitoreo podría ser algo en la dirección de:

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

Algunos documentos relacionados:

  1. Documentation/input/input.txt, especialmente tenga en cuenta la sección 5.
  2. Documentation/input/event-codes.txt, descripción de varios eventos, etc. Tome nota de lo que se menciona en, por ejemplo, EV_SYNacerca deSYN_DROPPED
  3. Documentation/input... lee el resto si quieres.

Respuesta2

Puedes hacerlo fácilmente haciendo referencia a /dev/input/by-id/usb-manufacturername_*serialnumber*. Estos aparecen como enlaces simbólicos a los que puede desreferenciar readlink -epara determinar el dispositivo de bloque asociado. Sin embargo, estos enlaces se crean y udeves posible que no estén presentes en su entorno integrado.

O... Mire dmesgdespués de conectar el dispositivo USB. Debería darte el /devnodo.

información relacionada