Отчеты USB HID OUT - какая конечная точка правильная?

У нас есть встроенное устройство на основе CC2531 от TI, которое имеет (помимо управляющего EP0 и ряда конечных точек только для IN) конечную точку, которая одновременно является IN и OUT. Мы заметили разницу в том, как Windows отправляет отчеты OUT и как это делает Linux. На самом деле это довольно долгое время поражало нас, но мы так и не смогли найти объяснения.

Мне кажется, что linux делает это так, как должно быть: отчет OUT передается через конечную точку, которая связана с отчетом HID, как мы получаем его из libusb:

Item             | Dev | EP | Status | Speed |Payload
-----------------+-----+----+--------+-------+-------------------------------
OUT transaction  | 13  | 4  |  ACK   |  FS   | 64 bytes (90 13 00 00 00 00 ..

Windows, с другой стороны, отправляет его через контрольную конечную точку (EP0). Мы используем API настройки, чтобы найти устройство с нужным нам использованием, открыть его для IN и OUT и использовать один и тот же дескриптор файла для чтения и записи. Отчеты EP4 IN хорошо принимаются через этот файловый дескриптор, но запись отчета через тот же файловый дескриптор заканчивается на EP0:

Item               | Dev | EP | Status | Speed |Payload
-------------------+-----+----+--------+-------+-------------------------------
Class request OUT  | 25  | 0  |   OK   |  FS   | 64 bytes (90 13 00 00 00 00 ..

(извините, не могу публиковать изображения (пока). Я скопировал отчеты Ellisys вручную)

Встроенное устройство не проверяет, на каком EP получен отчет OUT (т.е. отчеты SET на EP0 будут выполнять ту же функцию, что и отчеты OUT, найденные на других конечных точках при обработке событий HID), поэтому оно будет реагировать в любом случае.

Мой вопрос: правильны ли оба способа, и если нет, то какой правильный, а какой нет? Может быть, ошибка в наших дескрипторах вызывает такое поведение в Windows?

Для полноты: вот наш дескриптор: http://tny.cz/ac745a8f (удалено из идентификации поставщика на держи моего босса счастливым :))

Увеличение отчета в окнах: (Радость! Теперь я могу делать снимки :))

введите описание изображения здесь

Вся сделка:

введите описание изображения здесь

Используемые библиотеки в Windows: hid.lib, hidclass.lib и setupapi.lib. При написании отчета мы используем функции HidP_SetUsageValueArray и HidD_SetOutputReport. PHIDP_PREPARSED_DATA и HIDP_CAPS находятся с функциями HidD_GetAttributes, HidD_GetPreparsedData и HidP_GetCaps. Путь к файлу для устройства находится с помощью SetupDiEnumDeviceInterfaces. Если мы найдем устройство с правильным VID, PID, caps.UsagePage и caps.Usage, то это устройство, которое мы используем.

В Linux это немного сложнее, так как я не тот, кто реализовал код Linux. Я могу сказать, что используется libusb-1.0.9, устройство открывается с помощью libusb_open_device_with_vid_pid, отчеты отправляются с помощью libusb_fill_interrupt_transfer и libusb_submit_transfer. Я вижу, что libuwand_fill_interrupt_transfer принимает конечную точку в качестве параметра, поэтому я думаю, что с помощью только дескриптора из libusb_open_device_with_vid_pid и передачи правильного параметра в качестве конечной точки libusb выяснит, куда поместить отчет.


person Henk Kok    schedule 10.02.2015    source источник
comment
Конечная точка 0 зарезервирована для передачи управления, поэтому перед полезной нагрузкой должен быть отправлен 8-байтовый пакет SETUP с такими параметрами, как bRequestType, bRequest, wValue и т.п. Не могли бы вы опубликовать значения этого пакета SETUP? Эта информация позволит нам заглянуть в спецификацию HID и увидеть, какой запрос отправляет Windows, и является ли он действительным. Кроме того, какие стеки драйверов / библиотек вы используете для отправки данных в Linux и Windows? Конечная точка, которую использует Windows, может зависеть от драйвера (например, hidusb.sys) и библиотеки, которую вы используете для взаимодействия с драйвером (например, libusb), и вашего кода.   -  person David Grayson    schedule 11.02.2015
comment
Встроенное устройство не проверяет, для какого EP получен отчет OUT: для меня это звучит необычно. Как это работает? Структура передачи управления, отправляемой в конечную точку 0, намного сложнее, чем данные в обычном прерывании или групповой конечной точке, поэтому вам понадобится специальный код для обработки данных конечной точки 0 в первую очередь.   -  person David Grayson    schedule 11.02.2015


Ответы (1)


Думаю, я нашел ответ.

чисто случайно я наткнулся на форум Keil, где я нашел утверждение «HidD_SetOutputReport будет использовать контрольную конечную точку, если вы хотите, чтобы это происходило через другую конечную точку, используйте WriteFile». Я знаю, что более 5 лет назад я пробовал этот путь, но застрял в асинхронном вводе-выводе с перекрывающимися структурами. Поскольку оказалось, что у меня есть выход (с помощью HidD_SetOutputReport), я отказался от пути WriteFile. Итак, пришло время снова искать этот путь, и я сделал это. Код:

res = HidD_SetOutputReport(m_DeviceControl[dev], report, m_CapsControl[dev].OutputReportByteLength);

был заменен на

                DWORD bytesWritten;
                OVERLAPPED eventWrite = {0};

                eventWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

                int rv3 = WriteFile(m_DeviceControl[dev], report, m_CapsControl[dev].OutputReportByteLength, &bytesWritten, &eventWrite);
                if (rv3 == 0)
                {
                    int err = GetLastError();

                    if (err == ERROR_IO_PENDING)
                    {
                        bool done = false;

                        do
                        {
                            // yes. Wait till pending state has gone
                            rv3 = WaitForSingleObject(eventWrite.hEvent, 25);
                            if (rv3 == WAIT_OBJECT_0)
                            {
                                GetOverlappedResult(m_DeviceControl[dev], &eventWrite, &bytesWritten, FALSE);
                                done = true;
                                res = TRUE;
                            }
                            else if (rv3 == WAIT_TIMEOUT)
                            {
                                // Need to try again.
                            }
                            else
                            {
                                m_StoppingControlOut = true;
                                done = true;
                            }
                        }
                        while (!done && !m_StoppingControlOut);
                    }
                }
            }

и это заставляет запрос проходить через надлежащую конечную точку.

Поэтому я прихожу к следующему выводу:

  • Я считаю неправильным то, что HID-устройство интерпретирует отчеты OUT, отправленные через контрольную конечную точку.
  • Правильное использование WriteFile (с перекрывающимся вводом-выводом) заставляет отчеты OUT использовать правильную конечную точку.
person Henk Kok    schedule 24.03.2015