HID设备空闲态的状态检测
在HIDCLASS.SYSS中使用一个一个周期为1秒间隔的定时器来周期性地检查设备空闲检测。
#define HID_IDLE_SCAN_INTERVAL 1
scanTime = RtlConvertLongToLargeInteger(-10*1000*1000 * HID_IDLE_SCAN_INTERVAL);
KeSetTimerEx(&idleTimer,
scanTime,
HID_IDLE_SCAN_INTERVAL*1000, // call wants milliseconds
&idleTimerDpc);
当然,这里的1秒并不是说明设备1秒,而是检查的周期间隔为1秒,而空闲的时长可由驱动指定,这里指定为5,所以也就是说5秒。
当检测到了5次即5䚱时,由HIDCLASS.SYS发送一个IRP到HUB,再由HUB驱动程序的端口进行设置。
而执行的IRP为IRP_MJ_INTERNAL_DEVICE_CONTROL,控制码为IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST,不过在下发该IRP时,会传递一个回调函数及上下文信息组成的结构体指针,使用的的是InputBufferLength和Type3InputBuffer。
stack = IoGetNextIrpStackLocation(irp);
stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST;
stack->Parameters.DeviceIoControl.InputBufferLength = sizeof(fdoExt->idleCallbackInfo);
stack->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID) &(fdoExt->idleCallbackInfo);
//
// Hook a completion routine for when the device completes.
//
IoSetCompletionRoutine(irp,
HidpIdleNotificationRequestComplete,
DeviceObject->DeviceExtension,
TRUE,
TRUE,
TRUE);
当然,HIDCLASS.SYS发送该IRP,也需要知道什么时候该IRP完成了,故需要设置一个完成例程。
HIDCLASS.SYS下发的该IRP会首先发给hidusb.sys,hidusb.sys进行转发,不过由于hidusb.sys和hidclass.sys复杂的关系,故不会像别的驱动栈那样直接IoSkipCurrentStactionIrp,再IoCallDriver等。
而hidusb.sys会将IOCTL码变为IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION。
next->MajorFunction = current->MajorFunction;
next->Parameters.DeviceIoControl.InputBufferLength = current->Parameters.DeviceIoControl.InputBufferLength;
next->Parameters.DeviceIoControl.Type3InputBuffer =
current->Parameters.DeviceIoControl.Type3InputBuffer;
next->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;
next->DeviceObject = GET_NEXT_DEVICE_OBJECT(DeviceObject);
return IoCallDriver(GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp);
hidusb.sys转发的IPR最终由HUB驱动的USBPORT_PdoDeviceControl函数处理,详细的可见ReactOS源代码:https://doxygen.reactos.org/d6/d69/usbhub_8c_source.html#l03722 可见实际的处理函数为USBPORT_IdleNotification。
USBH_PortIdleNotificationRequest的处理比较简单,就是将该IRP挂起,并保存起来,设置端口标识USBHUB_PDO_FLAG_IDLE_NOTIFICATION。
当然,关于该IRP的完成,会出现多种状态,如返回不支持空闲态检测,应取消空闲态求。
大概看了REACTOS的源代码,设备的IDLE请求与HUB的IDLE有关,至于细节,本人确实无精力研究。。。