데몬 (예 : 백그라운드) 프로세스가 USB 키보드에서 키 누르기를 찾을 수 있습니까?


13

나는 부팅시 자동으로 실행되고 문자 디스플레이와 일종의 버튼 배열을 통해 사용자와 상호 작용하는 프로그램을 개발할 임베디드 Linux 프로젝트를 진행하고 있습니다. 간단한 GPIO 버튼 배열을 사용하면 GPIO 라인에서 키 누르기를 찾는 프로그램을 쉽게 작성할 수 있습니다. 그러나 우리의 생각 중 하나는 사용자 입력 대신 USB 숫자 패드 장치를 사용하는 것이 었습니다. 내 이해는 그 장치가 USB 키보드로 OS에 표시된다는 것입니다. 이 경로를 따라 가면 가상 터미널이나 VGA 디스플레이가 없다는 것을 명심하면서 내 프로그램이 Linux 내 에서이 USB 키보드에서 입력을 찾을 수있는 방법이 있습니까? USB 키보드가 연결되면 '/ dev'에 파일 디스크립터를 열 수있는 엔티티가 있습니까?

답변:


24

장치 는 N /dev/input/이라는 이름 eventN이 마우스, 키보드, 잭, 전원 단추 등과 같은 다양한 장치 인 파일을 얻을 가능성이 높습니다 .

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

힌트를 주어야합니다.

또한보십시오 :

cat /proc/bus/input/devices

여기서 Sysfsvalue는 path 아래에 /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 :

"마지막 진술을 설명 할 때…"와 관련하여 . 여기 에 스택 오버 플로우 토지가 있습니다 ...하지만 :

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_devinput.h방법은 ledbit정의됩니다.

LED의 상태를 확인하려면

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

비트 1 in 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 사용자가 포함 된 환경에 없을 수도 있습니다.

또는 .. dmesgUSB 장치를 연결 한 후를 보십시오 . /dev노드를 제공해야 합니다.


1
항목 /dev/disk/by-id/은 imho에 의해 생성됩니다 udev. 질문은이 부분적 사례 (임베디드 플랫폼)에서 사용 가능한지 여부입니다.
peterph

@ peterph : 맞습니다. udev를 사용하지 않으면 첫 번째 제안이 작동하지 않습니다.
Jeight

@ Giles : 대답을 편집하고 디스크가 아닌 입력 경로를 변경하는 것을 알았습니다. 이 경우 디스크 / ID가 아닌 경로 / 입력 경로라고 생각합니다. 어느 쪽이든 효과가있을 것 같습니다.
Jeight

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 'SO- 악마 그만해

@Jeight : 올바른 장치 노드를 찾으면 키 누름에 액세스하는 방법을 알고 있습니까?
KyleL
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.