Có thể cho quá trình daemon (tức là nền) tìm kiếm các lần nhấn phím từ bàn phím USB không?


13

Tôi đang làm việc trong một dự án Linux nhúng, nơi tôi sẽ phát triển một chương trình sẽ tự động chạy khi khởi động và tương tác với người dùng thông qua màn hình hiển thị ký tự và một số mảng nút. Nếu chúng ta đi với một mảng nút GPIO đơn giản, tôi có thể dễ dàng viết chương trình sẽ tìm kiếm các phím bấm trên các dòng GPIO đó. Tuy nhiên, một trong những suy nghĩ của chúng tôi là sử dụng thiết bị số USB thay cho đầu vào của người dùng. Tôi hiểu rằng những thiết bị đó sẽ tự trình bày với HĐH dưới dạng bàn phím USB. Nếu đi xuống con đường này, có cách nào để chương trình của tôi tìm kiếm đầu vào trên bàn phím USB này từ bên trong Linux, hãy nhớ rằng không có thiết bị đầu cuối ảo hoặc màn hình VGA. Khi cắm bàn phím USB, có thực thể nào trong '/ dev' xuất hiện mà tôi có thể mở mô tả tệp không?

Câu trả lời:


24

Các thiết bị rất có thể nhận được một tệp /dev/input/có tên eventNN trong đó các thiết bị khác nhau như chuột, bàn phím, giắc cắm, nút nguồn, v.v.

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

sẽ cho bạn một gợi ý.

Cũng nhìn vào:

cat /proc/bus/input/devices

Trong đó Sysfsgiá trị là đường dẫn dưới /sys.

Bạn có thể kiểm tra bằng ví dụ

cat /dev/input/event2 # if 2 is kbd.

Để thực hiện sử dụng ioctl và kiểm tra thiết bị + màn hình.

EDIT 2:

ĐỒNG Ý. Tôi đang mở rộng câu trả lời này dựa trên giả định /dev/input/eventNđược sử dụng.

Một cách có thể là:

  1. Tại vòng lặp khởi động tất cả eventcác tập tin được tìm thấy trong /dev/input/. Sử dụng ioctl()để yêu cầu bit sự kiện:

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

    sau đó kiểm tra xem EV_KEY-bit có được đặt không.

  2. IFF đặt sau đó kiểm tra các khóa:

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

    Ví dụ: nếu các phím số thú vị, thì hãy kiểm tra xem các bit cho KEY_0- KEY9KEY_KP0đến KEY_KP9.

  3. Các khóa IFF được tìm thấy sau đó bắt đầu theo dõi tệp sự kiện trong chuỗi.

  4. Quay lại 1.

Bằng cách này, bạn sẽ có được để theo dõi tất cả các thiết bị đáp ứng các tiêu chí mong muốn. Bạn không thể chỉ kiểm tra EV_KEYví dụ như nút nguồn sẽ có bộ bit này, nhưng rõ ràng là nó sẽ không được KEY_Ađặt.

Đã thấy dương tính giả cho các khóa kỳ lạ, nhưng đối với các khóa bình thường thì điều này là đủ. Không có tác hại trực tiếp nào trong việc giám sát, ví dụ như tệp sự kiện cho nút nguồn hoặc giắc cắm, nhưng bạn sẽ không phát ra các sự kiện trong câu hỏi (hay còn gọi là mã xấu).

Chi tiết hơn dưới đây.


CHỈNH SỬA 1:

Liên quan đến "Giải thích rằng tuyên bố cuối cùng" . Đi qua vùng đất stackoverflow tại đây nhưng:

Một mẫu nhanh và bẩn trong C. Bạn sẽ phải triển khai nhiều mã khác nhau để kiểm tra xem bạn có thực sự nhận được đúng thiết bị, dịch loại sự kiện, mã và giá trị. Điển hình là khóa xuống, khóa phím, lặp lại khóa, mã khóa, v.v.

Không có thời gian, (và quá nhiều ở đây), để thêm phần còn lại.

Kiểm tra linux/input.h, các chương trình như dumpkeys, mã hạt nhân, vv để biết mã bản đồ. Ví dụdumpkeys -l

Dù sao đi nữa:

Chạy như ví dụ:

# ./testprog /dev/input/event2

Mã số:

#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;
}

EDIT 2 (tiếp theo):

Lưu ý rằng nếu bạn nhìn vào /proc/bus/input/devicesbạn có một chữ cái ở đầu mỗi dòng. Ở đây Bcó nghĩa là bản đồ bit. Đó là ví dụ:

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

Mỗi bit đó tương ứng với một thuộc tính của thiết bị. Theo ý nghĩa của bit-map, 1 chỉ ra một thuộc tính, như được định nghĩa trong 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

Có một cái nhìn /drivers/input/input.{h,c}trong cây nguồn kernel. Rất nhiều mã tốt ở đó. (Ví dụ: các thuộc tính của thiết bị được tạo bởi chức năng này .)

Mỗi bản đồ tài sản có thể đạt được bởi ioctl. Ví dụ: nếu bạn muốn kiểm tra các thuộc tính LED nào có sẵn, hãy nói:

ioctl(fd, EVIOCGBIT(EV_LED, sizeof(ledbit)), &ledbit);

Nhìn vào định nghĩa struct input_devtrong input.hlàm thế nào ledbitđược xác định.

Để kiểm tra trạng thái cho đèn LED:

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

Nếu bit 1 in ledbitlà 1 thì num-lock sẽ sáng. Nếu bit 2 là 1 thì nắp khóa được bật sáng, v.v.

input.h có định nghĩa khác nhau.


Lưu ý khi theo dõi sự kiện:

Mã giả để theo dõi có thể là một cái gì đó theo hướng:

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

Một số tài liệu liên quan:

  1. Documentation/input/input.txt, đặc biệt lưu ý phần 5.
  2. Documentation/input/event-codes.txt, mô tả về các sự kiện khác nhau, vv Hãy lưu ý đến những gì được đề cập dưới đây, ví dụ như EV_SYNvềSYN_DROPPED
  3. Documentation/input ... Đọc phần còn lại nếu bạn muốn.

2

Bạn có thể làm điều này một cách dễ dàng bằng cách tham khảo /dev/input/by-id/usb-manufacturername_*serialnumber*. Chúng xuất hiện dưới dạng các liên kết tượng trưng mà bạn có thể sử dụng readlink -eđể xác định thiết bị khối liên quan. Tuy nhiên, các liên kết này được tạo bởi udevcó thể không có trong môi trường nhúng của bạn.

Hoặc .. Nhìn vào dmesgsau khi kết nối thiết bị USB. Nó sẽ cung cấp cho bạn các /devnút.


1
Các mục trong /dev/disk/by-id/imho được tạo bởi udev- câu hỏi là liệu điều này có sẵn trong trường hợp này không (nền tảng nhúng).
peterph

@peterph: Bạn nói đúng. Nếu không sử dụng udev, đề xuất đầu tiên sẽ không hoạt động.
Jeight

@Gilles: Tôi thấy bạn đã chỉnh sửa câu trả lời và thay đổi đường dẫn đến đầu vào chứ không phải đĩa. Trong trường hợp đó tôi tin rằng nó sẽ là đầu vào / theo đường dẫn chứ không phải đĩa / by-id. Tôi nghi ngờ hoặc sẽ làm việc.
Jeight

1
Không, by-idlà chính xác. Ví dụ, bàn phím USB của tôi có sẵn như /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 'SO- ngừng trở nên xấu xa'

@Jeight: Một khi tôi tìm thấy nút thiết bị chính xác, bạn có biết làm thế nào tôi có thể truy cập các phím bấm không?
KyleL
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.