// Store file object pointer
PIO_STACK_LOCATI0N IrpStack = IoGetNextIrpStackLocation(Irp);
IrpStack->FileObject = FileObject;
// Call the driver and wait for completion if necessary
NTSTATUS status = IoCallDriver(dx->HidDevice, Irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
status = IoStatus.Status;
}
// return IRP completion status
DebugPrint('ReadHidKbdInputReport: status %x', status);
BytesTxd = IoStatus.Information;
return status;
}
Permanently Allocated IRP
A kernel mode HID client is likely to be reading many input reports. Rather than building up a suitable IRP for each call, it is more efficient to have one at the ready all the time. However, this approach is a bit more complicated to set up. The HidKbd driver has this alternative code commented out.
When a HidKbd device is created, it must allocate the IRP that will be reused in all subsequent read and write requests. The SetupHidIrp routine, shown in Listing 23.12, calls IoAllocateIrp to obtain a suitable IRP pointer from the I/O Manager. As IRPs have a variable number of stack locations, SetupHidIrp must pass the desired stack size. The second parameter to IoAllocateIrp should be FALSE for intermediate drivers.
It also makes sense to preallocate a buffer for input and output reports. SetupHidIrp works out the size of buffer needed and allocates it from the nonpaged pool. The final preparatory step is to allocate an MDL for this buffer. Remember that the HID class driver uses Direct I/O and so needs an MDL passed in Read and Write IRPs. The call to
Listing 23.12 SetupHidIrp routine
void SetupHidIrp(IN PHIDKBD_DEVICE_EXTENSION dx, IN CCHAR StackSize) {
// Work out maximum size of input and output reports
dx->HidMaxReportLen = dx->HidInputReportLen;
if (dx->HidOutputReportLen > dx->HidMaxReportLen) dx->HidMaxReportLen = dx->HidOutputReportLen;
DebugPrint('Setting up HidIrp etc %d', dx->HidMaxReportLen);
if( dx->HidMaxReportLen==0) return;
dx->HidReport = ExAllocatePool(NonPagedPool, dx->HidMaxReportLen);
if (dx->HidReport==NULL) return;
dx->HidIrp = IoAlIocateIrp(StackSize, FALSE);
if (dx->HidIrp==NULL) return;
dx->HidReportMdl = IoAllocateMdl(dx->HidReport, dx->HidMaxReportLen, FALSE, FALSE, NULL);
if (dx->HidReportMdl==NULL) {
IoFreeIrp(dx->HidIrp);
dx->HidIrp = NULL;
}
}
When the HidKbd device is removed, the IRP, the buffer memory, and the MDL must be freed. Listing 23.13 shows how the
Listing 23.13 RemoveHidIrp routine
void RemoveHidIrp(IN PHIDKBD_DEVICE_EXTENSION dx) (
DebugPrintMsg('Removing HidIrp etc');
if (dx->HidReportMdl!=NULL) {
IoFreeMdl(dx->HidReportMdl);
dx->HidReportMdl = NULL;
}
if (dx->HidIrp!=NULL) {
IoFreeIrp(dx->HidIrp);
dx->HidIrp = NULL;
}
if (dx->HidReport!=NULL) {
ExFreePool(dx->HidReport);
dx->HidReport = NULL;
}
}
I can now discuss how to use this preallocated IRP. Listing 23.14 shows the replacement
The
Finally,
The
Listing 23.14 New ReadHidKbdInputReport routine
NTSTATUS ReadHidKbdInputReport(PHIDKBD_DEVICE_EXTENSION dx, PFILE_OBJECT FileObject, PVOID Buffer, ULONG& BytesTxd) {
BytesTxd = 0;
if (HidKbdDo==NULL || dx->HidIrp==NULL || dx->HidReport==NULL) {
DebugPrintMsg('No HidIrp');
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(dx->HidReport, dx->HidMaxReportLen);
// Initialise IRP completion event
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
// Initialise IRP
UCHAR AllocationFlags = dx->HidIrp->AllocationFlags;
IoInitializeIrp(dx->HidIrp, IoSizeOfIrp(HidKbdDo->StackSize), HidKbdDo->StackSize);
dx->HidIrp->AllocationFlags = AllocationFlags;
dx->HidIrp->MdlAddress = dx->HidReportMdl;
PIO_STACK_LOCATION IrpStack = IoGetNextIrpStackLocation(dx->HidIrp);
IrpStack->MajorFunction = IRP_MJ_READ;
IrpStack->Parameters.Read.Key = 0;
IrpStack->Parameters.Read.Length = dx->HidInputReportLen;
IrpStack->Parameters.Read.ByteOffset.QuadPart = 0;
IrpStack->FileObject = FileObject;