I have a setup where I have two separate drivers (WDDM + filter) on top of my hardware that both serve different purposes but which are always used together from any context where (non-OS) usermode code is accessing the device. Access to the WDDM portion of the driver can be performed through the existing D3D API (D3DKMTEnumAdapters2), but for access to the filter portion I am exposing an interface and manually iterating over devices that expose that interface using SetupDiEnumDeviceInterfaces. This is technically a bit clunky, as it involves having to basically iterate over all the devices twice and leaves open a situation where if more than one device were installed on the system, mismatched interfaces could be selected by usermode code.
The WDDM driver has an Escape mechanism to transfer generic data between user and kernel modes. I’d like to use this functionality to expose the device path that would normally be found through the SetupDiEnumDeviceInterfaces API, so usermode code only ever needs to use the D3DKMTEnumAdapters API to find the adapter, and can then retrieve the correct path to use for the filter interface through an Escape call.
How can I determine the correct device path to return? I have access to the PDEVICE_OBJECT for the device itself, but I’m not sure how to go from there to the path I’m looking for.
The path I’m trying to find a way to get at looks like \\?\pci\#ven_XXXX&dev_XXXX&subsys_XXXXXXXX&rev_XX#4&280b1369&0&00009#{INTERFACE GUID}. I can build the first and last parts of this path manually (I know the vendor, device, etc. identifiers for my device, obviously, and the interface GUID is also defined by me), but I do not know what the values between the #s mean or how I can get at them from inside my driver.
I’m sure there’s an API call that will just give me this, but I haven’t been able to find it. If the solution requires access to the WDF framework that would be alright, since I already have communication set up between the WDDM and filter drivers and could use the filter to push the info back to the WDDM driver.
the device interface path should be considered opaque and you should never reconstruct it on your own. There is a DEVPKEY that allows you to retrieve the file name but I don’t think you have to do this either. When you register the device interface you can provide a ReferenceString value. This is appended to the file name
you could provide this reference string value in the WDDM escape channel and then your application can simply findstr for the reference string in the device interface path (don’t parse it, just look for the substring).
You’re suggesting that I still need to do the SetupDiEnumDeviceInterfaces loop, but instead of just hoping I get the correct device path that matches up to the WDDM device, using this ReferenceString to ensure the correct one is selected? That would absolutely solve one portion of this, thank you!
For the other (less-necessary, but highly-“would really prefer”) part, Is there no way I can directly grab the device path (with interface GUID) from the WDF framework? I was hoping to just pass the string directly from the filter driver to the WDDM driver, and then have usermode use the Escape channel to get that string as-is. No manual manipulation of the path would be required if that were possible. I’m trying out DevicePropertyPhysicalDeviceObjectName, but I have a feeling I’d still need to manually tack on the interface GUID which isn’t good if that path is meant to be opaque.
I suppose another solution might be to do all of this enumeration inside the Escape mechanism when it’s called from usermode, and return the path there?
I don’t mind having to use two different API to get access to the two different driver interfaces here, I just would prefer to do so in a way that makes it as fool-proof as possible from the usermode side. I’m going to be the fool dealing with this, so anything I can do to make it easier on myself is always a good idea! I also really like the idea of enforcing the concept that these are really just two aspects of the “same” driver and should always be treated as such. It makes no sense for usermode code to only get access to the WDDM portion, nor does it make sense to only get access to the filter portion. Making it so that there is only one way to get access to both parts of the driver does sort of “hide” the hacky two-driver solution that I’m using here.
WdfDeviceRetrieveDeviceInterfaceString will return the interface string. This is the kernel mode symbolic link, it is not compatible with user mode (the symbolic link name spacing is a bit different) and would need to be transformed in place (fragile and error prone) to be UM compatible. the reference string I originally mentioned still requires you to enumerate instances of the interface and look for the reference string in the resulting symbolic link name returned.
Am I just very, very lucky or does the interface string returned by WdfDeviceRetrieveDeviceInterfaceString just work as-is in user mode? Because I’ve just copied its output which I’m logging from the driver into my usermode test app, and I can use it as-is to open handles to the various device objects exposed by the interface in question! Nice!
Assuming Doron doesn’t come back and tell me this is insanely risky is is guaranteed to break if the wind blows, I think this is a perfect solution for what I’m trying to do.