Installation of USB video class filter driver

Hi,

I am very new in windows driver development. Please forgive my basic questions.

Following the toaster class filter driver and the generic KMDF driver here http://www.osronline.com/article.cfm?article=446, I have written a KMDF filter driver for USB video class, only to check whether the Streaming data passes through this filter or not. For now, I am not particularly interested to process any streaming data.

Now I want to install the filter driver as a lower filter driver for USB video class. I am not getting how to install the filter driver and where to install. In device manager, I see ‘usbvideo.sys’ for imaging devices. Under sound, video and gaming controller, I see ‘portcls.sys’, ‘usbaudio.sys’ and ‘dmk.sys’. If I make my usb webcam on and want to filter the video, to which driver I should install the filter driver and how? Please help me.

Thanks for your patience.

xxxxx@gmail.com wrote:

Following the toaster class filter driver and the generic KMDF driver here http://www.osronline.com/article.cfm?article=446, I have written a KMDF filter driver for USB video class,…

First, I want to make sure you understand that the word “class” is used
in two different ways in that sentence. “Toaster class” is a Windows
install class. That filter will run on all devices that install
themselves in the “toaster install class”. “USB Video Class” is the
name of a specification from the USB Implementor’s Forum. You cannot
install a “class filter” for USB Video Class devices. USB Video Class
devices are sometimes installed in the “image” install class, and
sometimes in the “media” install class.

What you want to do is install a DEVICE lower filter for your specific
device.

only to check whether the Streaming data passes through this filter or not. For now, I am not particularly interested to process any streaming data.

The streaming data will pass through the filter, as URBs (USB request
blocks). URBs are sent as IRP_MJ_INTERNAL_DEVICE_CONTROL requests. The
URB will be for URB_FUNCTION_ISOCH_TRANSFER.

What do you plan to do with the video here? Usbvideo.sys expects to
receive USB packets in the UVC format.

Now I want to install the filter driver as a lower filter driver for USB video class. I am not getting how to install the filter driver and where to install. In device manager, I see ‘usbvideo.sys’ for imaging devices. Under sound, video and gaming controller, I see ‘portcls.sys’, ‘usbaudio.sys’ and ‘dmk.sys’. If I make my usb webcam on and want to filter the video, to which driver I should install the filter driver and how? Please help me.

There are two parts to this. First, you have to create a service for
your driver. You can do that using the “sc” tools. Copy your binary
into \Windows\System32\Drivers, then run:
sc create yourservicename type= kernel start= demand binPath=
system32\drivers\yourbinary.sys

Then, you have to install that service as a lower filter for the
device. That’s done via the registry. If you are on XP, you can modify
the registry by hand. You would find the entry in
HKLM\System\CurrentControlSet\Enum\USB, in the subkey that matches your
camera’s VID and PID. You would add a new entry called LowerFilters of
type REG_MULTI_SZ, and enter your service name. Then, plug in the device.

On systems newer than XP, you don’t have permission to write to those
registry keys. You need to use a small application, using these APIs:
SetupDiGetClassDevs
SetupDiEnumDeviceInfo
SetupDiGetDeviceRegistryProperty
SetupDiSetDeviceRegistryProperty
SetupDiDestroyDeviceInfoList
It’s wordy, but not difficult.

Having said all that, there IS another choice. IF you are only doing
this for experimenting on one computer, AND you know that your device is
installed in “image” class, AND you know that you don’t have any other
devices in the “image” class, you can sneak by with installing this as a
class lower filter. After you have created the service with “sc”, you
can do that using “devcon” from the WDK:
devcon classfilter image upper +yourservicename


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

Hi Tim Roberts,

Thanks a lot for your quick reply. I am working in Windows 7 with Visual Studio beta 2011 and WDK 8. As you said, I created a service with ‘sc’ and then could change the camera subkey in registry entry HKLM\System\CurrentControlSet\Enum\USB. And I could install the video lower filter. Thanks for your step by step instruction. Before being able to change the registry keys, I also tried ‘devcon’ but it didn’t work for windows 7.

For now, I just wanted to see whether I can install the filter as a lower filter to the usbvideo. I am using DebugView to see the ‘DbgPrint’ messages from the filter driver. Now I changed the filter driver (just added some more comments) and wanted to integrate the filter again. First, I put the new .sys file in the windows\system32\drivers but I don’t think that worked. I could not see my new debug messages in the ‘DebugView’. Then, I deleted my service ‘videolowerfilter’ and tried to create the service again. Now, it shows that ‘the service is marked for deletion’. Can you please tell me what to do to reflect any change in my ‘.sys’ file?

Besides this, is there any way to see the debug print messages or to see that the filter is properly installed? Thanks a lot for your patience and help.

xxxxx@gmail.com wrote:

Thanks a lot for your quick reply. I am working in Windows 7 with Visual Studio beta 2011 and WDK 8. As you said, I created a service with ‘sc’ and then could change the camera subkey in registry entry HKLM\System\CurrentControlSet\Enum\USB. And I could install the video lower filter. Thanks for your step by step instruction. Before being able to change the registry keys, I also tried ‘devcon’ but it didn’t work for windows 7.

Devcon works just fine, as long as you use the 64-bit devcon on a 64-bit
system, and as long as you are in a “run as administrator” command shell.

For now, I just wanted to see whether I can install the filter as a lower filter to the usbvideo. I am using DebugView to see the ‘DbgPrint’ messages from the filter driver. Now I changed the filter driver (just added some more comments) and wanted to integrate the filter again. First, I put the new .sys file in the windows\system32\drivers but I don’t think that worked. I could not see my new debug messages in the ‘DebugView’. Then, I deleted my service ‘videolowerfilter’ and tried to create the service again. Now, it shows that ‘the service is marked for deletion’. Can you please tell me what to do to reflect any change in my ‘.sys’ file?

Your driver is only loaded when the device is newly recognized. So,
when you replace the .sys file, you need to restart the device. You can
do that either by unplugging and replugging, or doing “Disable” and
“Enable” in Device Manager, or (the best way) by using
devcon restart “usb\vid_xxxx&pid_xxxx”

The quotes are required because & is a special character to the command
interpreter. I usually embed that command in a batch file called
“restart.bat” so I only have to type that one word.

Besides this, is there any way to see the debug print messages or to see that the filter is properly installed? Thanks a lot for your patience and help.

I’m not sure what you mean. You could hook up a kernel debugger and
check the device stack for your device, but it’s a lot easier to use
DebugView.


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

Thanks a lot. It worked. I should have tried these obvious things actually.

Hi,

Since I installed the usb video lower filter driver, I am trying to get and print the webcam data in my ‘EvtIoInternalCompletion’ routine. My code is as follows


VOID
KMDFDriver1EvtIoInternalDeviceControl(
In WDFQUEUE Queue,
In WDFREQUEST Request,
In size_t OutputBufferLength,
In size_t InputBufferLength,
In ULONG IoControlCode
)
{
WDFDEVICE device;
PIRP wdmIrp;
WDF_REQUEST_PARAMETERS params;

UNREFERENCED_PARAMETER( Queue);

//
// Get the WDM IRP
//
wdmIrp = WdfRequestWdmGetIrp(Request);
WDF_REQUEST_PARAMETERS_INIT(&params);
WdfRequestGetParameters(Request, &params);

WdfFltrTrace((“IRP-0x%p; InputBufferLength-%ld; OutputBufferLength-%ld; I/O Control
Code-0x%x;\n”,
wdmIrp, (long)InputBufferLength, (long)OutputBufferLength, IoControlCode));

device = WdfIoQueueGetDevice(Queue);
FilterForwardRequestWithCompletion(Request, WdfDeviceGetIoTarget(device),
InternalIOCompletionRoutine, WDF_NO_CONTEXT);

return;
}

void InternalIOCompletionRoutine(
__in WDFREQUEST Request,
__in WDFIOTARGET Target,
__in PWDF_REQUEST_COMPLETION_PARAMS Params,
__in WDFCONTEXT Context
){
PIRP wdmIrp;

UNREFERENCED_PARAMETER(Target);
UNREFERENCED_PARAMETER(Context);

wdmIrp = WdfRequestWdmGetIrp(Request);
WdfFltrTrace(("MDL adress-%p; IRP User Buffer Address-%p; IRP SystemBuffer
Address-%p;\n ", wdmIrp->MdlAddress, wdmIrp->UserBuffer,
wdmIrp->AssociatedIrp.SystemBuffer));

/*
WdfFltrTrace((“IRP-%p; Status-0x%x; Type-0x%x; Size-0x%x\n”,
wdmIrp, Params->IoStatus.Status,
Params->Type, Params->Parameters.Ioctl.Output.Offset));
*/

WdfRequestComplete(Request, Params->IoStatus.Status);

return;
}

VOID
FilterForwardRequestWithCompletion(
IN WDFREQUEST Request,
IN WDFIOTARGET Target,
IN PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine,
IN WDFCONTEXT CompletionContext
)
{
NTSTATUS status;

WdfRequestFormatRequestUsingCurrentType(Request);
WdfRequestSetCompletionRoutine(Request, CompletionRoutine, CompletionContext);

if(!WdfRequestSend(Request, Target, WDF_NO_SEND_OPTIONS)){
status = WdfRequestGetStatus(Request);
WdfFltrTrace((“WdfRequestSend failed - 0x%x\n”, status));
WdfRequestComplete(Request, status);
}
return;
}

However, my debug output is


Filter!KMDFDriver1EvtIoInternalDeviceControl: IRP-0x854DDE28; InputBufferLength-0; OutputBufferLength–2065253848; I/O Control Code-0x220003;

Filter!InternalIOCompletionRoutine: MDL adress-00000000; IRP User Buffer Address-00000000; IRP SystemBuffer Address-00000000;

All the data buffers are null. Even if when I print Params->Type, it shows 0xff which means ‘WdfRequestTypeNoFormat’. The Params->Parameters.Ioctl.Output.Offset prints 0x0. The UsbCompletionParams in the WDF_REQUEST_Completion_Params is also null.

Do I have to format the request while forwarding or do anything specific to USB? Please help me with some directions. Thanks for your patience and support.

xxxxx@gmail.com wrote:

Since I installed the usb video lower filter driver, I am trying to get and print the webcam data in my ‘EvtIoInternalCompletion’ routine. My code is as follows

However, my debug output is


Filter!KMDFDriver1EvtIoInternalDeviceControl: IRP-0x854DDE28; InputBufferLength-0; OutputBufferLength–2065253848; I/O Control Code-0x220003;

Filter!InternalIOCompletionRoutine: MDL adress-00000000; IRP User Buffer Address-00000000; IRP SystemBuffer Address-00000000;

All the data buffers are null.

As a lower filter to usbvideo.sys, what you are capturing here are URBs
– USB request blocks. They are sent via an internal ioctl that uses
the IRP fields in a custom way (which internal ioctls are allowed to
do). As a result, none of the IRP address fields have the values you
expect. The contract you’re expecting is the contract for external
ioctls. The ONLY field with any meaning is the pointer to the URB,
which lives in Parameters.Others.Argument1. The rest of the IRP is
nonsense.

Fetch the IRP, then use the URB_FROM_IRP macro to fetch the URB pointer
from Parameters.Others.Argument1. The URB then contains the function
code, a pointer to the buffer, and the IsoPacket array that you must use
to find the data in the buffer. Isochronous requests are not set up the
way you would expect (see my article here:
http://timr.probo.com/isoch.html ).


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

Hi Tim,

Thanks a lot! The URB headers I print are fine. I also tried to print the raw USB data from TransferBuffer using the information in IsoPacket array as mentioned in your article. The data in TransferBuffer[IsoPacket[0].offset] to TransferBuffer[IsoPacket[0].offset + IsoPacket[0].length] is perfect. However, though the number of packets is 128, I cannot access IsoPacket[127] or so. The best I could access is upto IsoPacket[80]. Besides, the offset and length information in IsoPacket[0] only seem to be valid. I printed IsoPacket[1].offset and length (and for some other indices too) which appear to be some huge garbage numbers. I cannot also print the data in other parts of the buffer using the IsoPacket information. I checked raw USB data using ‘USBLyzer’ which prints all the IsoPacket information and data correctly.

Do you think the problem is due to debug print latency? From my filter driver, I complete the request after printing all these information. Is it somehow completed before I print? Should I use some pipe from a user mode application to store the data? I also wanted to modify the raw data, but till now I cannot control the buffer fully. Please help me with some directions. Thanks for your patience and support.

xxxxx@gmail.com wrote:

Thanks a lot! The URB headers I print are fine. I also tried to print the raw USB data from TransferBuffer using the information in IsoPacket array as mentioned in your article. The data in TransferBuffer[IsoPacket[0].offset] to TransferBuffer[IsoPacket[0].offset + IsoPacket[0].length] is perfect. However, though the number of packets is 128,

Shouldn’t be, at least not with usbvideo.sys. It doesn’t queue up that
many packets, because the latency is too high. I believe it targets 32
packets per URB. Are you getting that number from the NumberOfPackets
field in the URB?

I printed IsoPacket[1].offset and length (and for some other indices too) which appear to be some huge garbage numbers.

Are you checking Status, too? Offset should always be valid, but Length
is only meaningful if Status is 0 for that packet.

I cannot also print the data in other parts of the buffer using the IsoPacket information. I checked raw USB data using ‘USBLyzer’ which prints all the IsoPacket information and data correctly.

Do you think the problem is due to debug print latency? From my filter driver, I complete the request after printing all these information. Is it somehow completed before I print?

No. The URB cannot be reused by the originating driver until you
complete it. There must be something else going on. Have you dumped
the raw URB in the debugger to see if it is sensible? You are looking
in a completion routine that you establishded?

I also wanted to modify the raw data, but till now I cannot control the buffer fully.

What do you plan to do to the data?


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

Hi,

I feel like that there is no problem with ‘TransferBuffer’ but with reading the ‘IsoPacket’ array since the array elements are not printing right. Here is what I do in my ‘InternalIOCompletionRoutine’


void InternalIOCompletionRoutine(
__in WDFREQUEST Request,
__in WDFIOTARGET Target,
__in PWDF_REQUEST_COMPLETION_PARAMS Params,
__in WDFCONTEXT Context
){

wdmIrp = WdfRequestWdmGetIrp(Request);
purb = (PURB) URB_FROM_IRP(wdmIrp);
isochTransferMsg = purb->UrbIsochronousTransfer;

if ( purb->UrbHeader.Function == URB_FUNCTION_ISOCH_TRANSFER){
buffer = (PUCHAR) isochTransferMsg.TransferBuffer;
offset = isochTransferMsg.IsoPacket[0].Offset;

WdfFltrTrace((“Data Transfer in %lu Packets over %lu Byte Buffer with Error Count %lu.”,
isochTransferMsg.NumberOfPackets, isochTransferMsg.TransferBufferLength, isochTransferMsg.ErrorCount));

DbgPrint(“Offset[0]-%lu, Length[0]-%lu, Status[0]-%u”\
“Offset[1]-%lu, Length[1]-%lu, Status[1]-%u”, isochTransferMsg.IsoPacket[0].Offset, isochTransferMsg.IsoPacket[0].Length, isochTransferMsg.IsoPacket[0].Status,
isochTransferMsg.IsoPacket[1].Offset, isochTransferMsg.IsoPacket[1].Length, isochTransferMsg.IsoPacket[1].Status);

WdfRequestComplete(Request, Params->IoStatus.Status);

The DbgPrint messages are

Data Transfer in 128 Packets over 271288 Byte Buffer with Error Count 0.
Offset[0]-0, Length[0]-1976, Status[0]-0
Offset[1]-7411402, Length[1]-2192738528, Status[1]-2309269237

The IsoPacket[0] elements are perfect. I also printed the USB raw data starting at IsoPacket[0].Offset to IsoPacket[0].Length and matched to ‘USBLyzer’ output. This data is good. But other IsoPacket array entries are garbage as you see in the debug prints. So when I go to print the ‘TransferBuffer’ contents using IsoPacket[1].offset, there is memory corruption and my computer crashes. Please tell me whether I am doing right in case of reading ‘IsoPacket’ array. In ‘USBLyzer’, I see all the 128 IsoPacket array entries printed correctly. I could not enable the debugging and cannot see more apart from the debug prints.

I am using ‘Microsoft Lifecam’ camera. In device manage, I see the drivers installed are ‘usbvideo.sys’, ‘nx6000.sys’ and my filter driver. In registry entries, I see MSHUSBVideo as lower filter. I put my filter below it. So, I guess my filter driver gets the data first from the USB hub.

I randomly changed the contents in ‘TransferBuffer’ (within IsoPacket[0].offset - IsoPacket[0].length definitely) and found that if I change the first byte the video screen goes blank. Is the first byte any kind of video header? I want to redirect the ‘TransferBuffer’ pointer to one of my own buffer of the same size. Is it possible after the control message transfer and negotiation?

Thanks for your replies. Your comments about the IsoPacket array will benefit me a lot.

One more thing - I am using USB3 which I see is different from USB2 in case of data transfers. In that case, reading the IsoPacket array would be same in USB 3?

xxxxx@gmail.com wrote:

I feel like that there is no problem with ‘TransferBuffer’ but with reading the ‘IsoPacket’ array since the array elements are not printing right. Here is what I do in my ‘InternalIOCompletionRoutine’


void InternalIOCompletionRoutine(
__in WDFREQUEST Request,
__in WDFIOTARGET Target,
__in PWDF_REQUEST_COMPLETION_PARAMS Params,
__in WDFCONTEXT Context
){

wdmIrp = WdfRequestWdmGetIrp(Request);
purb = (PURB) URB_FROM_IRP(wdmIrp);
isochTransferMsg = purb->UrbIsochronousTransfer;

Aha! A very subtle problem, and I hope I get extra credit for spotting it.

The important line that you did not include here is this:
struct _URB_ISOCH_TRANSFER isochTransferMsg;

The problem with the method you have chosen is that this declares a
structure, not a pointer to structure, Your last line of code above is
COPYING the isochronous part of the URB. So, what’s the problem with
that? Well, look at the last line of _URB_ISOCH_TRANSFER:

USBD_ISO_PACKET_DESCRIPTOR IsoPacket[1];

This is a variable-length structure. In reality, your URB is much
longer than a struct _URB_ISOCH_TRANSFER, because the IsoPacket array
has many more entries than just one. The compiler cannot know how many
entries there really are (you told it there was 1). Because you
declared the structure directly, you are only copying the first entry.

So, use a pointer instead:

struct _URB_ISOCH_TRANSFER * isochTransferMsg;

isochTransferMsg = &purb->UrbIsochronousTransfer;

offset = isochTransferMsg->IsoPacket[0].Offset;

and you will be much happier.


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

xxxxx@gmail.com wrote:

One more thing - I am using USB3 which I see is different from USB2 in case of data transfers. In that case, reading the IsoPacket array would be same in USB 3?

I’m guessing your camera is actually a USB 2 device. In that case, the
fact that you’re plugged in to a USB 3 bus is irrelevant.

Prior to Windows 8, usbvideo.sys doesn’t know how to talk to USB 3
isochronous pipes. The descriptor layouts are different.


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

Regarding the IsoPacket array, extremely sorry to bother. I fixed it, it was a simple programming mistake. I now want to redirect the USB data buffer pointer to a different buffer from the filter driver. I am studying about it, but any advice or knowledge sharing regarding its feasibility or understanding the video data format from the raw USB data would be extremely helpful. Thanks for your patience and support.

I guess, we both were posting at the same time :-D. I appreciate you a lot for pointing out the problem, it was really a subtle mistake. I also figured out later on that my camera is a USB 2.0 one. Anyway, a huge thanks to you for everything.

xxxxx@gmail.com wrote:

I now want to redirect the USB data buffer pointer to a different buffer from the filter driver. I am studying about it, but any advice or knowledge sharing regarding its feasibility or understanding the video data format from the raw USB data would be extremely helpful.

You can’t change the upper driver’s pointer. The buffer was allocated
by that driver, and he expects to find the data in HIS buffer. Your
only option is to copy your data into his buffer. If your data is of a
different size, then you will need to adjust the contents of the
IsoPacket array to reflect your data.

The format of the streaming data is all spelled out in the USB Video
Class specifications, and it depends on the format that was negotiated
in the Probe/Commit sequence before streaming began. Every packet has a
header (of variable size, from 2 to 14 bytes), and the rest of the
packet is just frame data or streaming data, depending on the format.

What do you expect to do to the data? If you are completely replacing
the camera’s data, there’s no point in getting the camera involved at all.


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

Hi,

Thanks for the transfer buffer related information. Regarding the video streaming data format, I went through UVC 1.1 spec. Now, I can understand different descriptors, which interface is being selected and can also identify the payload header in case of video transmission for my webcam. For example, 3 transactions per microframe in each 125 us, 12 byte payload header, etc.

However, I still cannot get the format (MJPEG, YUV, etc) of the video data. In the UVC spec, I see that the ‘video data format’ is determined in the ‘Video Probe and Commit Control’ negotiation process. In usblyzer, I see many ‘class interface and control transfers’ before the ‘Isoch trasnfers’ start. From the ‘bRequest’ field in the URB, I can understand some of the control transfers, such as GET_DESCRIPTOR etc with standard request codes. There are also many control transfers with custom request codes (bRequest = 0x 81 / 0x85 etc) . What are these control transfers for? Actually I wanted to know which control transfers are used for ‘Video Probe and Commit Control’ or how this process is done. How can I know the transferred video data format?

Thanks for your patience and support.

xxxxx@gmail.com wrote:

However, I still cannot get the format (MJPEG, YUV, etc) of the video data. In the UVC spec, I see that the ‘video data format’ is determined in the ‘Video Probe and Commit Control’ negotiation process. In usblyzer, I see many ‘class interface and control transfers’ before the ‘Isoch trasnfers’ start. From the ‘bRequest’ field in the URB, I can understand some of the control transfers, such as GET_DESCRIPTOR etc with standard request codes. There are also many control transfers with custom request codes (bRequest = 0x 81 / 0x85 etc) . What are these control transfers for? Actually I wanted to know which control transfers are used for ‘Video Probe and Commit Control’ or how this process is done. How can I know the transferred video data format?

I’m sure I’ve asked you this before, but I’m going to ask again. Do you
have the USB Video Class specification? Have you read the section on
probe and commit (4.3.1) Everything you ask is spelled out in great
detail in the spec. You cannot hope to filter the UVC traffic without
reading the UVC spec. When they say “class-specific”, they mean UVC.

I am trapped between my innate desire to type up a long detailed
reference guide to the UVC probe/commit process, and my annoyance that
you seem not to have read the spec. I suggest you go read 4.3.1, and if
the process is still not clear (and that is possible – it is a bit
detailed), ask again.


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

Thanks for your suggestion. It appeared that I read the UVC specification but left out the most relevant and necessary part - ‘video probe and commit control’. I got a good idea about the negotiation process and could match the exchanged messages with the specification. Thanks!