É possível que um processo de daemon (por exemplo, segundo plano) procure teclas pressionadas em um teclado USB?

13

Estou trabalhando em um projeto Linux embarcado onde desenvolverei um programa que será executado automaticamente na inicialização e interagirá com o usuário por meio de uma exibição de caracteres e algum tipo de matriz de botões. Se formos com um simples array de botões GPIO, posso escrever facilmente programas que procurarão pressionamentos de teclas nessas linhas GPIO. No entanto, um dos nossos pensamentos foi usar um dispositivo de teclado numérico USB para a entrada do usuário. Meu entendimento é que esses dispositivos se apresentarão no SO como um teclado USB. Se seguir este caminho, existe uma maneira de o meu programa procurar informações sobre este teclado USB a partir do Linux, tendo em mente que não há terminal virtual ou monitor VGA. Quando um teclado USB é conectado, existe uma entidade em '/ dev' que parece que eu posso abrir um descritor de arquivo?

    
por KyleL 09.10.2013 / 22:52

2 respostas

23

Os dispositivos provavelmente obtêm um arquivo em /dev/input/ chamado eventN , em que N é um dos vários dispositivos, como mouse, teclado, tomada, botões liga / desliga, etc.

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

deve lhe dar uma dica.

Veja também:

cat /proc/bus/input/devices

Em que Sysfs value é o caminho em /sys .

Você pode testar, por exemplo,

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

Para implementar use ioctl e verifique dispositivos + monitor.

EDIT 2:

OK. Estou expandindo essa resposta com base na suposição de que /dev/input/eventN é usado.

Um caminho pode ser:

  1. No loop de inicialização, todos os arquivos event encontrados em /dev/input/ . Use ioctl() para solicitar bits de evento:

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

    verifique se EV_KEY -bit está definido.

  2. IFF set e verifique as chaves:

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

    Por exemplo Se as teclas numéricas forem interessantes, verifique se os bits de KEY_0 - KEY9 e KEY_KP0 to KEY_KP9 .

  3. As chaves IFF encontradas então começam a monitorar o arquivo de eventos no thread.

  4. Voltar para 1.

Dessa forma, você deve monitorar todos os dispositivos que atendem aos critérios desejados. Você não pode verificar apenas EV_KEY , por exemplo, botão liga / desliga terá esse bit definido, mas obviamente não terá KEY_A etc. definido.

Vimos falsos positivos para chaves exóticas, mas para teclas normais isso deve ser suficiente. Não há danos diretos no monitoramento, por ex. arquivo de evento para o botão de energia ou um jack, mas você não vai emitir eventos em questão (também conhecido como código ruim).

Mais em detalhes abaixo.

EDIT 1:

Em relação a "Explique essa última afirmação ..." . Indo em stackoverflow aterre aqui… mas:

Um exemplo rápido e sujo em C. Você terá que implementar vários códigos para verificar se realmente obtém o dispositivo correto, o tipo de evento, o código e o valor do evento. Tipicamente key-down, key-up, key-repeat, código-chave, etc.

Não tenho tempo (e é muito aqui) para adicionar o resto.

Confira linux/input.h , programas como dumpkeys , código do kernel, etc. para códigos de mapeamento. Por exemplo. dumpkeys -l

De qualquer forma:

Executar como por exemplo:

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

EDIT 2 (continuação):

Observe que, se você olhar para /proc/bus/input/devices , você tem uma carta no início de cada linha. Aqui B significa mapa de bits. Por exemplo:

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 um desses bits corresponde a uma propriedade do dispositivo. Que por meio do mapa de bits, 1 indica que uma propriedade está presente, conforme definido em 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

Dê uma olhada em /drivers/input/input.{h,c} na árvore de fontes do kernel. Muito bom código lá. (Por exemplo, as propriedades dos dispositivos são produzidas por esta função .)

Cada um desses mapas de propriedade pode ser obtido por ioctl . Por exemplo, se você quiser verificar quais propriedades de LED estão disponíveis, diga:

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

Veja a definição de struct input_dev em input.h de como ledbit está definido.

Para verificar o status dos LEDs, diga:

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

Se o bit 1 em ledbit for 1, então o num-lock estará aceso. Se o bit 2 é 1, então o caps lock está aceso, etc.

input.h tem as várias definições.

Notas quando se trata de monitoramento de eventos:

O pseudo-código para monitoramento pode ser algo na direção 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

Alguns documentos relacionados:

  1. Documentation/input/input.txt , esp. observe a seção 5.
  2. Documentation/input/event-codes.txt , descrição de vários eventos, etc. Tome nota do que é mencionado em, e. EV_SYN sobre SYN_DROPPED
  3. Documentation/input ... leia o resto se quiser.
por 09.10.2013 / 23:41
2

Você pode fazer isso facilmente referenciando /dev/input/by-id/usb-manufacturername_*serialnumber* . Eles aparecem como links simbólicos que você pode excluir usando readlink -e para determinar o dispositivo de bloco associado. Esses links, no entanto, são criados pelo udev , que pode não estar presente em seu ambiente incorporado.

Ou ... Veja o dmesg depois de conectar o dispositivo USB. Deve dar-lhe o nó /dev .

    
por 09.10.2013 / 23:24