// 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 IoAllocateMdl makes a suitable MDL out of the buffer pointer.

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 RemoveHidIrp routine does this job using the IoFreeMdl, IoFreeIrp, and ExFreePool routines.

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 ReadHidKbdInputReport routine. This time, it cannot use IoBuildSynchronousFsdRequest, so the IRP and its stack must be built by hand.

The IoInitializeIrp call is used to initialize the IRP. IoInitializeIrp incorrectly clears the IRP AllocationFlags field, so this must be preserved. In W2000, IoReuseIrp correctly reinitialises the IRP. ReadHidKbdInputReport then stores the MDL for the buffer in the IRP MdlAddress field. As before, it calls IoGetNextIrpStackLocation to get the next stack location. ReadHidKbdInputReport must set up all the stack parameters carefully: the MajorFunction, the Parameters.Read fields, and the FileObject.

Finally, ReadHidKbdInputReport needs to set a completion routine so that it knows when the IRP has completed. It passes an event to the completion routine. The completion routine sets the event into the signalled state when it is run). ReadHidKbdInputReport waits until the event is set (i.e., when the IRP has been completed by the lower driver. Assuming that the HID driver has returned data, the final job is to copy the data into the user's buffer, using RtlCopyMemory.

The ReadComplete completion routine returns STATUS_MORE_PROCESSING_REQUIRED. This stops the I/O Manager from deleting the IRP. The IRP will be reused so it must not be deleted.

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;

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату