Linux

守護程序(即後台)程序是否可以從 USB 鍵盤查找按鍵?

  • October 9, 2016

我正在開發一個嵌入式 Linux 項目,我將開發一個程序,該程序將在啟動時自動執行並通過字元顯示和某種按鈕陣列與使用者互動。如果我們使用一個簡單的 GPIO 按鈕陣列,我可以輕鬆編寫程序來查找這些 GPIO 線上的按鍵。但是,我們的一個想法是使用 USB 數字鍵盤設備代替使用者輸入。我的理解是,這些設備將作為 USB 鍵盤呈現給作業系統。如果沿著這條路走,我的程序有沒有辦法從 Linux 中尋找這個 USB 鍵盤上的輸入,記住沒有虛擬終端或 VGA 顯示器。插入 USB 鍵盤後,“/dev”中是否有一個實體可以打開文件描述符?

設備最有可能獲得一個/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:

關於*“解釋最後的陳述……”*。在這裡查看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);

查看 in 的定義struct input_devinput.h了解如何ledbit定義。

要檢查 LED 的狀態,請說:

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

如果位 1ledbit為 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…如果您願意,請閱讀其餘部分。

引用自:https://unix.stackexchange.com/questions/94322