%20procure%20pressionamentos%20de%20teclas%20em%20um%20teclado%20USB%3F.png)
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 optarmos por um array de botões GPIO simples, posso facilmente escrever um programa que procurará pressionamentos de tecla nessas linhas GPIO. No entanto, uma de nossas ideias foi usar um dispositivo de teclado numérico USB para entrada do usuário. Meu entendimento é que esses dispositivos se apresentarão ao sistema operacional como um teclado USB. Se seguir esse caminho, existe uma maneira de meu programa procurar entradas neste teclado USB de dentro 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 aparece para a qual posso abrir um descritor de arquivo?
Responder1
Os dispositivos provavelmente recebem um arquivo /dev/input/
nomeado eventN
onde N são os vários dispositivos, como mouse, teclado, conector, 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
Onde Sysfs
o valor é o caminho abaixo /sys
.
Você pode testar, por exemplo
cat /dev/input/event2 # if 2 is kbd.
Para implementar use ioctl e verifique dispositivos + monitor.
EDITAR 2:
OK. Estou expandindo esta resposta com base na suposição /dev/input/eventN
usada.
Uma maneira poderia ser:
Na inicialização, todos
event
os arquivos encontrados em/dev/input/
. Useioctl()
para solicitar bits de evento:ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
então verifique se
EV_KEY
-bit está definido.Defina IFF 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 for
KEY_0
-KEY9
eKEY_KP0
toKEY_KP9
.As chaves IFF encontradas iniciam o monitoramento do arquivo de eventos no thread.
Voltar para 1.
Dessa forma, você deverá monitorar todos os dispositivos que atendam aos critérios desejados. Você não pode apenas verificar EV_KEY
se, por exemplo, o botão liga / desliga terá esse bit definido, mas obviamente não terá KEY_A
etc.
Vi falsos positivos para chaves exóticas, mas parateclas normaisisso deve ser suficiente. Não há nenhum dano direto no monitoramento, por exemplo, do arquivo de eventos do botão liga / desliga ou de uma tomada, mas você não emitirá os eventos em questão (também conhecido como código incorreto).
Mais detalhes abaixo.
EDITAR 1:
No que diz respeito a"Explique essa última afirmação...". Indo para dentrofluxo de pilhapousar aqui… mas:
Um exemplo rápido e sujo em C. Você terá que implementar vários códigos para verificar se realmente obteve o dispositivo correto, traduziu o tipo de evento, o código e o valor. Normalmente key-down, key-up, key-repeat, key-code, etc.
Não tenho tempo (e é demais aqui) para adicionar o resto.
Confira linux/input.h
programas como dumpkeys
código do kernel etc. para códigos de mapeamento. Por exemplodumpkeys -l
De qualquer forma:
Execute 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;
}
EDITAR 2 (continuação):
Observe que se você olhar, /proc/bus/input/devices
terá uma letra no início de cada linha. Aqui B
significa mapa de bits. Isso é, 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. O que, por meio de 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 /drivers/input/input.{h,c}
na árvore de origem do kernel. Há muito código bom lá. (Por exemplo, as propriedades dos dispositivos são produzidas poresta função.)
Cada um desses mapas de propriedades 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
in input.h
para saber como ledbit
são definidos.
Para verificar o status dos LEDs, diga:
ioctl(fd, EVIOCGLED(sizeof(ledbit)), &ledbit);
Se o bit 1 ledbit
for 1, então num-lock estará aceso. Se o bit 2 for 1, então o caps lock está aceso, etc.
input.h
tem várias definições.
Notas quando se trata de monitoramento de eventos:
O pseudocó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:
Documentation/input/input.txt
, esp. observe a seção 5.Documentation/input/event-codes.txt
, descrição de vários eventos, etc. Tome nota do que é mencionado em, por exemplo,EV_SYN
sobreSYN_DROPPED
Documentation/input
... leia o resto, se quiser.
Responder2
Você pode fazer isso facilmente referenciando /dev/input/by-id/usb-manufacturername_*serialnumber*
. Eles aparecem como links simbólicos que você pode usar para desreferenciar readlink -e
para determinar o dispositivo de bloco associado. No entanto, esses links são criados e udev
podem não estar presentes em seu ambiente incorporado.
Ou.. Veja dmesg
depois de conectar o dispositivo USB. Deve lhe dar o /dev
nó.