守護程式(即背景)進程是否可以從 USB 鍵盤尋找按鍵?

守護程式(即背景)進程是否可以從 USB 鍵盤尋找按鍵?

我正在開發一個嵌入式 Linux 項目,其中我將開發一個程序,該程序將在啟動時自動運行,並透過字元顯示和某種按鈕陣列與用戶互動。如果我們使用簡單的 GPIO 按鈕陣列,我可以輕鬆編寫程式來尋找這些 GPIO 線路上的按鍵。然而,我們的想法之一是使用 USB 數位鍵盤裝置來取代使用者輸入。我的理解是,這些裝置會將自己作為 USB 鍵盤呈現給作業系統。如果沿著這條路走下去,有沒有辦法讓我的程式從 Linux 內部尋找這個 USB 鍵盤上的輸入,記住沒有虛擬終端或 VGA 顯示器。當插入 USB 鍵盤時,「/dev」中是否存在可以開啟檔案描述符的實體?

答案1

設備很可能會獲得一個/dev/input/名為的文件eventN,其中 N 是各種設備,例如滑鼠、鍵盤、插孔、電源按鈕等。

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

應該給你一個提示。

另請參閱:

cat /proc/bus/input/devices

其中Sysfsvalue 是 下的路徑/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_devin的定義input.h了解如何ledbit定義。

若要檢查 LED 的狀態,請執行以下操作:

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

如果位 1 為ledbit1,則數字鎖定燈亮起。如果位元 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_SYNSYN_DROPPED
  3. Documentation/input....如果您願意,請閱讀其餘部分。

答案2

您可以透過引用輕鬆完成此操作/dev/input/by-id/usb-manufacturername_*serialnumber*。它們顯示為符號鏈接,您可以使用它們取消引用readlink -e以確定關聯的區塊設備。然而,這些連結是由其創建的udev,可能不存在於您的嵌入式環境中。

dmesg或..連接USB設備後看一下。它應該給你/dev節點。

相關內容