Parameter Description
IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory EventCategoryDeviceInterfaceChange EventCategoryHardwareProfileChange or EventCategoryTargetDeviceChange
IN ULONG EventCategoryFlags Optionally PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
IN PVOID EventCategoryData Device GUID, NULL, or file object, respectively
IN PDRIVER_OBJECT DriverObject The driver object
IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine Your callback routine name
IN PVOID Context Context to pass to your callback
OUT PVOID *NotificationEntry Output value to pass to IoUnregisterPlugPlayNotification

Device Interface Change Notifications

Anyway, device interface change notifications do seem to work, so let's look at the HidKbd device interface change callback, HidKbdDicCallback shown in Listing 23.7. Each callback receives the context pointer and a notification structure pointer. For device interface change events, this is a pointer to a DEVICE_INTERFACE_CHANGE_NOTIFICATION structure.

The notification structure Event GUID field says what type of event has occurred. The SymbolicLinkName UNICODE_STRING field can be used to open a handle to the device.

When a new device arrives, Event contains GUID_DEVICE_INTERFACE_ARRIVAL For DeviceRemoval events, Event is GUID_DEVICE_INTERFACE_REMOVAL. HidKbdDicCallback uses the IsEqualGUID macro to detect each of these events. For Device Arrival events, the CreateDevice routine is called, and for Device Removals, DeleteDevice is called.

Listing 23.7 PnP device interface change notification callback

NTSTATUS HidKbdDicCallback(IN PVOID NotificationStructure, IN PVOID Context) {

 PDEVICE_INTERFACE_CHANGE_NOTIFICATION dicn = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)NotificationStructure;

 PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Context;

 if (IsEqualGUID(dicn->Event, GUID_DEVICE_INTERFACE_ARRIVAL)) {

  DebugPrint('Device arrival: XT', dicn->SymbolicLinkName);

  CreateDevice(DriverObject, dicn->SymbolicLinkName);

 } else if(IsEqualGUID(dicn->Event, GUID_DEVICE_INTERFACE_REMOVAL)) {

  DebugPrint('Device removal: %T', dicn->SymbolicLinkName);

  DeleteDevice(dicn->SymbolicLinkName);

 } else DebugPrint('Some other device event: %T', dicn->SymbolicLinkName);

 return STATUS_SUCCESS;

}

HidKbd Devices

Hold onto your hats, as the HidKbd device handling is a bit complicated.

Remember that the CreateDevice routine is called whenever a HID device is added to the system. HidKbd is just looking for a HID keyboard. However, it must cope if a HID device arrives that is not a keyboard, and if two HID keyboards arrive (it is possible).

HidKbd tries to make things simple by only coping with one keyboard. If a second HID keyboard arrives, it is ignored.

HidKbd creates its own device called \.HidKbd for the first HID keyboard that arrives. A user mode program can open a handle to this device and issue read requests. HidKbd handles these by calling the HID device to get an input report. HidKbd does not do anything for Write or IOCTL requests.

Has a HID Keyboard Been Found?

The CreateDevice routine shown in Listing 23.8 starts by checking to see if it has already found a HID keyboard. The HidKbdDo global variable stores a pointer to the HidKbd device object; if this is non-NULL, a suitable keyboard has already been found.

The first job is to open a connection to the HID device and see if it is HID keyboard. While CreateDevice could use ZwCreateFile to open a handle to the HID device, the IoGetDeviceObjectPointer routine is what is really needed. IoGetDeviceObjectPointer is passed the symbolic link for a device. If the symbolic link is found, IoGetDeviceObjectPointer issues a Create IRP to the device, passing an empty string as the IRP filename parameter[59]. IoGetDeviceObjectPointer returns two pieces of information: the device object pointer and the PFILE_OBJECT pointer.

HidKbd is going to use the HID device object pointer a lot. In addition, it needs a file object pointer when it eventually reads reports from (or writes reports to) the HID class driver. However, in the mean time, CreateDevice closes the file object pointer by calling ObDereferenceObject. Why is this done? If a file is open on a device, Windows 2000 will not let a device be removed. The file must be closed to let device removals take place.

CreateDevice now inspects the HID device capabilities using the GetCapabilities routine, which I describe later. If GetCapabilities finds a HID keyboard, HidKbdUser, like its user mode equivalent, returns a pointer to the preparsed data and the maximum input and output report lengths.

Creating the HidKbd Device

If a HID keyboard is found, CreateDevice can go on to create its own device object. However, it first calls ObReferenceObjectByPointer to reference the HID class driver device object. This ensures that the device object will not disappear from under our feet. When the HidKbd device is deleted, ObDereferenceObject is called to dereference the object. Note that referencing this device object does not stop it from processing removal requests successfully.

The next job is to allocate some memory for a copy of the HID device symbolic link name. This name is stored in a UNICODE_STRING field called HidSymLinkHame in the new device extension. The HidKbd Device Removal event handler only deletes a device if the correct underlying HID device is being removed.

HidKbd now sets up the device name and symbolic link names for the new HidKbd device. These are DeviceHidKbd and DosDevicesHidKbd respectively, and so the device appears in Win32 as \.HidKbd.

HidKbd is finally ready to call IoCreateDevice, with most of the parameters set up as usual. However this time it passes the device type FILE_DEVICE_KEYBOARD, as this seems most appropriate. If the device is created successfully, the global variable, HidKbdDo, stores the device object. Next, CreateDevice sets up the device extension. Finally, CreateDevice calls IoCreateSymbolicLink to create the symbolic link that makes the device visible to Win32 applications.

Note that HidKbd did not call IoAttachDeviceToDeviceStack. If it did make this call, the HidKbd device would be layered over the HID device. Any user mode calls direct to the HID device would arrive at the HidKbd device first, which is not what is wanted.

As a last touch, note that CreateDevice sets up the StackSize field of the HidKbdDo device object. If HidKbd had called IoAttachDeviceToDeviceStack, this routine would have set the IRP stack size to be one greater than the HID device stack size. As it did not call IoAttachDeviceToDeviceStack, HidKbd has to do this same job. Later, HidKbd passes IRPs to the HID class driver for processing. Setting the stack size in this way ensures that there will be enough IRP stack locations available.

Listing 23.8 HidKbd CreateDevice routine

void CreateDevice( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING HidSymLinkName) {

 if (HidKbdDo!=NULL) {

  DebugPrintMsg('Already got HidKbdDo');

  return;

 }

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

0

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

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