เป็นไปได้หรือไม่ที่กระบวนการภูต (เช่นพื้นหลัง) จะมองหาการกดปุ่มจากแป้นพิมพ์ USB?


13

ฉันกำลังทำงานในโครงการลินุกซ์ในตัวที่ฉันจะพัฒนาโปรแกรมที่จะทำงานโดยอัตโนมัติเมื่อบูตเครื่องและโต้ตอบกับผู้ใช้ผ่านการแสดงตัวอักษรและแถวเรียงปุ่ม ถ้าเราไปกับอาเรย์ปุ่ม GPIO ง่าย ๆ ฉันสามารถเขียนโปรแกรมที่จะค้นหาการกดปุ่มบนบรรทัด GPIO เหล่านั้นได้อย่างง่ายดาย อย่างไรก็ตามหนึ่งในความคิดของเราคือการใช้อุปกรณ์แผ่นหมายเลข USB แทนการป้อนข้อมูลของผู้ใช้ ความเข้าใจของฉันคือว่าอุปกรณ์เหล่านั้นจะนำเสนอตัวเองให้กับระบบปฏิบัติการเป็นแป้นพิมพ์ USB หากลงไปตามเส้นทางนี้จะมีวิธีให้โปรแกรมของฉันค้นหาอินพุตบนแป้นพิมพ์ USB นี้จากใน Linux โดยระลึกไว้เสมอว่าไม่มีจอเทอร์มินัลเสมือนหรือจอแสดงผล VGA เมื่อเสียบแป้นพิมพ์ USB มีเอนทิตีใน '/ dev' ที่ปรากฏว่าฉันสามารถเปิดไฟล์ descriptor ได้หรือไม่?

คำตอบ:


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:

ตกลง. ฉันกำลังขยายคำตอบนี้ตามสมมติฐานที่/dev/input/eventNใช้

วิธีหนึ่งอาจเป็น:

  1. ในวงเริ่มต้นทุกไฟล์ที่พบในevent /dev/input/ใช้ioctl()เพื่อร้องขอบิตของเหตุการณ์:

    ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
    

    จากนั้นตรวจสอบว่าEV_KEYมีการตั้งค่าบิต

  2. IFF ตั้งค่าแล้วตรวจสอบคีย์:

    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), &keybit);
    

    เช่นถ้าจำนวนคีย์เป็นที่น่าสนใจแล้วตรวจสอบว่าบิตKEY_0- KEY9และเพื่อKEY_KP0KEY_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หมายถึง bit-map นั่นคือตัวอย่าง:

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_devในinput.hสำหรับวิธีการledbitที่กำหนดไว้

ในการตรวจสอบสถานะของ LED ว่า:

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

ถ้า bit 1 in ledbitเป็น 1 ดังนั้น num-lock จะติดสว่าง ถ้า bit 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, esp. หมายเหตุมาตรา 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อาจไม่มีอยู่ในสภาพแวดล้อมแบบฝังของคุณ

หรือ .. ดูdmesgหลังจากการเชื่อมต่ออุปกรณ์ USB มันควรให้/devโหนดกับคุณ


1
รายการใน/dev/disk/by-id/เป็น imho สร้างขึ้นโดยudev- คำถามคือว่านี้มีอยู่ในกรณีอนุภาค (แพลตฟอร์มฝัง)
เตอร์

@peterph: ถูกต้อง หากไม่ได้ใช้ udev คำแนะนำแรกจะไม่ทำงาน
Jeight

@Gilles: ฉันเห็นคุณแก้ไขคำตอบและเปลี่ยนเส้นทางการป้อนข้อมูลมากกว่าดิสก์ ในกรณีนั้นฉันเชื่อว่ามันจะเป็น input / by-path ไม่ใช่ disk / by-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 'หยุดความชั่วร้าย'

@Jeight: เมื่อฉันพบโหนดอุปกรณ์ที่ถูกต้องคุณรู้วิธีที่ฉันสามารถเข้าถึงการกดปุ่ม?
KyleL
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.