【问题标题】:Failed to read a USB HID RFID-Reader with C无法使用 C 读取 USB HID RFID 阅读器
【发布时间】:2015-11-25 20:11:03
【问题描述】:

我有一个便宜的 USB-RFID 阅读器。这个阅读器是一个 HID 键盘(没有按钮)。 我需要捕获阅读器的输出而不将其写入任何控制台。 我在这里找到了这段代码: https://stackoverflow.com/a/7672324/4500123

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <termios.h>
#include <signal.h>

int main(int argc, char* argv[])
{
    struct input_event ev[64];
    int fevdev = -1;
    int result = 0;
    int size = sizeof(struct input_event);
    int rd;
    int value;
    char name[256] = "Unknown";
    char *device = "/dev/input/event3";


    fevdev = open(device, O_RDONLY);
    if (fevdev == -1) {
        printf("Failed to open event device.\n");
        exit(1);
    }

    result = ioctl(fevdev, EVIOCGNAME(sizeof(name)), name);
    printf ("Reading From : %s (%s)\n", device, name);

    printf("Getting exclusive access: ");
    result = ioctl(fevdev, EVIOCGRAB, 1);
    printf("%s\n", (result == 0) ? "SUCCESS" : "FAILURE");

   while (1)
    {
        if ((rd = read(fevdev, ev, size * 64)) < size) {
            break;
        }

        value = ev[0].value;

        if (value != ' ' && ev[1].value == 1 && ev[1].type == 1) {
            printf ("Code[%d]\n", (ev[1].code));
        }
    }

    printf("Exiting.\n");
    result = ioctl(fevdev, EVIOCGRAB, 1);
    close(fevdev);
    return 0;
}

这段代码应该可以工作。我让它在我的 RaspberryPI 上运行没有问题。 我现在尝试让这段代码在我的 Android 平板电脑(使用 root)上运行。但我经常会遗漏字母或代码不完整。

如果我写入一个文本文件,所有字母都可以毫无问题地传输。但是使用代码它不会正常工作。

我该怎么做才能找出问题所在?是时间问题吗?

【问题讨论】:

    标签: android c linux input hid


    【解决方案1】:

    当然,您偶尔会丢失事件:您读取了多达 64 个事件结构,但假设您恰好得到了两个,无论 rd 读取的数量是多少。

    您应该首先确认您有完整的输入事件 ((rd % sizeof (struct input_event)) == 0)。如果没有,您当然应该提醒用户,因为这种情况极为罕见(因为永远不应该发生),然后中止。

    然后,检查您收到了多少实际输入事件。 (那将是rd / sizeof (struct input_event)。)您不能依赖成对发生的事件,因为内核内部时序会影响事物,即使设备在连续的 HID 消息中报告它们。相反,您需要分别检查每个事件,并检查您阅读的每个事件。

    就个人而言,我建议为此使用finite-state machine。让您的外循环使用整个标识符。在外循环中,最初丢弃所有事件,除了那些可能开始一个新标识符的事件。在内部循环中接收这些标识符,以终止标识符结束。我会尽可能一次只读取单个事件,以使内部循环更简单。 (每秒最多有一千个事件左右,所以额外的系统调用开销在这里完全无关紧要。)

    我有一个完整的条形码示例here——与您的用例几乎没有区别。仔细检查barcode_read() 函数;它包括一个超时,而不是“挂起”(永远等待)如果一个新的输入事件序列(对于条形码,一个数字序列后跟一个非数字)在运行中被中断,比如因为阅读器出现故障或某物。我很确定您可以轻松修改它以适用于您的用例。

    【讨论】:

    • 非常感谢您的帮助!我已经接受了这个很好的答案并将我更改的代码作为答案发布(我希望这样可以)
    【解决方案2】:

    为了响应 Nominal Animal 的回答,我已将代码更改为:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <dirent.h>
    #include <linux/input.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/select.h>
    #include <sys/time.h>
    #include <termios.h>
    #include <signal.h>
    
    #define BARCODE_MAXLEN  1023
    
    
    size_t barcode_read(int fd,
                        char *const buffer, const size_t length)
    {
        size_t len = 0;
        int status = ETIMEDOUT;
    
        if (!buffer || length < 2 ) {
            //errno = EINVAL;
            return (size_t)0;
        }
    
    
        while (1) {
            struct input_event ev;
            ssize_t n;
            int digit;
    
            n = read(fd, &ev, sizeof ev);
            if (n == (ssize_t)-1) {
                if (errno == EINTR)
                    continue;
                status = errno;
                break;
    
            } else
            if (n == sizeof ev) {
    
                /* We consider only key presses and autorepeats. */
                if (ev.type != EV_KEY || (ev.value != 1 && ev.value != 2))
                    continue;
    
                switch (ev.code) {
                case KEY_0: digit = '0'; break;
                case KEY_1: digit = '1'; break;
                case KEY_2: digit = '2'; break;
                case KEY_3: digit = '3'; break;
                case KEY_4: digit = '4'; break;
                case KEY_5: digit = '5'; break;
                case KEY_6: digit = '6'; break;
                case KEY_7: digit = '7'; break;
                case KEY_8: digit = '8'; break;
                case KEY_9: digit = '9'; break;
                default:    digit = '\0';
                }
    
                /* Non-digit key ends the code, except at beginning of code. */
                if (digit == '\0') {
                    if (!len)
                        continue;
                    status = 0;
                    break;
                }
    
                if (len < length)
                    buffer[len] = digit;
                len++;
    
                continue;
    
            } else
            if (n == (ssize_t)0) {
                status = ENOENT;
                break;                
    
            } else {
                status = EIO;
                break;
            }
        }
    
        /* Add terminator character to buffer. */
        if (len + 1 < length)
            buffer[len + 1] = '\0';
        else
            buffer[length - 1] = '\0';
    
        errno = status;
        return len;
    }
    
    int main(int argc, char* argv[])
    {
        struct input_event ev[64];
        int fevdev = -1;
        int result = 0;
        int size = sizeof(struct input_event);
        int rd;
        int value;
        char name[256] = "Unknown";
        char *device = "/dev/usb/input1-1.4";
    
        fevdev = open(device, O_RDONLY);
        if (fevdev == -1) {
            printf("Failed to open event device.\n");
            exit(1);
        }
    
        result = ioctl(fevdev, EVIOCGNAME(sizeof(name)), name);
        printf ("Reading From : %s (%s)\n", device, name);
    
        printf("Getting exclusive access: ");
        result = ioctl(fevdev, EVIOCGRAB, 1);
        printf("%s\n", (result == 0) ? "SUCCESS" : "FAILURE");
    
        while (1) {
            char    code[BARCODE_MAXLEN + 1];
            size_t  len;
    
            //if (done) {
            //    status = EINTR;
            //    break;
            //}
    
            len = barcode_read(fevdev, code, sizeof code);
            if (errno) {
                //status = errno;
                break;
            }
            if (len < (size_t)1) {
                //status = ETIMEDOUT;
                break;
            }
    
            printf("%zu-digit barcode: %s\n", len, code);
            fflush(stdout);
        }
    
        printf("Exiting.\n");
        result = ioctl(fevdev, EVIOCGRAB, 1);
        close(fevdev);
        return 0;
    }
    

    我真的不知道我在这里做了什么。 (我只是 vb.net 中的一个爱好程序员) 本质上,我使用了 Nominal Animal (size_t barcode_read) 中的示例并删除了超时。 这完美地工作。没有更多的读取错误。 这当然不是一个好的程序风格……但它对我有用。

    非常感谢 Nominal Animal 对问题的解释和示例!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-03
      • 1970-01-01
      • 1970-01-01
      • 2021-03-23
      • 1970-01-01
      • 1970-01-01
      • 2021-06-09
      • 1970-01-01
      相关资源
      最近更新 更多