守护程序(即后台)进程是否可以从USB键盘查找按键?


13

我正在从事一个嵌入式Linux项目,在该项目中,我将开发一个程序,该程序将在启动时自动运行,并通过字符显示和某种按钮阵列与用户进行交互。如果我们使用简单的GPIO按钮阵列,我可以轻松编写程序来查找这些GPIO线上的按键。但是,我们的想法之一是使用USB数字键盘设备代替用户输入。我的理解是,这些设备将以USB键盘的形式呈现给操作系统。如果沿着这条路走,我的程序有一种方法可以从Linux内部在此USB键盘上寻找输入,请记住没有虚拟终端或VGA显示。插入USB键盘后,“ / dev”中是否有一个实体可以为我打开文件描述符?

Answers:


24

设备很可能会获得一个/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使用此答案。

一种方法是:

  1. 在启动循环时,在中event找到所有文件/dev/input/。使用ioctl()以请求事件位:

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

    然后检查是否EV_KEY设置了-bit。

  2. 设置IFF,然后检查密钥:

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

    例如,如果数字键很有趣,则检查KEY_0- KEY9KEY_KP0to的位KEY_KP9

  3. 找到IFF密钥,然后在线程中开始监视事件文件。

  4. 回到1。

这样,您应该监视所有符合所需条件的设备。您不仅可以检查,EV_KEY例如电源按钮是否已设置此位,但显然没有KEY_A设置等。

已经看到了异国密钥的误报,但是对于普通密钥来说就足够了。监视没有直接危害,例如,电源按钮或插孔的事件文件,但这些不会发出有问题的事件(又称错误代码)。

下面更详细。


编辑1:

关于“解释最后的陈述……”。在stackoverflow中继续浏览 ……但是:

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。例如,如果要检查可用的LED属性,请说:

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

查看struct input_devin的定义以input.h了解如何ledbit定义。

要检查LED状态,例如:

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

如果第1位ledbit为1,则num-lock点亮。如果位2为1,则大写锁定点亮,依此类推。

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

一些相关文件:

  1. Documentation/input/input.txt,尤其是 注意第5节。
  2. Documentation/input/event-codes.txt,各种事件的描述等。请注意EV_SYN有关“例如”的内容SYN_DROPPED
  3. Documentation/input ...如果需要,请继续阅读。

2

您可以通过引用轻松地做到这一点/dev/input/by-id/usb-manufacturername_*serialnumber*。这些显示为符号链接,您可以使用它们取消引用readlink -e以确定关联的块设备。但是,通过这些链接创建的链接udev可能不会出现在您的嵌入式环境中。

或.. dmesg连接USB设备后查看。它应该给您/dev节点。


1
/dev/disk/by-id/imho中的条目是由创建的udev-问题是在这种特殊情况下(嵌入式平台)是否可用。
彼得

@彼得:你是对的。如果不使用udev,第一个建议将不起作用。
13年

@吉尔斯:我看到你编辑了答案,并更改了输入路径而不是磁盘。在那种情况下,我相信它将是输入/按路径而不是磁盘/按ID。我怀疑任何一个都可以。
13年

1
不,by-id是正确的。例如,我的USB键盘可用作/dev/input/by-id/usb-_USB_Keyboard-event-kbd/dev/input/by-path/pci-0000:00:1d.2-usb-0:2:1.0-event-kbd
吉尔(Gilles)'所以

@Jeight:找到正确的设备节点后,您知道如何访问按键吗?
KyleL 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.