1

i have a cheap USB-RFID-Reader. This reader is a HID-Keyboard (without buttons). I need to capture the output of the reader without writing it to any console. I have found this code here: 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;
}

This code should work. I have it running on my RaspberryPI without problems. I try now to get this code to work on my Android Tablet (with root). But very often i missing letters or the Code is incomplete.

If i write to a text file, all letters are be transfered without problems. But with the code it wont work properly.

What can i do to find out the problem? Is it a timing problem?

Community
  • 1
  • 1
Blondie
  • 23
  • 7

2 Answers2

0

Of course you are occasionally missing events: you read up to 64 event structures, but assume you got exactly two, regardless of rd, the amount read.

You should first verify that you have complete input events ((rd % sizeof (struct input_event)) == 0). If not, you certainly should alert the user, as that's exceedingly rare (as in should never ever occur), and abort.

Then, check how many actual input events you have received. (That would be rd / sizeof (struct input_event).) You cannot rely on the events occurring as pairs, because kernel internal timing will affect things, even if the device reports them in consecutive HID messages. Instead, you need to examine each event separately, and examine each event you read.

Personally, I'd suggest using a finite-state machine for this. Have your outer loop consume entire identifiers. Within the outer loop, initially discard all events except those that may begin a new identifier. Receive those identifiers in an inner loop, ending with a terminating identifier. I'd go as far as read only single events at a time, to make the inner loop simpler. (There are at most a thousand events per second or so, so the extra syscall overhead is completely irrelevant here.)

I have a complete example for barcodes here -- there is very little difference to your use case. Carefully examine the barcode_read() function; it includes a timeout, instead of "hanging" (waiting forever) if a new sequence of input events (for a barcode, a sequence of digits followed by a non-digit) is interrupted in mid-run, say because the reader glitched or something. I'm pretty sure you could easily modify it to work for your use cases.

Community
  • 1
  • 1
Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
  • Many thanks for the help! I have accepted this good answer and posted my changed code as answer (i hope that this is ok so) – Blondie Nov 26 '15 at 18:56
0

In response to the Answer from Nominal Animal i have changed my code to this:

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

I do not really know what I've done here. (Im only a hobby-programmer in vb.net) Essentially i have used the example from Nominal Animal (size_t barcode_read) and removed the timeout. This works perfectly. No more read errors. This is certainly not a good program style...but it works for me.

Many Thank to Nominal Animal for the explanation of the problem and the example!

Blondie
  • 23
  • 7