Custom USB client - Enable the BOS discriptor query part of enumeration

This post is to start a separate thread for the next issue that I am having with the USB device enumeration from this task tracker discussion, in Reply #2:
https://www.ghielectronics.com/community/forum/topic?id=22632

There seems to be little interest in this feature for NETMF, so just wondering if GHI Electronics was will to look at this USB bus stall issue. Assuming this was fixed and addition descriptors could be added to the configuration, a succesful enumeration may just be possible.

Thanks for your consideration on this matter.

@ PHITEK - To clarify, the issue is that when you set device.bcdUSB to 0x0210, the bus stalls. Additionally, our version of that field in our driver is read-only?

@ John - That’s correct. I have tried code that has the BOS descriptors as part of the configuration, I believe they are correctly formatted, at least the MFST one. So I don’t think it is due to not have a complete BOS descriptor in the test code.

@ PHITEK - So we can test the issue, can you post a complete but minimal program that shows the issue?

@ John - The test code from the following link will work, and then just add the line of code to set the bcdUSB version number.

https://www.ghielectronics.com/community/forum/topic?id=22658



If the USB bus stall is fixed then I would expect to see the request for more descriptors from the host to occur.

This should be what the first descriptor expected looks like:

```cs

// Create the OS String Descriptor - See Microsoft OS Descriptor Overview Page 6 for details.
// append the two bytes 0xA5 and 0x00 to "MSFT100" string with \ua500 syntax
// The bMS_VendorCode is set to 0xA5 (OS_DESCRIPTOR_STRING_VENDOR_CODE) but this can be set by the IHV.
 Configuration.StringDescriptor osName = new Configuration.StringDescriptor(OS_DESCRIPTOR_STRING, "MSFT100" + "\uA500");

Then add the osName after friendlyName in configuration statement.


             friendlyName,
osName };

@ John - This is a link to a document (must be downloaded) “Microsoft OS Descriptors Overview” page 6 states that the client device may stall the bus if there is no string descriptor for the index 0xEE or 238.
https://msdn.microsoft.com/en-us/windows/hardware/gg463179.aspx

I have done all my testing on the G80. I am switching to the G400 for testing this BOS query issue.
I am changing the Product ID after I get a request for for the MSFT100 descriptor - index=238

The definition for OS_DESCRIPTOR_STRING is listed below:


private const int OS_DESCRIPTOR_STRING = 0xEE;

I am having trouble now getting the Get Descriptor message from the host at the moment.

@ PHITEK - Thanks, we will take a look and let you know.

@ John - The new behavior with setting bcdUSB > 2.0 is that the SETUP part of the transaction never gets a response, I use to get the GetDescriptor at index 238 but can’t get that behavior to repeat right now, even after changing the USB Product ID.

I changed the bcdUSB = 2.0 and added the GenericDescriptor for MS Extended Compat ID Feature see code below. So now I get a GetDescriptor at index 4, the buffer is only 16 bytes, so the whole descriptor will not fit, in this request, so far normal, then the host requests the GetDescriptor at index 0 again with a buffer of 255, the data in the response is only four bytes, that I don’t recognize, then there is a GetDescriptor at index 2 which the G400 never responds to. I think USB analyzer trace line 1183 and 1257 should be the same.

This code I believe is format correctly but I can’t be sure right now.


            // Create the OS Feature Descriptor for WinUSB - See Extended Compat ID OS Feature Descriptor Spec. Page 10
            byte[] osExtComp = new byte[]   { 0x00, 0x00, 0x00, USB_GENERIC_DISCRIPTOR_HEADER_SIZE + USB_XCOMPATIBLE_OS_SIZE
                                            , (OS_DESCRIPTOR_EX_VERSION >> 8), (OS_DESCRIPTOR_EX_VERSION & 0xFF)
                                            , 0x00, 0x04 // Extended Comp ID Descriptor
                                            , 0x01 // Count of custom properties
                                            , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved
                                            // OS Extended Compatible ID for WinUSB
                                            , 0x00 // Interface Number
                                            , 0x01 // Reserved
                                            , 0x57, 0x49, 0x4e, 0x55, 0x53, 0x42, 0x00, 0x00 // WINUSB 
                                            , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Sub Compatible ID 
                                            , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Reserved

            Configuration.GenericDescriptor osExtendedCompatible = new Configuration.GenericDescriptor(USB_RESUEST_TYPE_IN | USB_REQUEST_TYPE_VENDOR, 0, osExtComp);
            osExtendedCompatible.bRequest = OS_DESCRIPTOR_STRING_VENDOR_CODE;
            osExtendedCompatible.wIndex = USB_XCOMPATIBLE_OS_REQUEST;

Add the osExtendedCompatible descriptor to the configuration:


friendlyName
, osName
, osExtendedCompatible
};

The device manager is kind of happy with this and displays the “G400 Proto HDW” in the Other devices node. I changed my string descriptor at index 2 to have that text.

Modified - add Microsoft OS Descriptor constants


        // Microsoft OS Descriptors 
        private const int OS_DESCRIPTOR_EX_VERSION = 0x0100;
        private const int OS_DESCRIPTOR_STRING = 0xEE;
        private const int OS_DESCRIPTOR_STRING_VENDOR_CODE = 0xA5;
        private const int USB_RESUEST_TYPE_IN = 0x80;
        private const int USB_REQUEST_TYPE_VENDOR = 0x40;
        private const int USB_RESUEST_TYPE_INTERFACE = 0x01;
        private const int USB_XCOMPATIBLE_OS_REQUEST = 0x04;
        private const int USB_XPROPERTY_OS_REQUEST = 0x05;
        private const int USB_GENERIC_DISCRIPTOR_HEADER_SIZE = 16;
        private const int USB_XCOMPATIBLE_OS_SIZE = 24;

@ PHITEK -

for bcdUSB greater than 2.0 issue, we tried your code and just add bcdUSB = 0x0210 as you asked and it still work fine for us, both on G400 and G80.

for MS Extended Compat ID Feature, please add whole project so we can give it a try, please!

@ Dat - Did you see the GetDescriptor for index = 238 request? What did the device show up like in the Device Manager?

Here is my Main:


 public static void Main()
 {
     Debug.Print("G400-D start USB init");
     //This "creates" your USB device and VS will not be able to see the device after this executes (if you debug via USB).
     USB.Init();
     Debug.Print("G400-D USB init complete");

      Thread.Sleep(Timeout.Infinite);
}

My USB.cs file with a stream read and write to echo messages back. This essentially the code from the codeshare project, with my fixes and extra descriptors for MS OS Descriptors. This still missing the descriptor for DeviceInterfaceGUID, and assigning WinUSB as the driver may not be correctly formated. So a working solution for me would be to check Device Manager and see that the WinUSB driver has been assigned to the G400. (No code necessary on the USB host system to confirm this.)

I try to do some more testing my self, like I mentioned I did see the setting bcdUSB = 2.10 work a could of times, but now it always fails.

Thank you for your support this far.


using System;
using Microsoft.SPOT;
using System.Threading;
using Microsoft.SPOT.Hardware.UsbClient;

namespace USBDevice
{
    class USB
    {

        public static string SerialNumber = "FFFFFFFFFFF";

        private const int WRITE_EP = 1;
        private const int READ_EP = 2;

        // Microsoft OS Descriptors 
        private const int OS_DESCRIPTOR_EX_VERSION = 0x0100;
        private const int OS_DESCRIPTOR_STRING = 0xEE;
        private const int OS_DESCRIPTOR_STRING_VENDOR_CODE = 0xA5;
        private const int USB_RESUEST_TYPE_IN = 0x80;
        private const int USB_REQUEST_TYPE_VENDOR = 0x40;
        private const int USB_RESUEST_TYPE_INTERFACE = 0x01;
        private const int USB_XCOMPATIBLE_OS_REQUEST = 0x04;
        private const int USB_XPROPERTY_OS_REQUEST = 0x05;
        private const int USB_GENERIC_DISCRIPTOR_HEADER_SIZE = 16;
        private const int USB_XCOMPATIBLE_OS_SIZE = 24;

        static UsbStream usbStream = null;

        /**
         * Simply echos back whatever is sent.
         */
        public static void HandleRequests()
        { 
            byte[] readData = new byte[512];

            int bytesRead;
             
            while (true)
            {
                bytesRead = usbStream.Read(readData, 0, readData.Length);
                if (bytesRead > 0)
                {
                    usbStream.Write(readData, 0, bytesRead);
                } 
            } 
        }

        private static bool ConfigureUSBController(UsbController usbController)
        {
            bool bRet = false;

            // Create the device descriptor
            Configuration.DeviceDescriptor device = new Configuration.DeviceDescriptor(0xDEAD, 0xF00C, 0x0200);
            device.bcdUSB = 0x210;
            device.bDeviceClass = 0xFF;     // Vendor defined class
            device.bDeviceSubClass = 0xFF;  // Vendor defined subclass
            device.bDeviceProtocol = 0;
            device.bMaxPacketSize0 = 64;    // Maximum packet size of EP0
            device.iManufacturer = 1;       // String #1 is manufacturer name (see string descriptors below)
            device.iProduct = 2;            // String #2 is product name
            device.iSerialNumber = 3;       // String #3 is the serial number

            // Create the endpoints
            Configuration.Endpoint writeEP = new Configuration.Endpoint(WRITE_EP, Configuration.Endpoint.ATTRIB_Write | Configuration.Endpoint.ATTRIB_Bulk | Configuration.Endpoint.ATTRIB_Synchronous);//Configuration.Endpoint.ATTRIB_Bulk | Configuration.Endpoint.ATTRIB_Write);
            writeEP.wMaxPacketSize = 64;
            writeEP.bInterval = 0;


            Configuration.Endpoint readEP = new Configuration.Endpoint(READ_EP, Configuration.Endpoint.ATTRIB_Read | Configuration.Endpoint.ATTRIB_Bulk | Configuration.Endpoint.ATTRIB_Synchronous);// Configuration.Endpoint.ATTRIB_Bulk | Configuration.Endpoint.ATTRIB_Read);
            readEP.wMaxPacketSize = 64;
            readEP.bInterval = 0;

            Configuration.Endpoint[] usbEndpoints = new Configuration.Endpoint[] { writeEP, readEP };

            // Set up the USB interface
            Configuration.UsbInterface usbInterface = new Configuration.UsbInterface(0, usbEndpoints);
            usbInterface.bInterfaceClass = 0xFF; // Vendor defined class
            usbInterface.bInterfaceSubClass = 0xFF; // Vendor defined subclass
            usbInterface.bInterfaceProtocol = 0;

            // Create array of USB interfaces
            Configuration.UsbInterface[] usbInterfaces = new Configuration.UsbInterface[] { usbInterface };

            // Create configuration descriptor
            Configuration.ConfigurationDescriptor config = new Configuration.ConfigurationDescriptor(200, usbInterfaces);

            // Create the string descriptors
            Configuration.StringDescriptor manufacturerName = new Configuration.StringDescriptor(1, "YourName");
            Configuration.StringDescriptor productName = new Configuration.StringDescriptor(2, "G400 Proto HDW");            
            Configuration.StringDescriptor serialNumber = new Configuration.StringDescriptor(3, "0000-0" + USB.SerialNumber.Substring(0, 3) + "-" + USB.SerialNumber.Substring(3, 4) + "-" + USB.SerialNumber.Substring(7, 4));
            Configuration.StringDescriptor displayName = new Configuration.StringDescriptor(4, "YourDisplayName");
            Configuration.StringDescriptor friendlyName = new Configuration.StringDescriptor(5, "YourFriendlyName");

            // Create the OS String Descriptor - See Microsoft OS Descriptor Overview Page 6 for details.
            // append the two bytes 0xA5 and 0x00 to "MSFT100" string with \ua500 syntax
            // The bMS_VendorCode is set to 0xA5 (OS_DESCRIPTOR_STRING_VENDOR_CODE) but this can be set by the IHV.
            Configuration.StringDescriptor osName = new Configuration.StringDescriptor(OS_DESCRIPTOR_STRING, "MSFT100" + "\uA500");
            // Create the OS Feature Descriptor for WinUSB - See Extended Compat ID OS Feature Descriptor Spec. Page 10
            byte[] osExtComp = new byte[]   { 0x00, 0x00, 0x00, USB_GENERIC_DISCRIPTOR_HEADER_SIZE + USB_XCOMPATIBLE_OS_SIZE
                                            , (OS_DESCRIPTOR_EX_VERSION >> 8), (OS_DESCRIPTOR_EX_VERSION & 0xFF)
                                            , 0x00, 0x04 // Extended Comp ID Descriptor
                                            , 0x01 // Count of custom properties
                                            , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved
                                            // OS Extended Compatible ID for WinUSB
                                            , 0x00 // Interface Number
                                            , 0x01 // Reserved
                                            , 0x57, 0x49, 0x4e, 0x55, 0x53, 0x42, 0x00, 0x00 // WINUSB 
                                            , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Sub Compatible ID 
                                            , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Reserved

            Configuration.GenericDescriptor osExtendedCompatible = new Configuration.GenericDescriptor(USB_RESUEST_TYPE_IN | USB_REQUEST_TYPE_VENDOR, 0, osExtComp);
            osExtendedCompatible.bRequest = OS_DESCRIPTOR_STRING_VENDOR_CODE;
            osExtendedCompatible.wIndex = USB_XCOMPATIBLE_OS_REQUEST;

            // Create the final configuration
            Configuration configuration = new Configuration();

            configuration.descriptors = new Configuration.Descriptor[] {
                                                                        device,
                                                                        config,
                                                                        manufacturerName,
                                                                        productName,
                                                                        serialNumber,
                                                                        displayName,
                                                                        friendlyName
                                                                        , osName
                                                                        , osExtendedCompatible
                                                                        };
            try
            {
                // Set the configuration
                usbController.Configuration = configuration;

                if (UsbController.ConfigError.ConfigOK != usbController.ConfigurationError)
                {
                    throw new ArgumentException();
                }

                // If all ok, start the USB controller.
                bRet = usbController.Start();
            }
            catch (ArgumentException)
            {
                Debug.Print("Can't configure USB controller, error " + usbController.ConfigurationError.ToString());
            }


            return bRet;


        }

        public static void Init()
        {

            // See if the hardware supports USB
            UsbController[] controllers = UsbController.GetControllers();
            // Bail out if USB is not supported on this hardware!
            if (0 == controllers.Length)
            {
                Debug.Print("USB is not supported on this hardware!");
                return;
            }

            foreach (UsbController controller in controllers)
            {

                if (controller.Status != UsbController.PortState.Stopped)
                {
                    controller.Stop();
                    Thread.Sleep(1000);
                }
            }

            // Find a free USB controller
            UsbController usbController = null;
            foreach (UsbController controller in controllers)
            {
                if (controller.Status == UsbController.PortState.Stopped)
                {
                    usbController = controller;
                    break;
                }
            }
            // If no free USB controller
            if (null == usbController)
            {
                Debug.Print("All available USB controllers already in use. Set the device to use Ethernet or Serial debugging.");
                return;
            }

            try
            {   // Configure the USB controller
                if (ConfigureUSBController(usbController))
                {
                    usbStream = usbController.CreateUsbStream(WRITE_EP, READ_EP);
					if (usbStream.CanTimeout)
					{
						usbStream.ReadTimeout = 60000;
						usbStream.WriteTimeout = 60000;
					}
                }
                else
                {
                    throw new Exception();
                }
            }
            catch (Exception)
            {
                Debug.Print("USB stream could not be created, error " + usbController.ConfigurationError.ToString());
                return;
            }
        }
    }
}


@ PHITEK - So we can solve this as quickly as possible, can you send us a complete project with any changes you have made just so we’re sure we’re on the same page? Keep it as small as possible, just enough to illustrate the behavior you are seeing. There’s a lot of code fragments and discussions on various USB issues going on, so we want to keep things straight.

Once we run the project you send us, what behavior do you see on the PC and what behavior are you expecting? Try to be as detailed as possible.

@ John - Sure, I will work on this over the weekend. There have been several changes to the environment since I first discovered the issues.

How did you want me to send you the “project”, zip it up in direct message or post to codeshare, and update it once the NETMF device installs and the host assigns the WinUSB driver?

Thank you for your support this far.

@ PHITEK - You can email it to support at ghielectronics.com, just reference this thread.

@ John - Will do, I should have enough time this weekend to test this more as well. I still curious why my G400 stopped getting the GetDescriptor at index 238.

Thanks again for all of this effort.

@ Dat - What version of Windows were you testing with that G400 and G80 worked?

This step to enable BOS query is not necessary for what I need to accomplish. This descriptor is really part of the USB 3.x specification, which is not my use case.

I have made some good progress with the USB bus enumeration. What looks like the last remaining issue is a failure of the G400 to respond to the Extended Properties Descriptor to assign the Device Interface GUID. So two of the three extended descriptor queries are working, so things are looking up for been able to run a Windows application with out installing drivers!

I will send my sample program showing this issue.

@ PHITEK - Does the Extended Properties Descriptor fail on the G80 as well or did you only test it on the G400?

@ John - I will test the G80 and G120 this evening, I’ll try to get to all the different Windows platforms.

@ John - I tested the G80 and G120 platforms with Windows 7, 8.1, and 10.

In short the GetDescriptor at Index=5 still fails, for all platforms and versions of Windows tested.
The failure is a bit cleaner however, with a control bus STALL condition on the USB bus.

I have forwarded you a updated document and USB Analyzer trace log.

@ PHITEK -

We found the issue and it will be correct in next release.
Your code also needs to be correct to get working so far. I think: