Linux на RPi debian, hidraw write() на USB-устройство выводит несколько ненужных символов в /dev/hidraw0, которые, если их не очистить, заклинивают устройство

#linux #usb #polling #hid #write

#c #linux #usb #опрос #hid

Вопрос:

У нас есть набор USB-устройств, которые мы отслеживаем с помощью RPi. Код мониторинга опрашивает устройства, использующие интерфейс hidraw direct, примерно раз в секунду. Протокол использует 64-байтовые пакеты для отправки команд и получения данных, и все ответы имеют длину не более 64 байт.

Та же схема отлично работает под Windows с использованием драйвера Windows HID. Однако в Linux мы используем hidraw и обнаруживаем, что интерфейс устройства через короткое время заклинивается, что приводит к неудачной записи {} на устройство.

После долгих исследований я наткнулся на рекомендацию попытаться отслеживать связь между хостом и устройством hidraw, используя это в терминале:

   sudo cat /dev/hidraw0
 

Как выясняется, выполнение этой команды выводит на терминал 4-8 байт нечитаемых символов каждый write() , и неожиданно это также устраняет замятие hidraw0 . Все последующие write() ‘s и read() ‘s для этого устройства работают безупречно.

Если это устройство отключено, а затем повторно подключено, состояние замятия возвращается вскоре после этого. Я сделал один шаг кода и проверил, что «мусор» выводится во время выполнения write() .

Я пытался добавлять fsync() вызовы до и после write() в надежде очистить буферы и избежать этой проблемы, но это не помогло. Код для write() и последующих read() является стандартным следующим образом:

 #define USB_PACKET 64
#define USB_WRDELAY 10 //ms

FILE* fd; 
int errno, res;
char packet[USB_PACKET];
fd = 0;

/* Open the Device with non-blocking reads. */
fd = open("/dev/hidraw0", O_RDWR|O_NONBLOCK);

if (fd < 0) {
    perror("Unable to open device");
    return 0;    // failure
}
memset(packet, 0x0, sizeof(packet));
packet[0] = 0x34;   // command code - request for USB device status bytes
fsync();
res = write(fd, amp;packet, sizeof(packet));
fsync();
if (res < 0) {
    printf("Error: %d in USB write()n", errno);
    close(fd);
    return 0;   // failure
} else {
    usleep(1000*USB_WRDELAY );      // delay gives OS and device time to respond
    res = read(fd, amp;packet, sizeof(packet));
    
    if (res < 0) {
        printf("Error: %d in USB read()n", errno);
        close(fd);
        return 0; // failure 
    } else {
        // good read, packet holds the response data
        // process the device data
        close(fd); 
        return 1; // OK
    }
 }  
 return 0; // failure
   
 

Это пример тарабарщины, которую мы читаем на терминале, выполняющем cat команду для каждого выполняемого write() :

     4n��@/5

Я не понимаю, откуда берется этот мусор и как от него избавиться. Я попробовал несколько вещей, которые не сработали, например, добавление a read() с таймаутом перед записью — надеясь, что это какие-то данные, оставшиеся от предыдущего неполного read() .

Также пытался записать буфер меньшего размера, так как мне нужно отправить только 2-байтовую команду, а также добавить задержку между open() и write() .

К сожалению, использование cat в терминале мешает обнаружению USB-устройств при горячем подключении / отключении, поэтому это не решение, которое мы можем использовать при развертывании.

Я буду признателен за любые мудрые слова по этому поводу.