问题遇到的现象和发生背景
(天啊,跳转出去没保存,又得重新写一次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监测设备枚举成功====================
电脑已经能识别设备为HID了,所以枚举不存在问题。
====================利用上述测试代码运行结果====================
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监测的内容====================
这里可以看见,设备能正常获取set_report请求,并且完成数据接收,但是看不见get_report请求!
====================串口调试工具监测设备获取到的主机请求====================
其中,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监测、串口工具监测====================
很明显,不是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判断?这块我还不是很明白,求解!