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


13

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

Respuestas:


24

Lo más probable es que los dispositivos obtengan un archivo con /dev/input/nombre eventNdonde N es los diversos 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 el Sysfsvalor es el camino bajo /sys.

Puedes probar por ej.

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

Para implementar use ioctl y verifique dispositivos + monitor.

EDITAR 2:

OKAY. Estoy ampliando esta respuesta en función de la suposición /dev/input/eventNutilizada.

Una forma podría ser:

  1. En el bucle de inicio todos los eventarchivos encontrados en /dev/input/. Use ioctl()para solicitar bits de eventos:

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

    luego verifique si EV_KEY-bit está configurado.

  2. IFF configurado y luego verifique las claves:

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

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

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

  4. Regresar a 1.

De esta manera, debe controlar todos los dispositivos que cumplan con los criterios deseados. No solo puede comprobarlo, por EV_KEYejemplo, el botón de encendido tendrá este bit establecido, pero obviamente no tendrá KEY_Aconfigurado etc.

He visto falsos positivos para claves exóticas, pero para claves normales esto debería ser suficiente. No hay ningún daño directo en la supervisión, por ejemplo, el archivo de eventos para el botón de encendido o un conector, pero usted no emitirá 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 "Explicar esa última declaración ..." . Pasando por la tierra de stackoverflow aquí ... pero:

Una muestra rápida y sucia en C. Tendrá que implementar varios códigos para verificar que realmente obtiene el dispositivo correcto, traducir el tipo de evento, el código y el valor. Por lo general, tecla abajo, tecla arriba, tecla repetida, código clave, etc.

No tengo tiempo, (y es demasiado aquí), para agregar el resto.

Echa un vistazo a linux/input.hprogramas como dumpkeyscódigo de kernel, etc. para 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/devices, tiene 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. Que por medio de mapa de bits, 1 indican 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

Echa un vistazo al /drivers/input/input.{h,c}árbol de fuentes del núcleo. Un montón de buen código allí. (Por ejemplo, las propiedades de los dispositivos son producidas por esta función ).

Cada uno de estos mapas de propiedades puede ser alcanzado por ioctl. Por ejemplo, si desea verificar qué propiedades de LED están disponibles, diga:

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

Mira la definición de struct input_devin input.hpara ver cómo ledbitse definen.

Para verificar el estado de los LED, diga:

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

Si el bit 1 de entrada ledbites 1, se enciende el bloqueo numérico. Si el bit 2 es 1, se enciende el bloqueo de mayúsculas, etc.

input.h tiene los distintos define.


Notas cuando se trata de monitoreo de eventos:

El seudocó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.txtesp. nota sección 5.
  2. Documentation/input/event-codes.txt, descripción de varios eventos, etc. Tome nota de lo que se menciona, por ejemplo, EV_SYNsobreSYN_DROPPED
  3. Documentation/input ... lee el resto si quieres.

2

Puede hacerlo fácilmente haciendo referencia /dev/input/by-id/usb-manufacturername_*serialnumber*. Estos aparecen como enlaces simbólicos que puede desreferenciar utilizando readlink -epara determinar el dispositivo de bloque asociado. Sin embargo, estos enlaces se crean mediante los udevcuales podrían no estar presentes en su entorno incrustado.

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


1
En mi opinión, las entradas /dev/disk/by-id/están creadas por udev: la pregunta es si está disponible en este caso particular (plataforma integrada).
Peter

@peterph: Estás en lo correcto. Si no usa udev, la primera sugerencia no funcionará.
Jeight

@Gilles: Veo que editó la respuesta y cambió la ruta de entrada en lugar de disco. En ese caso, creo que sería input / by-path y no disk / by-id. Sospecho que cualquiera funcionaría.
Jeight

1
No, by-ides correcto Por ejemplo, mi teclado USB está disponible como /dev/input/by-id/usb-_USB_Keyboard-event-kbdy /dev/input/by-path/pci-0000:00:1d.2-usb-0:2:1.0-event-kbd.
Gilles 'SO- deja de ser malvado'

@Jeight: una vez que encuentre el nodo de dispositivo correcto, ¿sabe cómo puedo acceder a las pulsaciones de teclas?
KyleL
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.