HID report descriptor caching / reenumerating HID PDOs

Hello all,

I have written a KMDF HID miniport driver for a PCI input device. It
uses an equivalent of mshidkmdf.sys as the actual miniport, with my
KMDF driver being its lower filter. This is the same setup that
several WDK samples use and everything works as expected.

Except that something seems to be caching the HID report descriptor
that my driver returns. My device is basically a pass-through virtual
hardware, exposing an arbitrary input device connected to the host. So
it may generate a different report descriptor every time it’s plugged
in, still having only one fixed vendor/product ID. What I see is the
system remembering the descriptor the first time the device is plugged
into the given PCI slot, and using it (and completely ignoring the one
I’m returning) from that time on.

Example: Slot 1 gets my device representing a keyboard and slot 2 gets
another instance representing a mouse. On first boot, PDOs are created
as usual, I’ll have one “HID Keyboard Device” and one “HID-compliant
mouse” for example. Then on next boot I put another keyboard in slot 2
and the PDOs remain the way they were on first boot - i.e. one
keyboard and one mouse, wrong. And the second keyboard is not working.
Depending on how different they were, it may also get the yellow
exclamation mark. Uninstalling the PDO in Device Manager and then
hitting Scan for hw changes fixes it - the right device is created and
everything works. This is super easy to reproduce on Win7 and Win8.
Win10 works fine for me although I have received reports claiming
otherwise.

Questions:

  1. Is there anything I can do in my driver to suppress the caching
    behavior, to make sure that the system doesn’t remember anything about
    the shape of the HID device?

  2. How feasible would it be do the equivalent of Uninstall + Scan for
    hw changes programmatically from the driver? Without looking a total
    hack, if possible.

Thanks!
Ladi

I’ve spent some time playing the hidkd debugger extension.

  1. Keyboard in first PCI slot, mouse in second slot, everything works.
    The output of !hidtree looks fine:

1: kd> !hidtree
HID Device Tree

FDO VendorID:0x1AF4(NCS Pearson, Inc.) ProductID:0x1052 Version:0x0001
!hidfdo 0xffffe0017ec13770
PowerStates: S0/D0 | 0n0 (No matching enumerant)
dt FDO_EXTENSION 0xffffe0017ec138e0
!devnode 0xffffe0017e153770 | DeviceNodeStarted (0n776)
InstancePath: PCI\VEN_1AF4&DEV_1052&SUBSYS_11001AF4&REV_01\3&13c0b0c5&0&48
IFR Log: !rcdrlogdump HIDCLASS -a 0xFFFFE0017EB1F000

PDO Generic Desktop Controls (0x01) | Mouse (0x02)
!hidpdo 0xffffe0017efb8560
PowerStates:S0/D0 | COLLECTION_STATE_RUNNING (0n3)
dt PDO_EXTENSION 0xffffe0017efb86d0
!devnode 0xffffe0017efb9010 | DeviceNodeStarted (0n776)
InstancePath:HID\VEN_1AF4&DEV_1052&SUBSYS_11001AF4&REV_01\4&e0a22c4&0&0000


FDO VendorID:0x1AF4(NCS Pearson, Inc.) ProductID:0x1052 Version:0x0001
!hidfdo 0xffffe0017ebef1b0
PowerStates: S0/D0 | 0n0 (No matching enumerant)
dt FDO_EXTENSION 0xffffe0017ebef320
!devnode 0xffffe0017e153a50 | DeviceNodeStarted (0n776)
InstancePath: PCI\VEN_1AF4&DEV_1052&SUBSYS_11001AF4&REV_01\3&13c0b0c5&0&40
IFR Log: !rcdrlogdump HIDCLASS -a 0xFFFFE0017EB1D000

PDO Generic Desktop Controls (0x01) | Keyboard (0x06)
!hidpdo 0xffffe0017efb4060
PowerStates:S0/D0 | COLLECTION_STATE_RUNNING (0n3)
dt PDO_EXTENSION 0xffffe0017efb41d0
!devnode 0xffffe0017efb4d30 | DeviceNodeStarted (0n776)
InstancePath:HID\VEN_1AF4&DEV_1052&SUBSYS_11001AF4&REV_01\4&208d8e0d&0&0000

================================================================================

and just in case:

1: kd> !devnode 0xffffe0017e153a50 <== keyboard
DevNode 0xffffe0017e153a50 for PDO 0xffffe0017e14d060

1: kd> !devobj ffffe0017e14d060
Device object (ffffe0017e14d060) is for:
NTPNP_PCI0013 \Driver\pci DriverObject ffffe0017e13ca30

1: kd> !devnode 0xffffe0017e153770 <== mouse
DevNode 0xffffe0017e153770 for PDO 0xffffe0017e14d880

1: kd> !devobj ffffe0017e14d880
Device object (ffffe0017e14d880) is for:
NTPNP_PCI0014 \Driver\pci DriverObject ffffe0017e13ca30

So keyboard is NTPNP_PCI0013 and mouse NTPNP_PCI0014. So far so good.

  1. Now the system is rebooted with the devices swapped, i.e. mouse in
    first PCI slot, keyboard in second.

1: kd> !hidtree
HID Device Tree

FDO VendorID:0x1AF4(NCS Pearson, Inc.) ProductID:0x1052 Version:0x0001
!hidfdo 0xffffe0017a6fc510
PowerStates: S0/D0 | 0n0 (No matching enumerant)
dt FDO_EXTENSION 0xffffe0017a6fc680
!devnode 0xffffe00179d50a50 | DeviceNodeStarted (0n776)
InstancePath: PCI\VEN_1AF4&DEV_1052&SUBSYS_11001AF4&REV_01\3&13c0b0c5&0&48
IFR Log: !rcdrlogdump HIDCLASS -a 0xFFFFE0017A70A000

PDO Generic Desktop Controls (0x01) | Keyboard (0x06)
!hidpdo 0xffffe0017abbb560
PowerStates:S0/D0 | COLLECTION_STATE_UNINITIALIZED (0n1)
dt PDO_EXTENSION 0xffffe0017abbb6d0
!devnode 0xffffe0017abbc010 | DeviceNodeRemoved (0n786)
InstancePath:HID\VEN_1AF4&DEV_1052&SUBSYS_11001AF4&REV_01\4&e0a22c4&0&0000


FDO VendorID:0x1AF4(NCS Pearson, Inc.) ProductID:0x1052 Version:0x0001
!hidfdo 0xffffe0017a6c6770
PowerStates: S0/D0 | 0n0 (No matching enumerant)
dt FDO_EXTENSION 0xffffe0017a6c68e0
!devnode 0xffffe00179d50d30 | DeviceNodeStarted (0n776)
InstancePath: PCI\VEN_1AF4&DEV_1052&SUBSYS_11001AF4&REV_01\3&13c0b0c5&0&40
IFR Log: !rcdrlogdump HIDCLASS -a 0xFFFFE0017A704000

PDO Generic Desktop Controls (0x01) | Mouse (0x02)
!hidpdo 0xffffe0017a97b770
PowerStates:S0/D0 | COLLECTION_STATE_RUNNING (0n3)
dt PDO_EXTENSION 0xffffe0017a97b8e0
!devnode 0xffffe0017a97b440 | DeviceNodeStarted (0n776)
InstancePath:HID\VEN_1AF4&DEV_1052&SUBSYS_11001AF4&REV_01\4&208d8e0d&0&0000

================================================================================

Neither of the two input devices work at this point. Note that
!hidtree prints them in reversed order - expected, I guess. And the
same exercise with PCI PDOs yields:

1: kd> !devnode 0xffffe00179d50a50 <== keyboard
DevNode 0xffffe00179d50a50 for PDO 0xffffe00179d4a880

1: kd> !devobj ffffe00179d4a880
Device object (ffffe00179d4a880) is for:
NTPNP_PCI0014 \Driver\pci DriverObject ffffe00179d3da50

1: kd> !devnode 0xffffe00179d50d30 <== mouse
DevNode 0xffffe00179d50d30 for PDO 0xffffe00179d4a060

1: kd> !devobj ffffe00179d4a060
Device object (ffffe00179d4a060) is for:
NTPNP_PCI0013 \Driver\pci DriverObject ffffe00179d3da50

So mouse is NTPNP_PCI0013 and keyboard NTPNP_PCI0014. Again, expected
because I swapped them.

Decoding the preparsed data with !hidppd for both PDOs returns
expected results too.

  1. Now I left the mouse in first PCI slot and removed the keyboard.

1: kd> !hidtree
HID Device Tree

FDO VendorID:0x1AF4(NCS Pearson, Inc.) ProductID:0x1052 Version:0x0001
!hidfdo 0xffffe000d46fd770
PowerStates: S0/D0 | 0n0 (No matching enumerant)
dt FDO_EXTENSION 0xffffe000d46fd8e0
!devnode 0xffffe000d3d4fa50 | DeviceNodeStarted (0n776)
InstancePath: PCI\VEN_1AF4&DEV_1052&SUBSYS_11001AF4&REV_01\3&13c0b0c5&0&40
IFR Log: !rcdrlogdump HIDCLASS -a 0xFFFFE000D4703000

PDO Generic Desktop Controls (0x01) | Mouse (0x02)
!hidpdo 0xffffe000d49d7770
PowerStates:S0/D0 | COLLECTION_STATE_RUNNING (0n3)
dt PDO_EXTENSION 0xffffe000d49d78e0
!devnode 0xffffe000d49d7440 | DeviceNodeStarted (0n776)
InstancePath:HID\VEN_1AF4&DEV_1052&SUBSYS_11001AF4&REV_01\4&208d8e0d&0&0000

================================================================================

Ok, there’s mouse. But Device Manager shows “HID Keyboard Device” and,
of course, mouse is not working. Ugh!

And as mentioned in my previous post, if I uninstall this “HID
Keyboard Device” and hit Scan for hardware changes, I get a
“HID-compliant mouse” and everything works from that point on.

So to refine my questions:

  1. What could be causing this mismatch between what DM shows and
    !hidtree prints? Any chance I can work around it?

  2. Specifically, can I force the re-enumeration, which is known to fix
    it, programmatically from my HID miniport?

Thanks!
Ladi

On Thu, Feb 23, 2017 at 9:03 PM, Ladi Prosek wrote:
> Hello all,
>
> I have written a KMDF HID miniport driver for a PCI input device. It
> uses an equivalent of mshidkmdf.sys as the actual miniport, with my
> KMDF driver being its lower filter. This is the same setup that
> several WDK samples use and everything works as expected.
>
> Except that something seems to be caching the HID report descriptor
> that my driver returns. My device is basically a pass-through virtual
> hardware, exposing an arbitrary input device connected to the host. So
> it may generate a different report descriptor every time it’s plugged
> in, still having only one fixed vendor/product ID. What I see is the
> system remembering the descriptor the first time the device is plugged
> into the given PCI slot, and using it (and completely ignoring the one
> I’m returning) from that time on.
>
> Example: Slot 1 gets my device representing a keyboard and slot 2 gets
> another instance representing a mouse. On first boot, PDOs are created
> as usual, I’ll have one “HID Keyboard Device” and one “HID-compliant
> mouse” for example. Then on next boot I put another keyboard in slot 2
> and the PDOs remain the way they were on first boot - i.e. one
> keyboard and one mouse, wrong. And the second keyboard is not working.
> Depending on how different they were, it may also get the yellow
> exclamation mark. Uninstalling the PDO in Device Manager and then
> hitting Scan for hw changes fixes it - the right device is created and
> everything works. This is super easy to reproduce on Win7 and Win8.
> Win10 works fine for me although I have received reports claiming
> otherwise.
>
> Questions:
> 1) Is there anything I can do in my driver to suppress the caching
> behavior, to make sure that the system doesn’t remember anything about
> the shape of the HID device?
>
> 2) How feasible would it be do the equivalent of Uninstall + Scan for
> hw changes programmatically from the driver? Without looking a total
> hack, if possible.
>
> Thanks!
> Ladi

Replying to my own post again because this may be useful to people
running into the same issue.

I have fixed it in:
https://github.com/virtio-win/kvm-guest-drivers-windows/commit/a2943d499bfb8ad91f191062506fe927ba624f6b

Basically implementing Vivek Gupta’s suggestion in this thread:
https://www.osronline.com/showthread.cfm?link=272517

The caching is not happening at HID level, it is a by-design “let’s
not rebuild a devnode if we’ve seen a PDO with this instance path
before” behavior of the PnP manager.

And because hidclass.sys doesn’t do anything to incorporate the shape
of the HID collections into the instance IDs of its children, a
miniport that was previously, say, a mouse and now switched to being
something else like a keyboard, without changing its device ID, will
still have a mouse stack built for its child.

Many thanks for Michael Myn for sharing his insights off-list.

Ladi

On Fri, Mar 3, 2017 at 5:31 PM, Ladi Prosek wrote:
>
> So to refine my questions:
>
> 1) What could be causing this mismatch between what DM shows and
> !hidtree prints? Any chance I can work around it?
>
> 2) Specifically, can I force the re-enumeration, which is known to fix
> it, programmatically from my HID miniport?