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


13

私は組み込みLinuxプロジェクトに取り組んでいます。そこでは、起動時に自動的に実行され、キャラクターディスプレイと何らかのボタン配列を介してユーザーと対話するプログラムを開発します。単純なGPIOボタンアレイを使用すれば、それらのGPIOラインでキー入力を探すプログラムを簡単に作成できます。しかし、私たちの考えの1つは、ユーザー入力に代わりにUSBテンキーデバイスを使用することでした。私の理解では、これらのデバイスはUSBキーボードとしてOSに表示されます。この道を進むと、仮想端末やVGAディスプレイがないことに注意して、私のプログラムがLinux内からこのUSBキーボードの入力を探す方法があります。USBキーボードが接続されている場合、「/ dev」にファイル記述子を開くことができるように見えるエンティティがありますか?

回答:


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:

OK。/dev/input/eventN使用されている仮定に基づいて、この答えを拡張しています。

1つの方法は次のとおりです。

  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

これらの各ビットは、デバイスのプロパティに対応しています。で定義されてlinux/input.hいるように、ビットマップが意味する1は、プロパティが存在することを示します。:

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の場合、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.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/作成者ですudev-問題は、この特定のケース(組み込みプラットフォーム)で利用できるかどうかです。
ペテルフ

@peterph:あなたは正しいです。udevを使用しない場合、最初の提案は機能しません。
ジェイト

@Gilles:答えを編集し、ディスクではなく入力にパスを変更したようです。その場合、disk / by-idではなくinput / by-pathになると思います。どちらかがうまくいくと思う。
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
ジル 'SO-悪であるのをやめる'

@Jeight:正しいデバイスノードを見つけたら、キープレスにアクセスする方法を知っていますか?
カイル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.