Isochronous endpoints MSFT UDE

I am writing USB device emulator using Microsoft Usb Device Emulation framework.

So far I have implemented support for interrupt and bulk transfers, but isochronous transfers just do not work.

Driver sets up dynamic endpoints (UdecxUsbDeviceInitSetEndpointsType is called with UdecxEndpointTypeDynamic). So when virtual USB device is plugged (UdecxUsbDevicePlugIn), UDE framework calls callback for endpoint creation. There I create and associate queue with endpoint:

Here is a part of the code:

WDF_IO_QUEUE_CONFIG queueConfig;
WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchSequential);
queueConfig.EvtIoInternalDeviceControl = OnTestIoInternalControl;
queueConfig.EvtIoCanceledOnQueue = OnTestCancelled;
queueConfig.EvtIoDefault = OnTestDefault;
queueConfig.EvtIoDeviceControl = OnTestIoControl;
queueConfig.EvtIoRead = OnTestRead;
queueConfig.EvtIoResume = OnTestResume;
queueConfig.EvtIoStop = OnTestStop;
queueConfig.EvtIoWrite = OnTestWrite;

WdfIoQueueCreate(hubDevice, &queueConfig, &queueAttributes, &queue);

UdecxUsbEndpointCreate(&endpointInit, &endpointAttributes, &udecxEndpoint);
UdecxUsbEndpointSetWdfIoQueue(udecxEndpoint, queue);

Note: I have just extracted part of the code, to make this post as short as possible

For bulk and interrupt endpoints it works fine, but for isochronous endpoint no “Test” callbacks are called. From log I can see that UDE has created endpoint and queue. USBVIEW shows opened pipe for isochronous endpoint, but when I try to play some music (tested virtual USB is audio device) it just does not trigger any callbacks that I could use to transfer data.

Does anybody know is isochronous transfers supported at all by Microsoft UDE?
If so, is there any additional setup required to make this work?

Yes, UDE supports isochronous transfers but you need to be careful of certain pitfalls with the descriptors. Can you post your Configuration Descriptor here?

(Sorry for double or triple posting, somehow my post was deleted when I tried to edit)

I have a similar issue with an emulated audio device. Communication with default control endpoint is OK, but the ISO endpoint’s callback is only called once, then never again. Looks like something is blocking the queue, but I have no idea what.

Here is the configuration descriptor I use. This should be a simple stereo USB speaker with 16bit / 48 kHz plus volume and mute controls.

// Configuration Descriptor Type
0x9,                              // Descriptor Size
USB_CONFIGURATION_DESCRIPTOR_TYPE, // Configuration Descriptor Type
0x76, 0x00,                        // Length of this descriptor and all sub descriptors
0x02,                              // Number of interfaces
0x01,                              // Configuration number
0x00,                              // Configuration string index
0xA0,                              // Config characteristics - bus powered
0x32,                              // Max power consumption of device (in 2mA unit) : 0 ma

// Interface association descriptor
0x08,                                      // Descriptor size
USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE, // Descriptor Type (Interface Association)
0x00,                                      // bFirstInterface (number of first interface of the collection)
0x02,                                      // bInterfaceCount (number of interfaces in the collection)
USB_DEVICE_CLASS_AUDIO,                    // bFunctionClass (Audio)
0x01,                                      // bFunctionSubclass (Audio)
0x00,                                      // bFunctionProtocol (None)
0x00,                                      // iFunction

// Audio Interface descriptor (Control)
0x09,                                     // Descriptor size
USB_INTERFACE_DESCRIPTOR_TYPE,            // Descriptor type (Interface descriptor)
0x00,                                     // bInterfaceNumber
0x00,                                     // bAlternateSetting
0x00,                                     // bNumEndpoints
USB_DEVICE_CLASS_AUDIO,                   // bInterfaceClass (Audio)
0x01,                                     // bInterfaceSubClass (Control)
0x00,                                     // bInterfaceProtocol 
0x00,                                     // iInterface

// AudioControl Interface Descriptor (Header)
0x09,                                     // Descriptor size
0x24,                                     // bDescriptorType (CS Interface)
0x01,                                     // bDescriptorSubType (HEADER)
0x00, 0x01,                               // bdcADC
0x28, 0x00,                               // wTotalLength
0x01,                                     // bInCollection
0x01,                                     // baInterfaceNr

// AudioControl Interface Descriptor (Input Terminal)
0x0C,                                     // Descriptor size
0x24,                                     // bDescriptorType (CS Interface)
0x02,                                     // bDescriptorSubType (Input Terminal)
0x01,                                     // bTerminalID
0x01, 0x01,                               // wTerminalType (USB Streaming)
0x00,                                     // bAssocTerminal
0x02,                                     // bNrChannels
0x03, 0x00,                               // wChannelConfig
0x00,                                     // iChannelNames
0x00,                                     // iTerminal

// AudioControl Interface Descriptor (Feature Unit)
0x0A,                                     // Descriptor size
0x24,                                     // bDescriptorType (CS Interface)
0x06,                                     // bDescriptorSubType (Feature Unit)
0x02,                                     // bUnitID
0x01,                                     // bSourceID
0x01,                                     // bControlSize
0x03,                                     // bmaControls (Mute, Volume)
0x00,                                     // bmaControls (1)
0x00,                                     // bmaControls (2)
0x00,                                     // iFeature

// AudioControl Interface Descriptor (Output Terminal)
0x09,                                     // Descriptor size
0x24,                                     // bDescriptorType (Class specific interface)
0x03,                                     // bDescriptorSubType (Output Terminal)
0x03,                                     // bTerminalID
0x01, 0x03,                               // wTerminalType (Speaker)
0x00,                                     // bAssocTerminal
0x02,                                     // bSourceID
0x00,                                     // iTerminal

// Audio Interface  descriptor (Streaming)
0x9,                                      // Descriptor size
USB_INTERFACE_DESCRIPTOR_TYPE,            // Interface Association Descriptor Type
0x01,                                     // bInterfaceNumber
0x00,                                     // bAlternateSetting
0x00,                                     // bNumEndpoints
0x01,                                     // bInterfaceClass (Audio)
0x02,                                     // bInterfaceSubClass (Streaming)
0x00,                                     // bInterfaceProtocol 
0x00,                                     // iInterface

// Audio Interface  descriptor (Streaming)
0x9,                                      // Descriptor size
USB_INTERFACE_DESCRIPTOR_TYPE,            // Interface Association Descriptor Type
0x01,                                     // bInterfaceNumber
0x01,                                     // bAlternateSetting
0x01,                                     // bNumEndpoints
0x01,                                     // bInterfaceClass (Audio)
0x02,                                     // bInterfaceSubClass (Streaming)
0x00,                                     // bInterfaceProtocol 
0x00,                                     // iInterface

// Audio Streaming interface descriptor
0x07,                                     // Descriptor size
0x24,                                     // Descriptor type 
0x1,                                      // bDescriptorSubType
0x1,                                      // bTerminalLink
0x0,                                      // bDelay
0x01, 0x00,                               // wFormatTag (PCM)

// Audio Streaming interface descriptor
0x0B,                                      // Descriptor size
0x24,                                      // Descriptor type 
0x02,                                      // Descriptor subtype (FORMAT_TYPE)
0x01,                                      // bFormatType
0x02,                                      // bNrChannels
0x02,                                      // bSubFrameSize
0x10,                                      // bBitResolution (16)
0x01,                                      // bSamFreqType
0x80, 0xBB, 0x00,                          // tSamFreq 48000
    
// Endpoint Descriptor :
0x9,                                       // Descriptor size
USB_ENDPOINT_DESCRIPTOR_TYPE,              // Descriptor type (endpoint)
0x01,                   // Endpoint address
0x9,                                       // bmAttributes (Iso, Adaptive, Data)
0xc0, 0x00,                                // wMaxPacketSize (192 bytes)
0x4,                                       // bInterval (8 microframes = 1 millisecond)
0x0,                                       // bRefresh
0x0,                                       // bSynchAddress

// AudioStreaming Endpoint descriptor
0x7,                                       // bLength
0x25,                                      // bDescriptorType
0x1,                                       // bDescriptorSubType (EP General)
0x00,                                      // bmAttributes
0x0,                                       // bLockDelayUnits
0x00, 0x00                                 // wLockDelay

How are you testing this? In battle, using a live audio app?

Thanks @Andreas_Graefe , I will try with hardcoded descriptor - just to see what happens.

Have you used static or dynamic endpoints?

For my project: I am doing USB forwarding. So I get configuration descriptor of remote device - I have double check - it is correct configuration. Basically when I “forward” device with bulk and interrupt endpoints, everything works fine. Just device with isochronous endpoints does not trigger any callbacks (to perform USB transfers). So I thought I am missing something for isochronous transfers.

Are you using UsbFullSpeed or UsbHighSpeed? If using UsbFullSpeed, try switching to UsbHighSpeed instead and retry.

@“Benjamin_Höglinger” I am using UdecxUsbHighSpeed, but I will try another values just to see will it make any difference.

@Tim_Roberts: I’m testing it “in battle” (nice term btw).
I also looked into manually creating URBs and sending it to the device via the WinUSB API (following this guide but I wasn’t able to open the device from my test application. As far as I understand, this is because the device is already “in use” by Windows’ class audio driver, but I didn’t find a way to work around this.

@bojan_janjic, @“Benjamin_Höglinger”

Regarding the speed: I tried both UsbFullSpeed and UsbHighSpeed, made no difference.

Regarding static and dynamic endpoints: I started with this UDE sample, which uses static endpoints and modified it to use the descriptor posted above, then I switched to dynamic endpoints.
I did that because I learned that the EvtUsbDeviceEndpointsConfigure callback (which handles the altsettings requests for an audio interface) is only called when dynamic endpoints are used.

Now I see the EvtUsbDeviceEndpointsConfigure callback called with setting my audio interfaces’ altsetting to 0 (which means audio playback is stopped), but it never seems to be set to 1 when I try to play something.

@Andreas_Graefe for WinUSB:
Yes Windows blocks lot of devices: has own services that use device. So to access device you will need to do it from kernel - I did write a filter driver to make it work.

For EvtUsbDeviceEndpointsConfigure:
I have noticed that it does not trigger correctly alternate setting change - even found a Github project where somebody else had similar issue and was making a workaround. So I will make a workaround for my project to deal with alternate setting change.

However I do receive EvtUsbDeviceEndpointsConfigure when alternate setting is changed (to choose setting that activates endpoint). I see that endpoint is created and usbview displays that endpoint’s pipe is opened. But UDE does not fire any of events for data transmission.

Yes, I can confirm that behaviour. The altsetting is either incorrect (set to 0 when it should be set to 1) or it’s just not called.

I believe that something needs to be done in EvtUsbDeviceEndpointsConfigure, but I don’t know what.

Can you please post the link to the mentioned github project? Would like to take a look myself at this workaround.

https://github.com/cezanne/usbip-win

Ah yes, I also looked into that before. I also tried it out, but it also wasn’t able to forward an audio device properly (at least I didn’t manage to get it running).

Indeed, EvtUsbDeviceEndpointsConfigure is called with UdecxEndpointsConfigureTypeInterfaceSettingChange, but Params->InterfaceNumber and Params->NewInterfaceSetting are wrong.

I’m asking myself what needs to be done in the EvtUsbDeviceEndpointsConfigure callback to notify the framework that the interface setting request was sucessful. I’m just doing

WdfRequestComplete(Request, STATUS_SUCCESS);

(because I only want to simulate an audio device), but this is seems to be not enough. Perhaps the endpoint has to be started / restarted somehow, but I was assuming that this is already done by the framework in the EvtUsbDeviceEndpointAdd callback.

I have logged today what values are received in EvtUsbDeviceEndpointsConfigure, and it is for sure wrong.

Device has interface 1 for speaker with 2 alternate settings:
0 - no endpoints
1 - two isochronous endpoints

When I try to test speaker, USB analyzer tool shows that alternate setting 1 of interface 1 is selected, however in EvtUsbDeviceEndpointsConfigure callback I receive:
InterfaceNumber = 1
NewInterfaceSetting = 0

Like streaming is off (alternate setting has not endpoints), but that is not the case. UDE driver actually created 2 endpoints and device has opened pipes.

I have tried to “fix” value for NewInterfaceSetting, but again I do not receive any data.

Seems like problem is not with isochronous endpoints, but with interfaces that have alternate settings.

Took a couple months of excruciating research, trial and error but we finally cracked it!

UDE doesn’t implement QueryBusTime in USB_BUS_INTERFACE_USBDI_V1 which USBAUDIO.SYS depends on to compare frame times. I’ve reported this to Microsoft and in the meantime have written a filter driver to mitigate this problem and now audio works with UDE! I’ll update my post once I got a writeup published.

Cheers

Alright, pardon the wait but a lot has happened in the past few weeks. Here you can find my published findings!

Cheers