デーモン (つまりバックグラウンド) プロセスが USB キーボードからのキー押下を探すことは可能ですか?

デーモン (つまりバックグラウンド) プロセスが USB キーボードからのキー押下を探すことは可能ですか?

私は組み込み Linux プロジェクトに取り組んでおり、起動時に自動的に実行され、文字ディスプレイと何らかのボタン アレイを介してユーザーと対話するプログラムを開発する予定です。単純な GPIO ボタン アレイを使用する場合は、それらの GPIO ラインでキー押下を検出するプログラムを簡単に作成できます。ただし、ユーザー入力には USB テンキー デバイスを使用するというアイデアもありました。私の理解では、これらのデバイスは OS に対して USB キーボードとして表示されます。この方法を採用する場合、仮想端末や VGA ディスプレイがないことを念頭に置き、Linux 内からこの USB キーボードの入力を検出するプログラムはありますか。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:

OK。 が使用されているという仮定に基づいて、この回答を拡張します/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-KEY9およびKEY_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_devinの定義を参照してください。input.hledbit

LED のステータスを確認するには、次のように言います。

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

ビット 1 がledbit1 の場合、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

関連文書:

  1. Documentation/input/input.txt特にセクション 5 に注意してください。
  2. Documentation/input/event-codes.txtEV_SYN、様々なイベントの説明など。例えば、以下に記載されている内容に注意してください。SYN_DROPPED
  3. Documentation/input... 残りは必要に応じて読んでください。

答え2

を参照することで、これを簡単に行うことができます/dev/input/by-id/usb-manufacturername_*serialnumber*。これらは、readlink -e関連するブロック デバイスを決定するために使用して逆参照できるシンボリック リンクとして表示されます。ただし、これらのリンクは によって作成されるudevため、組み込み環境に存在しない可能性があります。

または、dmesgUSB デバイスを接続した後で を見てください。/devノードが表示されるはずです。

関連情報