2015-11-25 1 views
1

У меня есть дешевый USB-RFID-Reader. Этот читатель является HID-клавиатурой (без кнопок). Мне нужно захватить вывод читателя, не записывая его на любую консоль. Я нашел этот код здесь: https://stackoverflow.com/a/7672324/4500123Не удалось прочитать USB-HID RFID-Reader с C

#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 (с корнем). Но очень часто я пропускаю буквы или код неполный.

Если я пишу в текстовый файл, все письма передаются без проблем. Но с кодом он будет работать неправильно.

Что я могу сделать, чтобы узнать о проблеме? Это проблема времени?

ответ

0

Конечно, вы иногда пропускаете события: вы читаете до 64 структур событий, но предположим, что у вас есть ровно два, независимо от rd, количество прочитанного.

Прежде всего, вы должны убедиться, что у вас есть полные входные события ((rd % sizeof (struct input_event)) == 0). Если нет, вы наверняка должны предупредить пользователя, так как это чрезвычайно редко (как никогда должно происходить) и прервать.

Затем проверьте, сколько фактических событий ввода вы получили. (Это будет rd/sizeof (struct input_event).) Вы не можете полагаться на события, происходящие как пары, потому что внутреннее время ядра влияет на вещи, даже если устройство сообщает о них в последовательных HID-сообщениях. Вместо этого вам нужно изучить каждое событие отдельно и изучить каждое прочитанное событие.

Лично я бы предложил использовать для этого finite-state machine. Попросите свой внешний цикл использовать целые идентификаторы. Внутри внешнего цикла сначала отбрасывать все события, кроме тех, которые могут начать новый идентификатор. Получите эти идентификаторы во внутреннем цикле, заканчивая завершающим идентификатором. Я бы зашел так далеко, чтобы читать только отдельные события за раз, чтобы сделать внутренний цикл проще. (Существует не более тысячи событий в секунду или около того, поэтому дополнительные служебные данные для системного вызова совершенно неактуальны здесь.)

У меня есть полный пример для штрих-кодов here - разница в вашем случае использования очень мало. Внимательно изучите функцию barcode_read(); он включает в себя тайм-аут вместо «зависания» (ожидание навсегда), если новая последовательность входных событий (для штрих-кода, последовательность цифр, за которой следует незначащая цифра) прерывается в середине, скажем, потому что читатель захлопнулся или что нибудь. Я уверен, что вы можете легко изменить его, чтобы работать для ваших случаев использования.

+0

Большое спасибо за помощь! Я принял этот хороший ответ и отправил измененный код в качестве ответа (я надеюсь, что все в порядке) – Blondie

0

В ответ на ответ от номинальных животных я изменил свой код к этому:

#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) и удалил таймаут. Это прекрасно работает. Больше ошибок чтения. Это, конечно, не очень хороший программный стиль ... но он работает для меня.

Многие благодарны Номинальному животному за объяснение проблемы и примера!