HID多ReportId数据长度返回的问题
最近搞了一个虚拟的USB HID设备,为了测试各个报告描述符。不过遇到了一个奇怪的问题,以前没有留意,现在分享给大家。
比如在自定义的HID的报告描述符中,描述输入的报告内容分别如下:
ReportId | 数据长度 | 总长度 |
---|---|---|
01 | 16 | 17 |
02 | 60 | 61 |
可以看到,当ReportId=0x01时,加上其payload data的16字节,总计17长度。
而当ReportId=0x01时,加上其payload data的60字节,总计61长度。
在Windows应用层,我们通过ReadFile来读取报告内容。
不过在实际的驱动中,发现:
HID输入报告的读取原理
只有驱动正常创建,hidclass类驱动会对该设备下发两个连续的IRP来请求输入报告,无关是否在应用层有打开该设备读取数据。如果固件没有数据返回,这两个IRP处于Pending状态,直到有数据返回或者该IRP被取消掉。
这个问题其实很好理解,由于HID是中断输入请求,为了保证数据的实时性,在驱动中实际是上将从固件中读到的数据放到其缓存队列中的,当应用层的有读数据请求时,会从缓存的数据中取到数据,并返回给应用层,否则就是等待有数据返回,或者上层主动调用CancelIo取消本次请求。
HID输入报告的读取长度
这是我这次遇到的一个奇怪的问题。
就是如果该HID的多个ReportId并且数据长度不一致时,这时主机会使用最长的报告长度来获取据(本人测试的结果,并不一一定准确),当按实际的数据长度返回时,发现应用层返回的还是最大的长度。
如本人对PENDING起的IRP这样处理:
PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(pPendingIrp);
PURB Urb = (PURB)IoStack->Parameters.Others.Argument1;
ULONG length = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
ULONG copy = min(length, ulInputLength);
PVOID buffer = Urb->UrbBulkOrInterruptTransfer.TransferBuffer;
RtlCopyMemory(buffer, pInputReport, copy);
Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = copy;
Urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
pPendingIrp->IoStatus.Information = copy;
pPendingIrp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(pPendingIrp, IO_NO_INCREMENT);
实际在应用层HID返回的长度还是最大长度即实际的请求长度,而本人已经修改了长度
Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = copy;
这是我一个很迷或的问题,本人在查ReactOS的源代码HidUsb_ReadReportCompletion时,有这么一句:
//
// did the reading report succeed / cancelled
//
if (NT_SUCCESS(Irp->IoStatus.Status) || Irp->IoStatus.Status == STATUS_CANCELLED || Irp->IoStatus.Status == STATUS_DEVICE_NOT_CONNECTED)
{
//
// store result length
//
Irp->IoStatus.Information = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
这个应该没问题的啊,其实Windows的源代码也是这样的。不过不知道为什么我这里测试了出一个这样的问题。
纠结了半天,在IRP_MJ_READ转IOCTL_HID_READ_REPORT的完成例程HidClass_ReadCompleteIrp时,发现这样的
//
// copy result status
//
IrpContext->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status;
IrpContext->OriginalIrp->IoStatus.Information = Irp->IoStatus.Information;
这个长度是从IOCTL_HID_READ_REPORT的长度传给IRP_MJ_READ的。不过实际在Windows测试却是另不一样的。
至于原因,暂不清楚,这里记一下,后续再分析。
不过数据内容都是对的,。。。