%20%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%20%D0%BE%D1%82%D1%81%D0%BB%D0%B5%D0%B6%D0%B8%D0%B2%D0%B0%D1%82%D1%8C%20%D0%BD%D0%B0%D0%B6%D0%B0%D1%82%D0%B8%D1%8F%20%D0%BA%D0%BB%D0%B0%D0%B2%D0%B8%D1%88%20%D0%BD%D0%B0%20USB-%D0%BA%D0%BB%D0%B0%D0%B2%D0%B8%D0%B0%D1%82%D1%83%D1%80%D0%B5%3F.png)
Я работаю над проектом встроенного Linux, в котором я буду разрабатывать программу, которая будет автоматически запускаться при загрузке и взаимодействовать с пользователем через символьный дисплей и некий массив кнопок. Если мы возьмем простой массив кнопок GPIO, я смогу легко написать программу, которая будет искать нажатия клавиш на этих линиях GPIO. Однако одной из наших мыслей было использовать вместо этого USB-устройство цифровой клавиатуры для пользовательского ввода. Я понимаю, что эти устройства будут представляться ОС как USB-клавиатура. Если пойти по этому пути, есть ли способ для моей программы искать ввод на этой USB-клавиатуре из Linux, имея в виду, что нет виртуального терминала или VGA-дисплея. Когда подключена USB-клавиатура, появляется ли в '/dev' сущность, для которой я могу открыть файловый дескриптор?
решение1
Устройства, скорее всего, получают файл с /dev/input/
именем eventN
, где N — это различные устройства, такие как мышь, клавиатура, разъем, кнопки питания и т. д.
ls -l /dev/input/by-{path,id}/
должен дать вам подсказку.
Смотрите также:
cat /proc/bus/input/devices
Где Sysfs
значение — это путь под /sys
.
Вы можете проверить, например,
cat /dev/input/event2 # if 2 is kbd.
Для реализации используйте ioctl и проверьте устройства + монитор.
ПРАВКА 2:
Хорошо. Я расширяю этот ответ, основываясь на /dev/input/eventN
используемом предположении.
Одним из способов может быть:
При запуске цикла все
event
файлы, найденные в/dev/input/
. Используйтеioctl()
для запроса битов событий:ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
затем проверьте,
EV_KEY
установлен ли -бит.Установите IFF, затем проверьте наличие ключей:
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), &keybit);
Например, если интересны цифровые клавиши, то проверьте, есть ли биты для
KEY_0
-KEY9
иKEY_KP0
дляKEY_KP9
.Если найдены ключи IFF, то начинается мониторинг файла событий в потоке.
Вернуться к 1.
Таким образом, вы должны получить возможность контролировать все устройства, которые соответствуют желаемым критериям. Вы не можете только проверить, EV_KEY
например, что кнопка питания будет иметь этот бит установленным, но она, очевидно, не будет иметь KEY_A
и т. д. установленными.
Были зафиксированы ложные срабатывания для экзотических ключей, но дляобычные ключиэтого должно хватить. Прямого вреда в мониторинге, например, файла событий для кнопки питания или разъема нет, но вы не будете выдавать события в вопросе (т.е. плохой код).
Более подробно об этом ниже.
ПРАВКА 1:
В отношении«Объясните последнее утверждение…». Переходя впереполнение стекаприземлиться здесь… но:
Быстрый и грязный пример на C. Вам придется реализовать различный код, чтобы проверить, что вы действительно получаете правильное устройство, перевести тип события, код и значение. Обычно нажатие клавиши, отпускание клавиши, повторение клавиши, код клавиши и т. д.
Нет времени (и его здесь слишком много), чтобы добавить остальное.
Проверьте linux/input.h
, такие программы dumpkeys
, как код ядра и т. д. для сопоставления кодов. Напримерdumpkeys -l
В любом случае:
Запустить, например, как:
# ./testprog /dev/input/event2
Код:
#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;
}
ПРАВКА 2 (продолжение):
Обратите внимание, что если вы посмотрите, /proc/bus/input/devices
у вас есть буква в начале каждой строки. Здесь B
имеется в виду битовая карта. Например:
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
Каждый из этих битов соответствует свойству устройства. Что по битовой карте означает, что 1 указывает на наличие свойства, как определено в 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
Посмотрите на /drivers/input/input.{h,c}
исходный код ядра. Там много хорошего кода. (Например, свойства устройств создаютсяэта функция.)
Каждая из этих карт свойств может быть получена с помощью ioctl
. Например, если вы хотите проверить, какие свойства светодиода доступны, скажите:
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(ledbit)), &ledbit);
Посмотрите определение struct input_dev
in input.h
, чтобы узнать, как ledbit
оно определяется.
Чтобы проверить состояние светодиодов, произнесите:
ioctl(fd, EVIOCGLED(sizeof(ledbit)), &ledbit);
Если бит 1 ledbit
равен 1, то горит Num-Lock. Если бит 2 равен 1, то горит Caps Lock и т. д.
input.h
имеет различные определения.
Примечания по мониторингу событий:
Псевдокод для мониторинга может быть чем-то вроде:
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
Некоторые сопутствующие документы:
Documentation/input/input.txt
, особенно примечание к разделу 5.Documentation/input/event-codes.txt
, описание различных событий и т.д. Обратите внимание на то, что упоминается под, напримерEV_SYN
,SYN_DROPPED
Documentation/input
...прочитайте остальное, если хотите.
решение2
Вы можете легко сделать это, ссылаясь на /dev/input/by-id/usb-manufacturername_*serialnumber*
. Они отображаются как символические ссылки, которые вы можете разыменовать, readlink -e
чтобы определить связанное блочное устройство. Однако эти ссылки создаются с помощью , udev
которые могут отсутствовать в вашей встроенной среде.
Или.. Посмотрите на dmesg
после подключения USB-устройства. Он должен дать вам /dev
узел.