USB HID OUT 报告 - 哪个端点是正确的?

问题描述 投票:0回答:2

我们有一个基于 TI CC2531 的嵌入式设备,它具有(除了控制 EP0 和许多仅 IN 端点之外)一个既是 IN 又是 OUT 的端点。我们注意到 Windows 发送 OUT 报告的方式与 Linux 发送 OUT 报告的方式不同。这实际上让我们困惑了一段时间,但我们一直找不到解释。

在我看来,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 报告(即,处理 HID 事件时,EP0 上的 SET 报告将汇集到与在其他端点上找到的 OUT 报告相同的功能),因此它会以任何一种方式响应。

我的问题是:这两种方法都正确吗?如果不正确,哪种方法正确,哪种方法错误?难道我们的描述符中的错误会在 Windows 上触发此行为吗?

为了完整起见:这是我们的描述符:http://tny.cz/ac745a8f(从供应商标识中删除,以使我的老板高兴:))

在 Windows 上放大报告:(高兴!我现在可以拍照了:))

enter image description here

整个交易:

enter image description here

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 就会找出将报告放在哪里。

linux windows usb endpoint hid
2个回答
3
投票

我想我已经找到答案了。

纯属巧合,我偶然发现了 Keil 论坛,在那里我发现了这样的说法:“HidD_SetOutputReport 将使用控制端点,如果您希望通过另一个端点,请使用 WriteFile”。我知道 5 多年前我曾尝试过这条路,但我陷入了具有重叠结构的异步 IO 中。既然看起来我有一条出路(使用 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(使用重叠 IO)使 OUT 报告使用正确的端点。

0
投票

HID 规范允许在没有 OUT 端点的情况下通过控制端点向设备传输数据。最后,它取决于 USB 描述符,如果有替代配置,还可能取决于操作系统和驱动程序。这意味着设备上的 USB 堆栈应该支持两种方式,并且很容易被操作系统开发人员忽视,因为无论数据发送方式如何,它都可以工作。

© www.soinside.com 2019 - 2024. All rights reserved.