FreelfAllocated(dx->HidPreparsedData);

 FreeIfAllocated(dx->HidSymLinkName.Buffer);

 // Initialise Symbolic link names

 UNICODE_STRING linkName;

 RtlInitUnicodeString(&linkName, SYM_LINK_NAME);

 // Remove symbolic link

 DebugPrint('Deleting symbolic link XT', &linkName);

 IoDeleteSymbolicLink(&linkName);

 ObDereferenceObject(dx->HidDevice);

 IoDeleteDevice(HidKbdDo);

 HidKbdDo = NULL;

}

Getting HID capabilities

The HidKbd GetCapabilities routine is largely the same as its equivalent in HidKbdUser. It returns true if the HID device capabilities indicate that it is a HID keyboard.

However, GetCapabilities must obtain the device attributes and the preparsed data in a different way from HidKbdUser. The GetPreparsedData routine shown in Listing 23.10 does this job.

GetPreparsedData uses two IOCTLs to obtain the information needed from the HID class driver. IOCTL_HID_GET_COLLECTION_INFORMATION returns the attributes and the size of memory buffer required for the preparsed data. Next, IOCTL_HID_GET_COLLECTION_DESCRIPTOR is used to get the preparsed data itself. GetPreparsedData returns a pointer to the preparsed data memory. This memory must eventually be freed.

The CallHidIoctl routine is used to issue the two IOCTLs. This works in a very similar way to the CallUSBDI routine shown in Listing 21.2. It calls IoBuildDeviceIoControlRequest to build the IOCTL passing the IOCTL code, the output buffer details and a completion event. CallHidIoctl calls the HID class driver using IoCallDriver, waiting until it completes using the event, if necessary.

Listing 23.10 HidKbd GetPreparsedData routine

bool GetPreparsedData(IN PDEVICE_OBJECT HidDevice, OUT PHIDP_PREPARSED_DATA HidPreparsedData) {

 HID_COLLECTION_INFORMATION HidCi;

 NTSTATUS status = CallHidIoctl(HidDevice, IOCTL_HID_GET_COLLECTION_INFORMATION, &HidCi, sizeof(HidCi));

 if (!NT_SUCCESS(status)) {

  DebugPrint('IOCTL_HID_GET_COLLECTION_INFORMATION failed %x', status);

  return false;

 }

 DebugPrint('HID attributes: VendorID=%4x, ProductID=%4x, VersionNumber=%4x',

  HidCi.VendorID, HidCi.ProductIO, HidCi.VersionNumber);

 ULONG PreparsedDatalen = HidCi.DescriptorSize;

 DebugPrint('PreparsedDatalen %d', PreparsedDatalen);

 HidPreparsedData = (PHIDP_PREPARSED_DATA)ExAllocatePool(NonPagedPool, PreparsedDatalen);

 if (HidPreparsedData==NULL) {

  DebugPrintMsg('No memory');

  return false;

 }

 status = CallHidIoctl(HidDevice, IOCTL_HID_GET_COLLECTON_DESCRIPTOR, HidPreparsedData, PreparsedDatalen);

 if (!NT_SUCCESS(status)) {

  DebugPrint('IOCTL_HID_GET_COLLECTION_DESCRIPTOR failed %x', status);

  return false;

 }

 return true;

}

Opening and Closing the HidKbd Device

The DDK documentation for the HID class driver read and write handler says that the IRP file object pointer must be valid. HidKbd obtained a file object using IoGetDeviceObjectPointer when it first found a HID device. However, this file handle was closed because it stops the HID device from being removed.

When a user mode application opens a handle to a HidKbd device, the Create IRP handler receives another file object pointer. This same file object pointer is passed in subsequent Read, Write, and Close IRPs, etc.

The HidKbd Create IRP handler, HidKbdCreate, therefore, has to tell the HID class driver about this new file object pointer. It does this by passing the Create IRP to the HID class driver. This is actually very easy to do by putting this extra code in the HidKbdCreate routine.

As HidKbd does not need to process the IRP afterwards, there is no need to set a completion routine.

// Forward IRP to HID class driver device

IoSkipCurrentIrpStackLocation(Irp);

return IoCallDriver(dx->HidDevice, Irp);

The HidKbd Close IRP handler, HidKbdClose, has exactly the same lines in it. This tells the HID class driver that the file handle is being closed.

A side effect of making the HID class driver open a handle for the device is that Windows 2000 will not let the HID device be removed for the duration. This is a perfectly acceptable behavior.

Reading and Writing Data

Our HID kernel mode client is now finally ready to read and write data.

HidKbd currently only supports reading of input reports. The Read IRP expects the provided buffer to be big enough. For a keyboard-input report, the buffer must be at least nine bytes long. The first byte will be 0, with the eight bytes of the input report in the remaining bytes. HidKbd makes no attempt to analyze the data in the same way as HidKbdUser. Instead, it simply returns all the information to the user mode application.

The main Read IRP handler, HidKbdRead, eventually calls ReadHidKbdInputReport, shown in Listing 23.11. ReadHidKbdInputReport is passed the precious file object pointer and a pointer to the buffer. It returns a count of the number of bytes transferred.

ReadHidKbdInputReport looks similar to the CallUSBDI and CallHidIoctl routines described before. This time HidKbd must issue a read request to the HID class driver, so it uses IoBuildSynchronousFsdRequest kernel call to build a suitable Read IRP. An event can be used to wait synchronously for the IRP to be completed, so ReadHidKbdInputReport must be called at PASSIVE_LEVEL IRQL.

By default, IoBuildSynchronousFsdRequest does not insert a file object pointer into the IRP. Therefore, HidKbd must do this job by hand. It calls IoGetNextIrpStackLocation to get the stack location that will be seen by the next driver, the HID class driver. ReadHidKbdInputReport then simply stores the PFILE_OBJECT in the stack FileObject field.

Finally, HidKbd runs IoCallDriver to call the HID class driver. If the IRP is still pending when this call returns, ReadHidKbdInputReport waits for the event to become signalled when the IRP does complete.

I have left out one small part of the story. The DDK says that HID class drivers use Direct I/O for their input and output buffers, not Buffered I/O. Luckily, IoBuildSynchronousFsdRequest sorts this out for us. It checks if the called driver uses Direct I/O. If it does,it allocates the required MDL for the passed input or output buffer (and deallocates it on completion).

Listing 23.11 ReadHidKbdlnputReport routine

NTSTATUS ReadHidKbdInputReport(PFILE_OBJECT FileObject, PVOID Buffer, ULONG& BytesTxd) {

 PHIDKBD_DEVICE_EXTENSION dx = (PHIDKBD_DEVICE_EXTENSION)HidKbdDo->DeviceExtension;

 BytesTxd = 0;

 if (HidKbdDo==NULL) return STATUS_NO_MEDIA_IN_DEVICE;

 IO_STATUS_BLOCK IoStatus;

 IoStatus.Information = 0;

 KEVENT event;

 LARGE_INTEGER FilePointer;

 FilePointer.QuadPart = 0i64;

 // Initialise IRP completion event

 KeInitializeEvent(&event, NotificationEvent, FALSE);

 PIRP Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, dx->HidDevice,

  Buffer, dx->HidInputReportLen, &FilePointer, &event, &IoStatus);

 if (Irp==NULL) return STATUS_INSUFFICIENT_RESOURCES;

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

0

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

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