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

通过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日

悬赏问题

  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效
  • ¥15 悬赏!微信开发者工具报错,求帮改
  • ¥20 wireshark抓不到vlan
  • ¥20 关于#stm32#的问题:需要指导自动酸碱滴定仪的原理图程序代码及仿真
  • ¥20 设计一款异域新娘的视频相亲软件需要哪些技术支持
  • ¥15 stata安慰剂检验作图但是真实值不出现在图上
  • ¥15 c程序不知道为什么得不到结果
  • ¥40 复杂的限制性的商函数处理
  • ¥15 程序不包含适用于入口点的静态Main方法