无法用C读取USB HID RFID阅读器

时间:2015-11-25 20:11:03

标签: android c linux input hid

我有一个便宜的USB-RFID-Reader。这个阅读器是一个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)。但我常常丢失字母或代码不完整。

如果我写入文本文件,所有字母都可以毫无问题地转移。但是使用代码它无法正常工作。

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

2 个答案:

答案 0 :(得分:0)

当然,您偶尔会遗漏一些事件:您最多可以阅读64个事件结构,但假设您只有两个,无论rd是多少,都会读取。

您应首先验证您是否有完整的输入事件((rd % sizeof (struct input_event)) == 0)。如果没有,你当然应该提醒用户,因为这非常罕见(从未发生过),并且中止。

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

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

我有条形码here的完整示例 - 您的用例几乎没有什么区别。仔细检查barcode_read()函数;它包括一个超时,而不是“挂起”(永远等待)如果一个新的输入事件序列(对于一个条形码,一个数字序列后面跟一个非数字)在中途运行中断,比如因为读者发生了故障或一些东西。我很确定你可以很容易地修改它以适应你的用例。

答案 1 :(得分:0)

在回应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解释问题和示例!