无法使用 C 读取 USB HID RFID-Reader
Failed to read a USB HID RFID-Reader with C
我有一个便宜的 USB-RFID-Reader。这个 reader 是一个 HID 键盘(没有按钮)。
我需要捕获 reader 的输出而不将其写入任何控制台。
我在这里找到了这段代码:
#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 权限)上运行。但是我经常漏掉字母或者代码不完整。
如果我写入文本文件,所有字母都可以毫无问题地传输。但是使用代码无法正常工作。
我该怎么做才能找出问题所在?是时间问题吗?
当然,您偶尔会遗漏事件:您最多读取了 64 个事件结构,但假设您恰好有两个,无论 rd
、读取的数量如何。
您应该首先确认您有完整的输入事件 ((rd % sizeof (struct input_event)) == 0
)。如果没有,您当然应该提醒用户,因为这种情况非常罕见(永远不会发生),然后中止。
然后,检查您收到了多少实际输入事件。 (那将是 rd / sizeof (struct input_event)
。)您 不能 依赖成对发生的事件,因为内核内部时序会影响事物,即使设备在连续的 HID 消息中报告它们。相反,您需要分别检查每个事件,并检查您阅读的每个事件。
就我个人而言,我建议为此使用 finite-state machine。让您的外循环使用整个标识符。在外循环中,最初丢弃所有事件,但可能开始新标识符的事件除外。在内部循环中接收这些标识符,以终止标识符结束。我会尽量一次只读取单个事件,以使内部循环更简单。 (每秒最多有一千个事件左右,因此额外的系统调用开销在这里完全不相关。)
我有一个完整的条形码示例 -- 与您的用例差别不大。仔细检查 barcode_read()
函数;它包括一个超时,而不是 "hanging"(永远等待)如果一个新的输入事件序列(对于条形码,一个数字序列后跟一个非数字)在 运行 中被中断,说是因为 reader 出现故障或其他原因。我很确定您可以轻松修改它以适用于您的用例。
为了回应 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 = '[=10=]';
}
/* Non-digit key ends the code, except at beginning of code. */
if (digit == '[=10=]') {
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] = '[=10=]';
else
buffer[length - 1] = '[=10=]';
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 对问题的解释和示例!
我有一个便宜的 USB-RFID-Reader。这个 reader 是一个 HID 键盘(没有按钮)。 我需要捕获 reader 的输出而不将其写入任何控制台。 我在这里找到了这段代码:
#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 权限)上运行。但是我经常漏掉字母或者代码不完整。
如果我写入文本文件,所有字母都可以毫无问题地传输。但是使用代码无法正常工作。
我该怎么做才能找出问题所在?是时间问题吗?
当然,您偶尔会遗漏事件:您最多读取了 64 个事件结构,但假设您恰好有两个,无论 rd
、读取的数量如何。
您应该首先确认您有完整的输入事件 ((rd % sizeof (struct input_event)) == 0
)。如果没有,您当然应该提醒用户,因为这种情况非常罕见(永远不会发生),然后中止。
然后,检查您收到了多少实际输入事件。 (那将是 rd / sizeof (struct input_event)
。)您 不能 依赖成对发生的事件,因为内核内部时序会影响事物,即使设备在连续的 HID 消息中报告它们。相反,您需要分别检查每个事件,并检查您阅读的每个事件。
就我个人而言,我建议为此使用 finite-state machine。让您的外循环使用整个标识符。在外循环中,最初丢弃所有事件,但可能开始新标识符的事件除外。在内部循环中接收这些标识符,以终止标识符结束。我会尽量一次只读取单个事件,以使内部循环更简单。 (每秒最多有一千个事件左右,因此额外的系统调用开销在这里完全不相关。)
我有一个完整的条形码示例 barcode_read()
函数;它包括一个超时,而不是 "hanging"(永远等待)如果一个新的输入事件序列(对于条形码,一个数字序列后跟一个非数字)在 运行 中被中断,说是因为 reader 出现故障或其他原因。我很确定您可以轻松修改它以适用于您的用例。
为了回应 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 = '[=10=]';
}
/* Non-digit key ends the code, except at beginning of code. */
if (digit == '[=10=]') {
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] = '[=10=]';
else
buffer[length - 1] = '[=10=]';
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 对问题的解释和示例!