Supporting More Than One Configuration Descriptors

Sub forums for various specialist XMOS applications. e.g. USB audio, motor control and robotics.
Post Reply
User avatar
aneves
Experienced Member
Posts: 93
Joined: Wed Sep 16, 2015 2:38 pm

Supporting More Than One Configuration Descriptors

Post by aneves »

Hi Guys,

I'm currently evaluating how to support 2 configuration descriptors using an xCORE-200 USB sliceKIT with the lib_usb 3.1.1. What I'm trying to accomplish is to have my dev board enumerate as one USB class with it's default configuration descriptor (1), but when the host sends a control transfer of SET_CONFIGURATION with wValue set to 2 (the number of the other configuration) the dev board will enumerate as a different USB class. According to the USB spec, this should be possible and the whole purpose of multiple configuration descriptors.

After snooping around the source code for the USB library, I saw some interesting comments in usb_device.xc stating that only one configuration descriptor is supported (line 227):

Code: Select all

 /* Configuration Descriptor */
                        case (USB_DESCTYPE_CONFIGURATION << 8):

                            /* Currently only 1 configuration descriptor supported */
                            /* TODO We currently return the same for all configs */
                            //if((sp.wValue & 0xff) == 0)
                            {
                                if((usbBusSpeed == XUD_SPEED_FS) && (cfgDescLength_fs != 0))
                                {
                                    /* Return full-speed configuration descriptor */
                                    cfgDesc_fs[1] = USB_DESCTYPE_CONFIGURATION;
                                    return XUD_DoGetRequest(ep_out, ep_in, cfgDesc_fs, cfgDescLength_fs, sp.wLength);
                                }
                                else if(cfgDescLength_hs != 0)
                                {
                                    /* Do get request (send descriptor then 0 length status stage) */
                                    cfgDesc_hs[1] = USB_DESCTYPE_CONFIGURATION;
                                    return XUD_DoGetRequest(ep_out, ep_in,  cfgDesc_hs, cfgDescLength_hs, sp.wLength);
                                }
                            }
                            break;
From what I've gathered from the lib_usb API and relevant examples, we have the ability to enable and disable endpoints, and there is a global which stores the current set configuration. I'm thinking I could modify the library such that more than one configuration descriptor is defined and based on the value of the global we could pass back that specific configuration descriptor at this point in the code.

I'm sure I'm making this sound easier than it really is. This is something I will attempt to do in the next day or two, but before I dug in too deep I wanted to see if there was any other limitation I'm not aware of that would warrant this as a futile exercise.

Maybe someone has already done this or has some experience they can share?

Thanks!


User avatar
Ross
XCore Expert
Posts: 962
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

Generally, yes I'd say you are correct, but I don't think returning the correct descriptor is all that is required..

One complication relates to the USB spec section 9.1.15, from the code:
/* USB 2.0 Spec 9.1.1.5 states that configuring a device should cause all
* the status and configuration values associated with the endpoints in the
* affected interfaces to be set to their default values. This includes setting
* the data toggle of any endpoint using data toggles to the value DATA0 */
It means you need to know what EP's are used for each configuration (so you know which ones to reset). Currently there is no link between EPs and configs, so this would need programming up also (I think this is the reason multiple config descs isn't supported by default.. its not quite "trivial")


This part will want to know about multiple config descs also:

Code: Select all

 /* Pull self/bus powered bit from the config descriptor */
                    if (cfgDesc_hs[7] & 0x40)
                        buffer[0] = 0x1;
                    else
                        buffer[0] = 0;
User avatar
aneves
Experienced Member
Posts: 93
Joined: Wed Sep 16, 2015 2:38 pm

Post by aneves »

Thank you! Very helpful!

I'll let you know how it goes.
User avatar
Ross
XCore Expert
Posts: 962
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

Great, yes please do keep us updated.
User avatar
aneves
Experienced Member
Posts: 93
Joined: Wed Sep 16, 2015 2:38 pm

Post by aneves »

Quick update on this. So I hacked up a crude, brute-force proof of concept where the configuration descriptors are now a 2D array. Unfortunately, I couldn't get very far with testing it. The firmware seems to run fine on the dev board - and by "fine" I mean not crash when I do anything.

The problem is on the host side. I am using NI-VISA in LabVIEW on Windows 7 to send a SET_CONFIGURATION. However, my USB analyzer shows that the correct config descriptor number is always coerced to 0. When I try the same test on OS X Yosemite, I can see a reaction on my MacBook. Granted, it is not the correct one - but evidence of progress nonetheless in my opinion. I have an active thread on NI's forum to try and figure out that issue.

At any rate, I've decided that this approach probably isn't the correct one for what I am trying to accomplish. Instead, I've followed the online documentation to provide a customized loader where I can selectively boot a firmware image.

If anyone is curious on the (little) progress on the lib_usb api edit, all I did was modify the function USB_StandardRequests in usb_device.xc from:

Code: Select all

XUD_Result_t USB_StandardRequests(XUD_ep ep_out, XUD_ep ep_in,
    NULLABLE_ARRAY_OF(unsigned char, devDesc_hs), int devDescLength_hs,
    NULLABLE_ARRAY_OF(unsigned char, cfgDesc_hs), int cfgDescLength_hs,
    NULLABLE_ARRAY_OF(unsigned char, devDesc_fs), int devDescLength_fs,
    NULLABLE_ARRAY_OF(unsigned char, cfgDesc_fs), int cfgDescLength_fs,
    char * unsafe strDescs[], int strDescsLength,
    USB_SetupPacket_t &sp, XUD_BusSpeed_t usbBusSpeed)
to:

Code: Select all

XUD_Result_t USB_StandardRequests(XUD_ep ep_out, XUD_ep ep_in,
    NULLABLE_ARRAY_OF(unsigned char, devDesc_hs), int devDescLength_hs,
    unsigned char cfgDesc_hs[][CFGDESCLENGTH], int cfgDescLength_hs,
    NULLABLE_ARRAY_OF(unsigned char, devDesc_fs), int devDescLength_fs,
    NULLABLE_ARRAY_OF(unsigned char, cfgDesc_fs), int cfgDescLength_fs,
    char * unsafe strDescs[], int strDescsLength,
    USB_SetupPacket_t &sp, XUD_BusSpeed_t usbBusSpeed)
where CFGDESCLENGTH is a #define 0x20. Each config descriptor is 32 bytes long (actually, they are identical at this point with the exception of bMaxPower). I use the global g_currentConfig to index the row of the 2D array to return the current config descriptor. Example:

Code: Select all

return XUD_DoGetRequest(ep_out, ep_in,  cfgDesc_hs[g_currentConfig], cfgDescLength_hs, sp.wLength);
I'm halting work on modifying the lib_usb api to support more than one config descriptor until the need for it rises again.
Post Reply