Chrobo 2022-01-21 11:59 采纳率: 75%
浏览 402
已结题

通过HIDAPI编写主机程序,USB-HID设备能获取SET_REPORT请求,但是无法获取GET_REPORT请求?

问题遇到的现象和发生背景

(天啊,跳转出去没保存,又得重新写一次QAQ)
最近在自学USB-HID协议,公司想把原来需要驱动的USB设备改成HID的,我已经把设备改成HID了,枚举也是成功识别为HID设备。然后用HIDAPI尝试对数据进行收发,在我调用hid_write函数后,设备能正常获取set_report请求,并且也可以获取到主机发送的数据,但是不论怎么调用hid_read函数,设备始终无法收到get_report请求!通过BusHound监测,也无法取得get_report请求,是不是hid_read不会发送get_report请求呢?

问题相关代码

==================主机的测试代码==================

#include <stdio.h>
#include "hidapi.h"

int main(int argc, char **argv)
{
    int res;
    unsigned char buf[65];            //发送数据的缓存
    unsigned char read_buf[65];        //接受数据的缓存
    unsigned short str[32] = { 0 };
    hid_device *handle;                //设备句柄

    /* Set up the command buffer. */
    memset(buf, 0x00, sizeof(buf));

    /* Open the device. */
    /* 获取设备句柄 */
    handle = hid_open(0x5448, 0x0002, str);
    if (handle == 0)
    {
        printf("unable to open device\n");
        return -1;
    }
    
    /* 调用hid_write,向设备发送数据 */

    for (res = 1; res < 65; res++)
    {
        buf[res] = res + 3;
    }

    res = hid_write(handle, buf, 65);
    if (res < 0)
        printf("Unable to write()\n");

    /* Read requested state */
    /* 调用hid_read,获取设备发送的数据 */
    res = hid_read(handle, read_buf, 64);
    if (res < 0)
        printf("Unable to read()\n");

    /* Print out the returned buffer. */
    for (int i = 0; i < 4; i++)
        printf("buf[%d]: %d\n", i, buf[i]);

    return 0;
}

==================配置描述符==================

const u8 DESCRIPTOR_CONFIG[41]= 
{
    //USB configuration descriptor
    0x09,                   //bLength:0x09 bytes
    DT_CONFIGURATION,       //bDescriptorType:configuration
    0x29,0x00,              //wTotalLength:total size
    0x01,                   //bNumInterface:interface num is 1
    0x01,                   //iConfigurationValue:1
    0x00,                   //iConfiguration:no index of configuration string
    0xa0,                   //bmAttributes:D7=1,D=1
    0x03,                   //bMaxPower:maximum power stream 6mA

    //USB interface descriptor
    0x09,                   //bLength:0x09 bytes
    DT_INTERFACE,           //bDescriptorType:interface
    0x00,                   //bInterfaceNumber
    0x00,                   //bAlternateSetting
    0x02,                   //bNumEndpoints
    0x03,                   //bInterfaceClass:HID class 0x03
    0x00,                   //bInterfaceSubClass:custom device
    0x00,                   //bInterfaceProtocol
    0x00,                   //iInterface:no index of string

    //USB-HID descriptor
    0x09,                   //bLength:size of USB-HID descriptor
    DT_HID,                 //bDescriptorType:HID
    0x10,0x01,              //bcdHID:version1.1
    0x00,                   //bCountryCode:no supported
    0x01,                   //bNumDescriptor:subordinate descriptor num
    0x22,                   //bDescriptorType:SubDescriptor is report descriptor
    0x19,0x00,              //wDescriptorLength:size of SubDescriptor

    //USB endpoint descriptor
    0x07,                   //bLength:0x07 bytes
    DT_ENDPOINT,            //bDescriptorType:endpoint
    0x81,                   //bEndpointAddress:D7=1,input;endpoint number 1
    0x03,                   //bmAttributes:0-Control,1-Isochronous,2-Bulk,3-Interrupt
    0x40,0x00,              //wMaxPacktSize:64 bytes
    0x20,                   //bInterval

    0x07,                   //bLength:0x07 bytes
    DT_ENDPOINT,            //bDescriptorType:endpoint
    0x02,                   //bEndpointAddress:D7=0,output;endpoint number 1
    0x02,                   //bmAttributes:0-Control,1-Isochronous,2-Bulk,3-Interrupt
    0x40,0x00,              //wMaxPacktSize:64 bytes
    0x20                    //bInterval
};

==================报表描述符==================

const u8 USB_Report_Descriptor[25] =  //USB报告描述符 25个字节
{
    0x06, 0x00, 0xFF,        //USAGE_PAGE(Vendor—defined)
    0x09, 0x00,             //USAGE(Undefined)
    0xA1, 0x01,                //COLLECTION(Application)
        0x09, 0x00,             //USAGE(Undefined)
        0x15, 0x00,             //Logical Minimum(0)
        0x26, 0xff, 0x00,       //Logical Maximum(255)
        0x75, 0x08,             //Report Size(8)
        0x95, REPORT_COUNT,     //Report Count
        0x81, 0x06,             //Input(Data,Variable,Absolute)
        0x09, 0x00,             //USAGE(Undefined)
        0x91, 0x06,             //Output(Data,Variable,Absolute)
    0xC0,                   //END COLLECTION
};
运行结果

====================BusHound监测设备枚举成功====================

img

电脑已经能识别设备为HID了,所以枚举不存在问题。

====================利用上述测试代码运行结果====================

img

write buf_data success中的第一个数据“00”是报表ID,设备接收到的数据从第二个数据开始(“4”);
input report length是我加在hid_read函数里面的,其位置看下面代码:

int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
{
    return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
}

int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
{
    DWORD bytes_read = 0;
    size_t copy_len = 0;
    BOOL res = FALSE;
    BOOL overlapped = FALSE;

    /* Copy the handle for convenience. */
    HANDLE ev = dev->ol.hEvent;

    if (!dev->read_pending) {
        /* Start an Overlapped I/O read. */
        dev->read_pending = TRUE;
        memset(dev->read_buf, 0, dev->input_report_length);
        printf("input report length:%d", dev->input_report_length);      //添加printf的位置
        ResetEvent(ev);
        res = ReadFile(dev->device_handle, dev->read_buf, (DWORD) dev->input_report_length, &bytes_read, &dev->ol);

        if (!res) {
            if (GetLastError() != ERROR_IO_PENDING) {
                /* ReadFile() has failed.
                   Clean up and return error. */
                CancelIo(dev->device_handle);
                dev->read_pending = FALSE;
                goto end_of_function;
            }
            overlapped = TRUE;
        }
    }
    else {
        overlapped = TRUE;
    }

    if (overlapped) {
        if (milliseconds >= 0) {
            /* See if there is any data yet. */
            res = WaitForSingleObject(ev, milliseconds);
            if (res != WAIT_OBJECT_0) {
                /* There was no data this time. Return zero bytes available,
                   but leave the Overlapped I/O running. */
                return 0;
            }
        }

        /* Either WaitForSingleObject() told us that ReadFile has completed, or
           we are in non-blocking mode. Get the number of bytes read. The actual
           data has been copied to the data[] array which was passed to ReadFile(). */
        res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
    }
    /* Set pending back to false, even if GetOverlappedResult() returned error. */
    dev->read_pending = FALSE;

    if (res && bytes_read > 0) {
        if (dev->read_buf[0] == 0x0) {
            /* If report numbers aren't being used, but Windows sticks a report
               number (0x0) on the beginning of the report anyway. To make this
               work like the other platforms, and to make it work more like the
               HID spec, we'll skip over this byte. */
            bytes_read--;
            copy_len = length > bytes_read ? bytes_read : length;
            memcpy(data, dev->read_buf+1, copy_len);
        }
        else {
            /* Copy the whole buffer, report number and all. */
            copy_len = length > bytes_read ? bytes_read : length;
            memcpy(data, dev->read_buf, copy_len);
        }
    }

====================上述程序运行后BusHound监测的内容====================

img

这里可以看见,设备能正常获取set_report请求,并且完成数据接收,但是看不见get_report请求!

====================串口调试工具监测设备获取到的主机请求====================

img

其中,SETUP_BUFF data就是获取到的请求,在最后一个SETUP_BUFF data中可以看见设备获取到了set_report请求(21 09 00 02 00 00 40 00),RECV_BUFF是获取到的数据,为了不显示那么多,我只显示前10个字节的数据,可见设备能正常获取主机下发的数据,但是这里依然没有获取到get_report请求

我尝试过的方法

====================尝试1====================

首先我想到的是,会不会是hid_write函数影响了hid_read函数(虽然不太可能,但是也尝试了),于是我去除hid_write的调用,再次运行,结果还是没有发生改变

====================运行结果、BusHound监测、串口工具监测====================

img

img

img

很明显,不是hid_write函数的问题。

====================尝试2====================

接下来我想到的是描述符的问题,HID协议规定我们必须有一个控制端点以及一个中断输入端点IN(中断输出OUT端点是可选的),于是我修改了描述符的相关信息,过程如下:
1.删除OUT端点描述符,结果:无变化
2.删除IN端点描述符,结果:无变化
3.将IN和OUT端点描述符都删除,结果:无变化
以上修改都修改了配置描述符对应的长度、端点数量等信息,不存在修改漏洞。
排除描述符的问题。

====================尝试3(还未开始)====================
再次阅读以及查看相关的历程,发现许多历程都可以收到get_report请求,而不通过get_report请求的方式,就是设备通过中断输入端点IN,通过主机轮询来发出数据,那么我的设备很可能就是用这种形式来收发的,但是在“尝试1中”,无论怎样都还是无法收到get_report请求(去除中断IN端点主机是否会通过get_report获取数据?)。主机通过IN发起轮询,我是能在控制端点监测到还是在中断输入端点IN监测到?

我想要达到的结果,以及其他问题

我想要的:我现在只想要让他通过控制端点传输数据啊!现在已经能获取set_report了,但是始终不能获取到get_report请求!我应该怎么做才能获取到get_report?(人麻了)

其他问题:如果是“尝试3”的那种传输方式,我应该怎么去判断主机发送了IN请求?端点0进行判断还是在中断输入端点IN判断?这块我还不是很明白,求解!

  • 写回答

4条回答 默认 最新

  • suqingxiao 2022-01-21 13:38
    关注

    第一,在电脑上有没有感叹号?
    第二,可以先参考别人成熟的设计,因为USB的问题不是简单的问题。
    第三,USB的问题不是一两句话就能解决的,影响的因素很多,只能一点点去定位问题点。
    第四,你的问题即使是熟悉USB的工程师也不一定知道问题。只能具体到哪个位置那个函数可能有问题。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
查看更多回答(3条)

报告相同问题?

问题事件

  • 系统已结题 2月8日
  • 已采纳回答 1月31日
  • 创建了问题 1月21日

悬赏问题

  • ¥15 seatunnel-web使用SQL组件时候后台报错,无法找到表格
  • ¥15 fpga自动售货机数码管(相关搜索:数字时钟)
  • ¥15 用前端向数据库插入数据,通过debug发现数据能走到后端,但是放行之后就会提示错误
  • ¥30 3天&7天&&15天&销量如何统计同一行
  • ¥30 帮我写一段可以读取LD2450数据并计算距离的Arduino代码
  • ¥15 飞机曲面部件如机翼,壁板等具体的孔位模型
  • ¥15 vs2019中数据导出问题
  • ¥20 云服务Linux系统TCP-MSS值修改?
  • ¥20 关于#单片机#的问题:项目:使用模拟iic与ov2640通讯环境:F407问题:读取的ID号总是0xff,自己调了调发现在读从机数据时,SDA线上并未有信号变化(语言-c语言)
  • ¥20 怎么在stm32门禁成品上增加查询记录功能