KMDF HIDUSBFX2 sample: what is the NULL service for?

Hi all,

I’m writing a soft only driver to simulate a complex HID device using the minidriver technique. I’m basically using the hidusbfx2 as a minidriver sample (using kmdf) and stripping away the physical device related stuff and adding my own. I’m also referring to vhidmini sample to install my soft driver as root-enumerated driver.

I kind got it installed using devcon but I’m not sure I did it the right way. After my driver gets loaded, three new HID devices appear according to my report descriptor (three top level collections) although they should be recognized as keyboard, mouse and joystick but they are not.

Here some questions follow:

  1. is there any way to see what report descriptor the system gets from the driver? I mean after the facts, so that I can check if for some reason something gets garbled.

  2. I don’t fully understand the reason for the NULL service in both the vhidmini and hidusbfx2 inf samples. What is that for? Can it be omitted?

Thank you in advance,
Stra.

Hi all,

meanwhile my post was sinking like a heavy bubble, I figured it out and I thought it may have been useful to others since I realized that some of the issues I encountered already appeared in this forum without being solved really.

There may be a simpler solution but after trying pretty much I could think of, I came to the conclusion that kmdf has one limitation when it comes to writing a root-enumerated hid minidriver.
It really seems that root-enumerated AND minidriver don’t really go along with kmdf and everything basically boils down to properly handling IRP_MJ_PNP/IRP_MN_QUERY_ID. This is required to give a proper name to the enumerated virtual HID devices you may create.
Unfortunately (or fortunately in the 99% of the cases) the kmdf doesn’t really provide a way of handling this IRP when the driver you are writing is NOT a bus driver. Since being a kmdf hid minidriver requires to write a filter driver (of a wdm shim) there’s no way of using WdfPdo… functions to set a IRP_MN_QUERY_ID response.
I tried to use the …WdmPreprocessIrp… function but it didn’t seem to work. I also tried to set a completion routine and postprocess the Irp with no luck (I could get the names go through but that didn’t seem to be enough).
So, I gave up, and I actually added proper handling of the IRP_MN_QUERY_ID directly in the shim following the vhidmini handler. That worked just fine.

I wonder if there’s a cleaner solution…

Stra

Actually one last problem I have is that the driver doesn’t seem to unload properly. When attempting to uninstall (from the device manager) the Device Removal dialog hangs after clicking OK but nothing really happens in the driver (nothing that I can see from the kernel debugger).
Setupapi.log appears to unload correctly all the virtual instances of the top level collections but no log record for the main entry of the driver.

Does anybody have some clue of what could cause such a problem or just a way to troubleshoot it?

Thanks a lot,
Stra

Are you sure your filter is below the shim? Setting a wdm preprocess routine on IRP_MJ_PNP/IRP_MN_QUERY_ID should work just fine in the filter (I have done this in the past specifically in this scenario). Since this is being sent by the hid shim, you will be in the stack when it is sent. Note that this is a limitation of hidclass over a root PDO, not KMDF necessarily.

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Thursday, July 10, 2008 9:08 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] KMDF HIDUSBFX2 sample: what is the NULL service for?

Hi all,

meanwhile my post was sinking like a heavy bubble, I figured it out and I thought it may have been useful to others since I realized that some of the issues I encountered already appeared in this forum without being solved really.

There may be a simpler solution but after trying pretty much I could think of, I came to the conclusion that kmdf has one limitation when it comes to writing a root-enumerated hid minidriver.
It really seems that root-enumerated AND minidriver don’t really go along with kmdf and everything basically boils down to properly handling IRP_MJ_PNP/IRP_MN_QUERY_ID. This is required to give a proper name to the enumerated virtual HID devices you may create.
Unfortunately (or fortunately in the 99% of the cases) the kmdf doesn’t really provide a way of handling this IRP when the driver you are writing is NOT a bus driver. Since being a kmdf hid minidriver requires to write a filter driver (of a wdm shim) there’s no way of using WdfPdo… functions to set a IRP_MN_QUERY_ID response.
I tried to use the …WdmPreprocessIrp… function but it didn’t seem to work. I also tried to set a completion routine and postprocess the Irp with no luck (I could get the names go through but that didn’t seem to be enough).
So, I gave up, and I actually added proper handling of the IRP_MN_QUERY_ID directly in the shim following the vhidmini handler. That worked just fine.

I wonder if there’s a cleaner solution…

Stra


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

Break into the debugger, run !locks. see if the thread owning the pnp lock is stuck somewhere. otherwise, run !stacks 2 to see if you are blocking a thread. If that gives nothing, try !stacks 2 wdf01000 to see kmdf is waiting for something

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Thursday, July 10, 2008 9:14 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] KMDF HIDUSBFX2 sample: what is the NULL service for?

Actually one last problem I have is that the driver doesn’t seem to unload properly. When attempting to uninstall (from the device manager) the Device Removal dialog hangs after clicking OK but nothing really happens in the driver (nothing that I can see from the kernel debugger).
Setupapi.log appears to unload correctly all the virtual instances of the top level collections but no log record for the main entry of the driver.

Does anybody have some clue of what could cause such a problem or just a way to troubleshoot it?

Thanks a lot,
Stra


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

Hi Doron,

thank you for your help.

Regarding your first reply, yes the filter is loaded as lower filter (I basically copied the HIDUSBFX2 inf file). May be I did something wrong in the preprocess routine: is it correct to complete the request from within the preprocess procedure? Here is the code of the routine (just a test) before I attempted to set the completion routine (and attempt the post process).

Anyway thank you so much, I’ll try a little harder since I really don’t like the current solution: I like the idea to stay within the framework as much as I can.

Stra.


NTSTATUS HidKmjEvtWdmPreprocessMnQueryId(IN WDFDEVICE Device, IN OUT PIRP Irp)
{
NTSTATUS ntStatus;
PDEVICE_OBJECT DeviceObject;
PIO_STACK_LOCATION IrpStack, previousSp;
PWCHAR buffer;

//
// Perform IRP preprocessing operations here.
//

IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceObject = WdfDeviceWdmGetDeviceObject(Device);

previousSp = ((PIO_STACK_LOCATION) ((UCHAR *) (IrpStack) + sizeof(IO_STACK_LOCATION)));

if (previousSp->DeviceObject == DeviceObject) {

ntStatus = Irp->IoStatus.Status;
}
else
{
switch (IrpStack->Parameters.QueryId.IdType)
{
case BusQueryDeviceID:
case BusQueryHardwareIDs:

buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, KMJ_VIRTUALDEVICE_IDS_LENGTH, KMJ_POOL_TAG);

if(buffer)
{
RtlCopyMemory(buffer, KMJ_VIRTUALDEVICE_IDS, KMJ_VIRTUALDEVICE_IDS_LENGTH);

Irp->IoStatus.Information = (ULONG_PTR)buffer;
ntStatus = STATUS_SUCCESS;
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}

Irp->IoStatus.Status = ntStatus;

IoCompleteRequest (Irp, IO_NO_INCREMENT);
break;
}
}

//
// Deliver the IRP back to the framework.
//

IoSkipCurrentIrpStackLocation(Irp);
return WdfDeviceWdmDispatchPreprocessedIrp(Device, Irp);
}

you do not need to handle hardware IDs, you are loaded way too late to effect that. Why are you looking at the previous stack location? To see if you are recursing?

Off the top of my head, you are completing the request and then touching it (by giving it back to the framework). If you complete the request, just return ntStatus after completing it. Also, run this with driver verifier enabled on your driver. I think you will need to send the request down the stack first and then process the request when the irp is travelling up the stack.

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Thursday, July 10, 2008 10:50 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] KMDF HIDUSBFX2 sample: what is the NULL service for?

Hi Doron,

thank you for your help.

Regarding your first reply, yes the filter is loaded as lower filter (I basically copied the HIDUSBFX2 inf file). May be I did something wrong in the preprocess routine: is it correct to complete the request from within the preprocess procedure? Here is the code of the routine (just a test) before I attempted to set the completion routine (and attempt the post process).

Anyway thank you so much, I’ll try a little harder since I really don’t like the current solution: I like the idea to stay within the framework as much as I can.

Stra.


NTSTATUS HidKmjEvtWdmPreprocessMnQueryId(IN WDFDEVICE Device, IN OUT PIRP Irp)
{
NTSTATUS ntStatus;
PDEVICE_OBJECT DeviceObject;
PIO_STACK_LOCATION IrpStack, previousSp;
PWCHAR buffer;

//
// Perform IRP preprocessing operations here.
//

IrpStack = IoGetCurrentIrpStackLocation(Irp);
DeviceObject = WdfDeviceWdmGetDeviceObject(Device);

previousSp = ((PIO_STACK_LOCATION) ((UCHAR *) (IrpStack) + sizeof(IO_STACK_LOCATION)));

if (previousSp->DeviceObject == DeviceObject) {

ntStatus = Irp->IoStatus.Status;
}
else
{
switch (IrpStack->Parameters.QueryId.IdType)
{
case BusQueryDeviceID:
case BusQueryHardwareIDs:

buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, KMJ_VIRTUALDEVICE_IDS_LENGTH, KMJ_POOL_TAG);

if(buffer)
{
RtlCopyMemory(buffer, KMJ_VIRTUALDEVICE_IDS, KMJ_VIRTUALDEVICE_IDS_LENGTH);

Irp->IoStatus.Information = (ULONG_PTR)buffer;
ntStatus = STATUS_SUCCESS;
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}

Irp->IoStatus.Status = ntStatus;

IoCompleteRequest (Irp, IO_NO_INCREMENT);
break;
}
}

//
// Deliver the IRP back to the framework.
//

IoSkipCurrentIrpStackLocation(Irp);
return WdfDeviceWdmDispatchPreprocessedIrp(Device, Irp);
}


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

The code was pretty much taken from the vhidmini sample (which is wdm) from the QUERY_ID handler. Although I have to say that I don’t grasp all the details of this there are few things that I noticed in may tests or read in the documentation:

  1. I noticed that if I don’t handle the hardwareIDs imposing mine the virtual devices I won’t have a known string to match in the inf file for the null service to load. This basically creates 3 unknown devices (I have 3 top level collections) which the system apparently fails to load the drivers for (keyboard, mouse and a joystick).

  2. I removed the comments from the code to compress it but the vhidmini sample has that check about the previous stack location supposedly to avoid the found new hardware baloon at the reboot.

  3. Completing and exiting was one of the things I thought at first but the kmdf documentation states that unless you are handling a IRP that is not handled but the framework you MUST call the dispatch function. I just thought that giving the irp back to the framework would allow it to free or reset whatever resources or flags may be set before calling my preprocess handler. If this is not the case, that may be one of the reasons for this code to bugcheck (if I remember well, that was the outcome).

  4. I actually tried to process the irp on the way back by setting a completion routine and doing the hardwareID stuff in there. I actually thought that that would have been identical to processing the irp directly in the shim. For some reason it did not work as I expected: I could read the hardwareIDs in the device manager as I coded but still the virtual devices were unknown (and I could not see the typical HID_DEVICE_UP:… strings). In that case, the only thing I did in the completion routine was providing the buffer and changing the IoStatus.Status with STATUS_SUCCESS.

Anyway, as soon as fix the hang problem at uninstall/shutdown I’ll try to remove the WdfDeviceWdmDispatchPreprocessedIrp section… it makes so sense.

Thank you very much, I really appreciate your help.

Stra.

Regarding the unload problem I described a couple of posts above, I’ve been investigating on it and I found out that basically there’s one thread that is holding a lock nt!IopDeviceTreeLock. Looking into the stack trace for that thread it seems that it’s waiting for HIDCLASS!CancelAllPingPongIrps which makes sense since there are always 2 Irps outstanding.
Digging a little more with !stacks I found out that there are 2 system threads in blocked state stopped somewhere in wdf01000+0x6657c also waiting for an object.

As the driver is designed, all the INTERNAL_IOCTL coming in are redirected and enqueued into another wdf queue that is handled manually so that I can process the READ irps only when I have something to send up. If I understand correctly, since the requests are parked into the queue, the framework should take care of the cancellation for me.

Is this correct? What could be stopping the framework from releasing the 2 outstanding requests?

Also, reading the article “A Soft Life - Implementing Software-Only Drivers Using KMDF”, I read that a root-enumerated driver should own the power policy however, being the minidriver implemented as filter, it’s not policy owner by default. So I tried to use WdfDeviceInitSetPowerPolicyOwnership but with no luck (at least on this issue).

Also, I don’t know if this completes the picture but I noticed one funny thing: after the minidrver is correctly loaded and running on the device manager I see one entry for it and 3 entries one each top level collection. That makes sense. Right clicking on the entries for the top level collections, I can see the action “Uninstall…” however on the main entry that corresponds to the actual minidriver, no Uninstall options appears. If I uninstall one by one all the virtual devices, suddenly the Uninstall action on the minidriver entry appears again (and that’s the one that makes the device manager hang). To spot similarities I tried the vhidmini sample and the Unistall action is always there at any given time: if I uninstall the minidriver, the virtual device disappears too.
Is this a sign of something not correctly installed or handled?

Thanks for any help,
Stra.

Fix you symbols so you can resolve the wdf01000 stacks. Run !wdfdevicequeues once you get into this state to see what the queues contain (also requires good symbols)

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Friday, July 11, 2008 5:46 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] KMDF HIDUSBFX2 sample: what is the NULL service for?

Regarding the unload problem I described a couple of posts above, I’ve been investigating on it and I found out that basically there’s one thread that is holding a lock nt!IopDeviceTreeLock. Looking into the stack trace for that thread it seems that it’s waiting for HIDCLASS!CancelAllPingPongIrps which makes sense since there are always 2 Irps outstanding.
Digging a little more with !stacks I found out that there are 2 system threads in blocked state stopped somewhere in wdf01000+0x6657c also waiting for an object.

As the driver is designed, all the INTERNAL_IOCTL coming in are redirected and enqueued into another wdf queue that is handled manually so that I can process the READ irps only when I have something to send up. If I understand correctly, since the requests are parked into the queue, the framework should take care of the cancellation for me.

Is this correct? What could be stopping the framework from releasing the 2 outstanding requests?

Also, reading the article “A Soft Life - Implementing Software-Only Drivers Using KMDF”, I read that a root-enumerated driver should own the power policy however, being the minidriver implemented as filter, it’s not policy owner by default. So I tried to use WdfDeviceInitSetPowerPolicyOwnership but with no luck (at least on this issue).

Also, I don’t know if this completes the picture but I noticed one funny thing: after the minidrver is correctly loaded and running on the device manager I see one entry for it and 3 entries one each top level collection. That makes sense. Right clicking on the entries for the top level collections, I can see the action “Uninstall…” however on the main entry that corresponds to the actual minidriver, no Uninstall options appears. If I uninstall one by one all the virtual devices, suddenly the Uninstall action on the minidriver entry appears again (and that’s the one that makes the device manager hang). To spot similarities I tried the vhidmini sample and the Unistall action is always there at any given time: if I uninstall the minidriver, the virtual device disappears too.
Is this a sign of something not correctly installed or handled?

Thanks for any help,
Stra.


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

Too bad, I’m using the latest version of the kmdf and it doesn’t seems to ship with symbols (and no other version is available for download). They are not available through the microsoft symbol server.

Are they freely available?