%20busque%20pulsaciones%20de%20teclas%20desde%20un%20teclado%20USB%3F.png)
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 eventN
donde 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 Sysfs
el 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/eventN
que se utiliza.
Una forma podría ser:
Al iniciar el ciclo, todos
event
los archivos encontrados en/dev/input/
. Úseloioctl()
para solicitar bits de eventos:ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
luego verifique si
EV_KEY
-bit está configurado.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
-KEY9
yKEY_KP0
paraKEY_KP9
.Se encuentran las claves IFF y luego comienza a monitorear el archivo de eventos en el hilo.
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_KEY
por ejemplo, el botón de encendido tenga este bit configurado, sino que obviamente no tendrá KEY_A
configurado 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.h
programas como dumpkeys
el 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/devices
tiene una letra al comienzo de cada línea. Aquí B
significa 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_dev
in input.h
para saber cómo ledbit
se definen.
Para verificar el estado de los LED, diga:
ioctl(fd, EVIOCGLED(sizeof(ledbit)), &ledbit);
Si el bit 1 ledbit
es 1, entonces el bloqueo numérico está encendido. Si el bit 2 es 1, entonces el bloqueo de mayúsculas está encendido, etc.
input.h
tiene 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:
Documentation/input/input.txt
, especialmente tenga en cuenta la sección 5.Documentation/input/event-codes.txt
, descripción de varios eventos, etc. Tome nota de lo que se menciona en, por ejemplo,EV_SYN
acerca deSYN_DROPPED
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 -e
para determinar el dispositivo de bloque asociado. Sin embargo, estos enlaces se crean y udev
es posible que no estén presentes en su entorno integrado.
O... Mire dmesg
después de conectar el dispositivo USB. Debería darte el /dev
nodo.