This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C123GE6PM: USB Device: add descriptor callback function?

Part Number: TM4C123GE6PM


Hello,

I have a modified version of the USB bulk transfer example for the TM4C123GXL launchpad up and running.  I'm seeing the successful microsoft OS string descriptors coming to the MCU and need to handle this request to load the WinUSB driver.  I'm curious what the best approach to add a "descriptor" handler to handle this custom string descriptor would be.

I see a potential option in the usbdbulk.c file shown below starting at line 329:

I'm asking though because all the other string descriptor handling is working fine and don't want to break them.  I also have this file locally stored in my project so I can modify things I'm unable to modify in the usb_bulk_structs.c.  Ideally I wouldn't modify any lower level driver code because it is all compiled down (I think).  I've tried modifying source code in the usblib driver folder and the changes don't take place - I'm assuming because this code is linked in and not compiled every time I clikc compile/build in CCS.

Any direction regarding a good place for a custom (microsoft os string descriptor) string descriptor handler might go would be greatly appreciated!

  • Hi Robert,

      If you know what to change to suit your application you can change the library source file but you would need to rebuild the usblib.lib so your changes will be reflected when you link to the library. Another method should also work is to copy usbdbulk.c to your current project folder. Make the change to it and it should have your updated changes after you build your project. 

  • Hi Charles,

    Thank you for your response!  I think I may be on the right track and you are correct I'd have to recompile the usblib as I've made some test changes that don't get picked up because I'm linking to it and not compiling it.  On that topic - do you know the rough order of steps to recompile this library? or is there a pdf somewhere that talks through recompiling TI libraries?  

    On the USB question topic - I think I have figured out what (in theory) you have to do to deal with this and unfortunately you have to (I think) edit the usbdenum.c file.  I believe this is because you have to modify the lower level enumeration handler found at line # 1937 in that file - see below.

    //*****************************************************************************
    //
    // This function handles the GET_DESCRIPTOR standard USB request.
    //
    // \param pvInstance is the USB device controller instance data.
    // \param psUSBRequest holds the data for this request.
    //
    // This function will return most of the descriptors requested by the host
    // controller.  The descriptor specified by \e
    // pvInstance->psInfo->pui8DeviceDescriptor will be returned when the device
    // descriptor is requested.  If a request for a specific configuration
    // descriptor is made, then the appropriate descriptor from the \e
    // g_pConfigDescriptors will be returned.  When a request for a string
    // descriptor is made, the appropriate string from the
    // \e pvInstance->psInfo->pStringDescriptors will be returned.  If the
    // \e pvInstance->psInfo->psCallbacks->GetDescriptor is specified it will be
    // called to handle the request.  In this case it must call the
    // USBDCDSendDataEP0() function to send the data to the host controller.  If
    // the callback is not specified, and the descriptor request is not for a
    // device, configuration, or string descriptor then this function will stall
    // the request to indicate that the request was not supported by the device.
    //
    // \return None.
    //
    //*****************************************************************************
    static void
    USBDGetDescriptor(void *pvInstance, tUSBRequest *psUSBRequest)
    {
        bool bConfig;
        tDCDInstance *psUSBControl;
        tDeviceInfo *psDevice;
        const tConfigHeader *psConfig;
        const tDeviceDescriptor *psDeviceDesc;
        uint8_t ui8Index;
        int32_t i32Index;
    
        ASSERT(psUSBRequest != 0);
        ASSERT(pvInstance != 0);
    
        //
        // Create the device information pointer.
        //
        psUSBControl = (tDCDInstance *)pvInstance;
        psDevice = g_ppsDevInfo[0];
    
        //
        // Need to ACK the data on end point 0 without setting last data as there
        // will be a data phase.
        //
        MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false);
    
        //
        // Assume we are not sending the configuration descriptor until we
        // determine otherwise.
        //
        bConfig = false;
    
        //
        // Which descriptor are we being asked for?
        //
        switch(psUSBRequest->wValue >> 8)
        {
            //
            // This request was for a device descriptor.
            //
            case USB_DTYPE_DEVICE:
            {
                //
                // Return the externally provided device descriptor.
                //
                psUSBControl->pui8EP0Data =
                                        (uint8_t *)psDevice->pui8DeviceDescriptor;
    
                //
                // The size of the device descriptor is in the first byte.
                //
                psUSBControl->ui32EP0DataRemain =
                    psDevice->pui8DeviceDescriptor[0];
    
                break;
            }
    
            //
            // This request was for a configuration descriptor.
            //
            case USB_DTYPE_CONFIGURATION:
            {
                //
                // Which configuration are we being asked for?
                //
                ui8Index = (uint8_t)(psUSBRequest->wValue & 0xFF);
    
                //
                // Is this valid?
                //
                psDeviceDesc =
                    (const tDeviceDescriptor *)psDevice->pui8DeviceDescriptor;
    
                if(ui8Index >= psDeviceDesc->bNumConfigurations)
                {
                    //
                    // This is an invalid configuration index.  Stall EP0 to
                    // indicate a request error.
                    //
                    USBDCDStallEP0(0);
                    psUSBControl->pui8EP0Data = 0;
                    psUSBControl->ui32EP0DataRemain = 0;
                }
                else
                {
                    //
                    // Return the externally specified configuration descriptor.
                    //
                    psConfig = psDevice->ppsConfigDescriptors[ui8Index];
    
                    //
                    // Start by sending data from the beginning of the first
                    // descriptor.
                    //
                    psUSBControl->ui8ConfigSection = 0;
                    psUSBControl->ui16SectionOffset = 0;
                    psUSBControl->pui8EP0Data =
                                    (uint8_t *)psConfig->psSections[0]->pui8Data;
    
                    //
                    // Determine the total size of the configuration descriptor
                    // by counting the sizes of the sections comprising it.
                    //
                    psUSBControl->ui32EP0DataRemain =
                                                USBDCDConfigDescGetSize(psConfig);
    
                    //
                    // Remember that we need to send the configuration descriptor
                    // and which descriptor we need to send.
                    //
                    psUSBControl->ui8ConfigIndex = ui8Index;
    
                    bConfig = true;
                }
                break;
            }
    
            //
            // This request was for a string descriptor.
            //
            case USB_DTYPE_STRING:
            {
                //
                // Determine the correct descriptor index based on the requested
                // language ID and index.
                //
                i32Index = USBDStringIndexFromRequest(psUSBRequest->wIndex,
                                                      psUSBRequest->wValue & 0xFF);
    
                //
                // If the mapping function returned -1 then stall the request to
                // indicate that the request was not valid.
                //
                if(i32Index == -1)
                {
                    USBDCDStallEP0(0);
                    break;
                }
    
                //
                // Return the externally specified configuration descriptor.
                //
                psUSBControl->pui8EP0Data =
                    (uint8_t *)psDevice->ppui8StringDescriptors[i32Index];
    
                //
                // The total size of a string descriptor is in byte 0.
                //
                psUSBControl->ui32EP0DataRemain =
                    psDevice->ppui8StringDescriptors[i32Index][0];
    
                break;
            }
    
            //
            // Any other request is not handled by the default enumeration handler
            // so see if it needs to be passed on to another handler.
            //
            default:
            {
                //
                // If there is a handler for requests that are not handled then
                // call it.
                //
                if(psDevice->psCallbacks->pfnGetDescriptor)
                {
                    psDevice->psCallbacks->pfnGetDescriptor(g_psDCDInst[0].pvCBData,
                                                          psUSBRequest);
                }
                else
                {
                    //
                    // Whatever this was this handler does not understand it so
                    // just stall the request.
                    //
                    USBDCDStallEP0(0);
                }
    
                return;
            }
        }
    
        //
        // If this request has data to send, then send it.
        //
        if(psUSBControl->pui8EP0Data)
        {
            //
            // If there is more data to send than is requested then just
            // send the requested amount of data.
            //
            if(psUSBControl->ui32EP0DataRemain > psUSBRequest->wLength)
            {
                psUSBControl->ui32EP0DataRemain = psUSBRequest->wLength;
            }
    
            //
            // Now in the transmit data state.  Be careful to call the correct
            // function since we need to handle the configuration descriptor
            // differently from the others.
            //
            if(!bConfig)
            {
                USBDEP0StateTx(0);
            }
            else
            {
                USBDEP0StateTxConfig(0);
            }
        }
    }

    I don't think my original approach will work because the "USB_DTYPE_STRING" needs to catch the descriptor message from windows because it is a "string descriptor" request.  The custom handler only gets triggered when the default case in this function gets called which is for an unknown request type.  So "I think" I need to modify the USB_DTYPE_STRING case to look for a string descriptor offset value of 0xEE (specified by windows for the os descriptors) and catch that rather than letting the stall event get executed.  Not sure what the best approach to this is yet, but would you agree per the TI USB driver library source code this would be where the mod should go to handle a "non-standard" string descriptor request from the OS?  Would sticking an if statement to catch the 0xEE offset case be pretty much all one would likely need to add?

  • On that topic - do you know the rough order of steps to recompile this library?

    Hi Robert,

      The usblib is in C:\ti\TivaWare_C_Series-2.2.0.295\usblib. You can just import it like a normal CCS project and compile it. I just tried it and at the end of the build it will produce the new usblib.lib. 

    On the USB question topic - I think I have figured out what (in theory) you have to do to deal with this and unfortunately you have to (I think) edit the usbdenum.c file. 

    Sorry, on the USB topic about the custom USB_DTYPE_STRING, I really don't have much knowledge and experience and I don't think I can provide guidance on how to do it. If you get it to work please share with us and I'm sure it will benefit the community. Sorry for lack for guidance. 

  • Hi Charles,

    Thank you for the time you're taking to help me here.  I will give the usblib re-compile a shot this week and see if I can use breakpoints to catch the string descriptor.  I think if I can get that far I can come up with a plan to handle/respond even if it isn't elegant as a first step.

    As far as some feedback for future usblib builds from TI - it would be really nice if you were able to assign a custom handler for some of the events handled in that enumeration case statement.  You guys are on the right track with the one custom handler check at the bottom with the default case to handle other request types, but within each request type it would be great to have the option to connect a custom handler rather than editing the lower level driver source.  I would say this is pretty reasonable because it is probably common for more involved product development to handle more situations like this.  This is a situation where you guys handle strings requests but only a subset and if anything outside of that subset comes in it would be great to be able to connect a handler similar to the default case to the other handle cases in the cases statement tat fall out of the basic message expectations.

    Not sure if that makes sense, but long story short it would be awesome if the driverlib could support custom handlers for the below cases:

            //
            // This request was for a device descriptor.
            //
            case USB_DTYPE_DEVICE:
            {
                //
                // Return the externally provided device descriptor.
                //
                psUSBControl->pui8EP0Data =
                                        (uint8_t *)psDevice->pui8DeviceDescriptor;
    
                //
                // The size of the device descriptor is in the first byte.
                //
                psUSBControl->ui32EP0DataRemain =
                    psDevice->pui8DeviceDescriptor[0];
    
                break;
            }
    
            //
            // This request was for a configuration descriptor.
            //
            case USB_DTYPE_CONFIGURATION:
            {
                //
                // Which configuration are we being asked for?
                //
                ui8Index = (uint8_t)(psUSBRequest->wValue & 0xFF);
    
                //
                // Is this valid?
                //
                psDeviceDesc =
                    (const tDeviceDescriptor *)psDevice->pui8DeviceDescriptor;
    
                if(ui8Index >= psDeviceDesc->bNumConfigurations)
                {
                    //
                    // This is an invalid configuration index.  Stall EP0 to
                    // indicate a request error.
                    //
                    USBDCDStallEP0(0);
                    psUSBControl->pui8EP0Data = 0;
                    psUSBControl->ui32EP0DataRemain = 0;
                }
                else
                {
                    //
                    // Return the externally specified configuration descriptor.
                    //
                    psConfig = psDevice->ppsConfigDescriptors[ui8Index];
    
                    //
                    // Start by sending data from the beginning of the first
                    // descriptor.
                    //
                    psUSBControl->ui8ConfigSection = 0;
                    psUSBControl->ui16SectionOffset = 0;
                    psUSBControl->pui8EP0Data =
                                    (uint8_t *)psConfig->psSections[0]->pui8Data;
    
                    //
                    // Determine the total size of the configuration descriptor
                    // by counting the sizes of the sections comprising it.
                    //
                    psUSBControl->ui32EP0DataRemain =
                                                USBDCDConfigDescGetSize(psConfig);
    
                    //
                    // Remember that we need to send the configuration descriptor
                    // and which descriptor we need to send.
                    //
                    psUSBControl->ui8ConfigIndex = ui8Index;
    
                    bConfig = true;
                }
                break;
            }
    
            //
            // This request was for a string descriptor.
            //
            case USB_DTYPE_STRING:
            {
                //
                // Determine the correct descriptor index based on the requested
                // language ID and index.
                //
                i32Index = USBDStringIndexFromRequest(psUSBRequest->wIndex,
                                                      psUSBRequest->wValue & 0xFF);
    
                //
                // If the mapping function returned -1 then stall the request to
                // indicate that the request was not valid.
                //
                if(i32Index == -1)
                {
                    USBDCDStallEP0(0);
                    break;
                }
    
                //
                // Return the externally specified configuration descriptor.
                //
                psUSBControl->pui8EP0Data =
                    (uint8_t *)psDevice->ppui8StringDescriptors[i32Index];
    
                //
                // The total size of a string descriptor is in byte 0.
                //
                psUSBControl->ui32EP0DataRemain =
                    psDevice->ppui8StringDescriptors[i32Index][0];
    
                break;
            }

    Just as you guys support customers connecting custom handlers for request types outside of the above cases - this is done in the default case shown below.

            //
            // Any other request is not handled by the default enumeration handler
            // so see if it needs to be passed on to another handler.
            //
            default:
            {
                //
                // If there is a handler for requests that are not handled then
                // call it.
                //
                if(psDevice->psCallbacks->pfnGetDescriptor)
                {
                    psDevice->psCallbacks->pfnGetDescriptor(g_psDCDInst[0].pvCBData,
                                                          psUSBRequest);
                }
                else
                {
                    //
                    // Whatever this was this handler does not understand it so
                    // just stall the request.
                    //
                    USBDCDStallEP0(0);
                }
    
                return;
            }
        }

    I bring all this up because the usb driver library is very close to supporting this already.