USB Kernel Filter Driver -- correct approach?

Hello all,

I have been digging through the web and slowly coming up to speed on WinUsb, libusb-win32, driver development, the WinDDK, etc. I have seen some of the examples available, and tried a few driver installations of my own. I need to come up with something within a week or two, however, so I thought I would ask if anyone could help me know if I am going in the right direction.

I have a composite USB device with 7 endpoints that has an existing, closed-source driver. That driver appears to create a virtual HID device as a “child” in the device manager chain, with a final HID-compliant item below that. The overall device is not a HID device, however, as far as I know.

I am trying to create my own driver or utility to add some functionality to this existing driver, since the device is complex enough that creating my own driver (or extending the work of others) seems nearly impossible if I want to maintain compatibility. Specifically, I need to be able to send control requests to some specific interfaces, write a few bytes to a pipe or two, and most importantly, read interrupt-endpoint messages from an endpoint that the existing driver shouldn’t (as far as I know) even be messing with.

So, what I need is a way to not affect the existing driver’s USB commands, but send control packet/read/write USB requests so that a user-mode program can say, “Hey, turn on those lights that the closed-source driver does not support.” With WinUsb as the driver for the device, everything now works great, and I can operate the extra functionality that I want. The problem is that I also need the existing functionality to be unchanged, and as far as I can tell, a (kernel) filter driver seems to be my only remaining hope.

Does this assessment seem accurate? Is this likely to be an extremely difficult task since I am only familiar with WinUsb-level USB commands, not low-level URB stuff? Most importantly, is there any good starting point in the WinDDK if this is feasible? I have heard the toaster generic filter driver is decent, but I do not know of any general kernel-level USB filter example, especially one that needs to pass through lots of complex operations for a composite device with the virtual HID subdevice etc.

Thanks very much for your time in reading this!

xxxxx@gmail.com wrote:

…So, what I need is a way to not affect the existing driver’s USB commands, but send control packet/read/write USB requests so that a user-mode program can say, “Hey, turn on those lights that the closed-source driver does not support.” With WinUsb as the driver for the device, everything now works great, and I can operate the extra functionality that I want. The problem is that I also need the existing functionality to be unchanged, and as far as I can tell, a (kernel) filter driver seems to be my only remaining hope.

Does this assessment seem accurate?

That’s certainly the approach I would take. You could pop an upper or
lower filter on the composite device to do this. You’ll need to monitor
the SelectConfiguration and SelectInterface messages so you can snaggle
the pipe handle for the pipe you’re interested in. You can send control
pipe vendor commands at any time.

Is this likely to be an extremely difficult task since I am only familiar with WinUsb-level USB commands, not low-level URB stuff?

The USB concepts don’t change. For the most part, your WinUSB calls map
one-for-one to URBs. It’s just a matter of finding the right spelling.

Most importantly, is there any good starting point in the WinDDK if this is feasible? I have heard the toaster generic filter driver is decent, but I do not know of any general kernel-level USB filter example, especially one that needs to pass through lots of complex operations for a composite device with the virtual HID subdevice etc.

You’re not really talking about “complex operations” here. You can
ignore (meaning “pass through”) all of the commands coming from above,
except for grabbing the pipe handle you need. The Toaster sample is a
great place to start for a filter driver, and KMDF makes it pretty
darned easy. There are other samples that show how to use a USB I/O target.

If you’re interested in having someone else do the basic work for you,
contact me off list, but I suspect you won’t have much trouble.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

(and now a word from your list slave)

To EVERYONE on the list: I know we’ve allowed some posts like this in the past. especially when such a solicitation is added to the end of a VERY helpful posting such as this one. The reply adds real value, and actually HELPS the OP with their problem. AND it’s from somebody who has an excellent reputation from offering help to the community (which counts for a lot). The solicitation is thus an afterthought, an incidental extension to an otherwise helpful post.

However… in the recent weeks we’ve seen an increasing number of posts containing solicitations (such as “see my blog at …” or whatever) that have drawn complaint emails from members. The list slaves have dealt with those situations individually as they’ve occured.

Thus, in the interest of fairness, I’d appreciate it if in the spirit of non-commercialization we can avoid ALL types of solicitations on the list… even those from well-known community leaders with excellent reputations, added as an afterthought to a very helpful posting.

Thanks,

Peter
OSR

Thanks very much, Tim.

Thanks to the Toaster example and a nice OSR Online guide for a filter driver (http://www.osronline.com/article.cfm?id=446), I have gotten skeleton code up and running, and now it’s time for the actual USB part. I do have one more quick question. Given what I have described, I wanted to make sure – there is no way to do this level of filtering and processing in userspace, correct?

There is the osrusbfx2 UMDF USB filter driver example, but from a cursory glance it seems to require WinUSB. If there is a way to do this without using (an initially unsigned) kernel driver, that would be amazing.

xxxxx@gmail.com wrote:

Thanks to the Toaster example and a nice OSR Online guide for a filter driver (http://www.osronline.com/article.cfm?id=446), I have gotten skeleton code up and running, and now it’s time for the actual USB part. I do have one more quick question. Given what I have described, I wanted to make sure – there is no way to do this level of filtering and processing in userspace, correct?

There is the osrusbfx2 UMDF USB filter driver example, but from a cursory glance it seems to require WinUSB. If there is a way to do this without using (an initially unsigned) kernel driver, that would be amazing.

A kernel driver is required in order to talk to USB devices. There is
no way around that. It can be a custom driver, or it can be WinUSB, or
it can be WinUSB via a UMDF driver, but there has to be a kernel component.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Thanks very much for the information. I got the filter working and can observe URB submit internal IOCTLs. As far as I can tell, practically everything the existing driver does is an URB submit, so I should be able to handle it without an excessive amount of trouble.

I did run into a problem with a completion routine though, and I feel like I must be missing something obvious. I am trying to use a completion routine so I can intercept certain messages back up to the top level driver, and I first just added the completion routine with WdfRequestSetCompletionRoutine. At that point the device stopped working completely, so I realized (please correct me if I’m wrong) that I needed to complete the requests myself, in the completion routine, so that they would go back up the stack. I think at that point, my computer started freezing when I would plug in the device, and I found I apparently needed to call WdfRequestFormatRequestUsingCurrentType(). THAT seemed to work, and the completion routine got called…except it did not seem to have the parameter types and ioctl number I was seeing on the way down.

After a little more searching, I saw someone mention WdfIoTargetFormatRequestForIoctl, so I tried that instead. Following the MSDN documentation for forwarding, I set InputBuffer and OutputBuffer to the WDFMEMORY object I got from calling WdfRequestRetrieveInputMemory(), and WdfRequestRetrieveOutputMemory(). I set up the ioctl code to the one I was already using, and ran the driver. At this point, I got my first BSOD, and I have not yet tried to see if I can trace it. It was somewhere in a WDF DLL if I recall correctly.

I am sorry if that was not detailed enough. Basically, is there some simple gotcha I may be missing for trying to route an internal IOCTL through a completion routine and back up the stack? Should I stick with WdfRequestFormatRequestUsingCurrentType? If not, could I have messed something up with the WDFMEMORY objects (i.e. do I need to do special allocation on them instead of just using local variables and treating them like handles)?

Any advice is greatly appreciated. Thanks for the quick responses and all the information on this site/mailing list. :slight_smile:

Tim wrote,

You’ll need to monitor the SelectConfiguration and SelectInterface messages so you can snaggle the pipe handle for the pipe you’re interested in. You can send control pipe vendor commands at any time.

I have almost gotten past this point, but I am having trouble understanding what is the “correct” KMDF way to do this. I was not intending to use commands like WdfUsbTargetDeviceCreate to initialize structures and configure the device myself, since I can merely monitor the select configuration and select interface messages that the driver above me is doing. In monitoring the replies to select configuration, however, is the “correct” thing to do, to use the USBD_INTERFACE_INFORMATION member of the UrbSelectConfiguration to gather information for all of the interfaces?

It looks like I could do pointer math using the Length field, but I am concerned because of the description of the MSDN documentation on _URB_SELECT_CONFIGURATION:

"Specifies a variable length array of USBD_INTERFACE_INFORMATION structures, each describing an interface supported by the configuration being selected.

On return from the host controller driver, this member contains a USBD_INTERFACE_INFORMATION structure with data describing the capabilities and format of the endpoints within that interface."

Does this imply that there will only be information for ONE interface when it returns? If not, should I just do pointer arithmetic, or go ahead and include the usbd library so I can use USBD_ParseConfigurationDescriptorEx? I ask all these silly questions because I feel like using the USBD library would be bad in this case, and there is some KMDF method that I ought to be using instead.

The other alternative to the whole issue would actually be issuing the select configuration command myself via the WDF API, but I did not want to make myself the middleman in that operation as well, unless I had to. Is it best to go ahead and start down that path, blocking the select configuration command and working out how to properly simulate its response to the top-level driver, so I can use all the WDF structures and API for pipe communication?

xxxxx@gmail.com wrote:

In monitoring the replies to select configuration, however, is the “correct” thing to do, to use the USBD_INTERFACE_INFORMATION member of the UrbSelectConfiguration to gather information for all of the interfaces?

Sure.

It looks like I could do pointer math using the Length field, but I am concerned because of the description of the MSDN documentation on _URB_SELECT_CONFIGURATION:

“Specifies a variable length array of USBD_INTERFACE_INFORMATION structures, each describing an interface supported by the configuration being selected.

On return from the host controller driver, this member contains a USBD_INTERFACE_INFORMATION structure with data describing the capabilities and format of the endpoints within that interface.”

Does this imply that there will only be information for ONE interface when it returns?

A SELECT_INTERFACE call will have one interface. A SELECT_CONFIGURATION
call should have all interfaces, if you are below usbccgp.sys.
Usbccgp.sys lets the drivers above it think they are running with a
single-interface.

If not, should I just do pointer arithmetic, or go ahead and include the usbd library so I can use USBD_ParseConfigurationDescriptorEx? I ask all these silly questions because I feel like using the USBD library would be bad in this case, and there is some KMDF method that I ought to be using instead.

There is no KMDF method for this, as far as I know. You should just use
pointer arithmetic. Remember that you’re not parsing the configuration
descriptor here. It’s an array of USBD_INTERFACE_INFORMATION structures.

The other alternative to the whole issue would actually be issuing the select configuration command myself via the WDF API, but I did not want to make myself the middleman in that operation as well, unless I had to. Is it best to go ahead and start down that path, blocking the select configuration command and working out how to properly simulate its response to the top-level driver, so I can use all the WDF structures and API for pipe communication?

Both ways will work. Not sure I can say which is better.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Thanks very much for the response. I am trying not to be rude and bump my own questions, especially since they are probably so simple.

I have almost gotten the method working where I issue the select configuration and select interface commands myself, by intercepting the URBs from the top-level driver. Unfortunately, I get a BSOD in the top-level driver itself (for which I don’t have source or debug info as far as I know) if I successfully complete the select interface request. I am doing the work in my internal IOCTL handler as well. I don’t know if that could cause problems.

I use this code to configure the device for a URB_FUNCTION_SELECT_CONFIGURATION URB:

WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_URB(&usbDevConfigurationParams, existingURB);
apiStatus = WdfUsbTargetDeviceSelectConfig(devContext->usbDevice, WDF_NO_OBJECT_ATTRIBUTES, &usbDevConfigurationParams);

I use this code to configure the device for a URB_FUNCTION_SELECT_INTERFACE URB, after I get a WDFUSBINTERFACE with the matching interface number:
WDF_USB_INTERFACE_SELECT_SETTING_PARAMS_INIT_URB(&usbInterfaceConfigurationParams, existingURB);
apiStatus = WdfUsbInterfaceSelectSetting(
usbInterface,
WDF_NO_OBJECT_ATTRIBUTES,
&usbInterfaceConfigurationParams);

Both of the calls succeed, but the second one causes an instant BSOD if I return successfully. If I return STATUS_INTERNAL_ERROR instead, then the driver unloads due to the failure, but there is no BSOD. Is there anything special about the select interface call? The only thing I can think of is to forward the select interface call, parse the data in the URB when it returns, and then compare it to the data in the URB when WdfUsbInterfaceSelectSetting returns.

Thanks very much to anyone who takes the time to read this!

After further digging, it APPEARS that WdfUsbInterfaceSelectSetting does not fill in the URB structure that I am passing in, from the other driver. It just presumably updates the alternate setting (0) and returns success. The URB is still nearly all 0’s, which presumably freaks out the higher-level driver when it comes back up.

If instead of calling WdfUsbInterfaceSelectSetting I forward the request on and monitor its completion, I see the URB structure has been filled in with the interface and endpoint details. I am not investigating how I might fill in the URB manually so that the higher-level driver will get what it expects.

Someone please enlighten me if I am confused. I suppose my confusion arose because WdfUsbTargetDeviceSelectConfig updates the URB as far I can tell from the documentation and a comment of someone on this mailing list at one point. I had hoped that WdfUsbInterfaceSelectSetting would do the same thing. I will report back if manually filling out the URB with the current interface configuration makes things work.

xxxxx@gmail.com wrote:

I use this code to configure the device for a URB_FUNCTION_SELECT_CONFIGURATION URB:

WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_URB(&usbDevConfigurationParams, existingURB);
apiStatus = WdfUsbTargetDeviceSelectConfig(devContext->usbDevice, WDF_NO_OBJECT_ATTRIBUTES, &usbDevConfigurationParams);

I use this code to configure the device for a URB_FUNCTION_SELECT_INTERFACE URB, after I get a WDFUSBINTERFACE with the matching interface number:
WDF_USB_INTERFACE_SELECT_SETTING_PARAMS_INIT_URB(&usbInterfaceConfigurationParams, existingURB);
apiStatus = WdfUsbInterfaceSelectSetting(
usbInterface,
WDF_NO_OBJECT_ATTRIBUTES,
&usbInterfaceConfigurationParams);

If you are passing on a request from above, you do NOT want to create
your own request. You just need to pass along the request you
received. Then, in the completion routine, you extract the information
you need before you send it back up.

You are eavesdropping – you shouldn’t be creating anything yourself.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Thanks for the reply. The reason I was trying to use WdfUsbInterfaceSelectSetting was because of the discussion in this thread:

http://www.osronline.com/showthread.cfm?link=185089

See message 15. I believe Doron suggested that configuring the device and interface by proxy, as it were, should be reasonable. My problem is that the top-level driver apparently expects its URB to be fully fleshed-out when it goes back up. After digging through the internet and MSDN, I feel like it is not possible to get the proper USBD handles that submitting the URB would fill in.

My current workaround is to run the select device configuration call myself, when I intercept the select device configuration URB. The WDF function either that fills out the URB, or the URB coming in was already properly formed, or else the top-level driver is fine with it. Then, when I see a select interface URB, I forward it on and let the normal process handle that.

As long as this process is safe, I suppose I am okay with using it. The result is that I have a WDF USB device and can presumably use the WDF API to read and write data to pipes. It remains to be seen whether there is a problem if I use the WDF API to write to the same pipe that the top-level driver sometimes sends URBs to write to. I suspect I will have to do some interception code to prevent the top-level driver from seeing some of the data into and out of the device, however.

I had used the workaround, but then I realized that I might be in danger of a device selecting a different alternate interface, and new pipe handles being created. I thought I had no alternatives to going to the non-WDF API or depending on the original pipe handles I created to still work, but tonight I finally figured out a way to make WdfUsbInterfaceSelectSetting work.

I realized that the one time I -do- get USBD_INTERFACE_INFORMATION data is after I call WdfUsbTargetDeviceSelectConfig, so I used pointer arithmetic to save pointers to the individual structures.

Then, after I called WdfUsbInterfaceSelectSetting , I just copied the exact binary data from the saved pointer (from my context) into the USBD_INTERFACE_INFORMATION member of the _URB_SELECT_INTERFACE structure. Once I had filled in the URB I intercepted in this fashion, no bluescreen happened, the URB contents look good, and I have device, interface, and pipe etc. information, all with the WDF API. :slight_smile: Since I now own the device by intercepting the configuration messages, I should be able to complete my project.

One of the only remaining tricky parts I see is intercepting interrupt transfer URBs. I would like my driver to have a setting that can be enabled, where the incoming data from one endpoint goes to userspace via my driver interface, rather than to the top-level driver I am extending. Since the top-level driver is continuously doing reads, however, I will have to see if using the WDF API to make a continuous reader (or another approach) will preempt the top-level driver, or whether I will have to figure out some way to prevent the driver from completing early. Perhaps there is something I could do in the completion routine.