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.

Dynamicly change USB Descriptors



Hi,

I need to change the USB Descriptors of the HIDAdvRemoteDongle Project depending on the Report-Map I will get once a connection with a ble-device is made. Therefor I would need to rewrite the descriptors which are hard-coded in usb_hid_descriptor.s51,  is there some way to do that?

And how would I reset the USB Connection, if my descriptors change?


The USB Descriptor Parser (usbdp) says: "The standard-formatted descriptors must be placed back-to-back in either XDATA or CODE memory."

But there is no example of placing the descriptors into XData, could someone point me to one, or better yet, give me one?

Thanks in Advance!

  • Hi Sebastian,

    I am not sure if that is possible, we are looking into it.

    Cheers,
    Fredrik
  • Hi Fredrik,

    thank you for replying, I looked through the documentation a little more and found the following:

    Within the documentation of the usb library BLE-CC254x-1.4.0\Components\hal\target\CC2540USB\usb\library\usb\usb_description_parser.h it says: "The standard-formatted descriptors must be placed back-to-back in either XDATA or CODE memory.".

    Furthermore it says that the markers can be dynamic. "The markers can be dynamic, provided that they are not changed while the descriptor parser is in use."

    But there are also no examples or descriptions on how to do just that. Since I need to use dynamic descriptors, I need some documentation or starting point or an example on how to work with dynamic descriptors using XDATA.

    Thanks in advance for your help!

  • Hi Fredrik, I haven't heard from you in a while, is there any update?
  • Hi Sebastian,

    It is possible to achieve what you're asking for, but it is unfortunately not that trivial with our current implementation. To make this work, you should be fairly familiar with basic 8051 assembly and know the 8051 memory model. I can provide some pointers to get you going:

    As you have noticed, the USB descriptor is hard coded in CODE space (flash), so you can't just change it on the fly. Potentially, you could do it once, e.g. after first boot, but this would involve using a default descriptor with just 0xFFs and reprogram the descriptor location when you have determined what descriptor to use. I don't think this will serve your purpose very well.

    You will have to move the USB descriptor over to RAM - either in the DATA or XDATA space. XDATA may give you the most flexibility as there's not much available DATA.

    The descriptor is basically just a large initialized structure or table, located in ROM. You'll see that it is located in the RCODE segment (look for "RSEG RCODE" in the descriptor file). What I recommend to do is to keep a "template" descriptor in flash and have a copy of the descriptor in XDATA. Then modify the descriptor parser implementation to point to relevant labels (or offsets in the descriptor table) to your XDATA copy instead of the hardcoded one in flash. The "template" would be your initialized data, which would have had to be placed in flash anyway.

    So this means that you should define a new segment in XDATA that has the same size as the descriptor. Then, as one of the first things you do when your system starts, copy the raw descriptor from flash to XDATA (it's just like initializing a variable). And then point the descriptor parser to the descriptor in XDATA rather than in CODE. Then you can dynamically change the descriptor.
  • Hi M,

    thank you for your lengthy answer, it is very comforting to hear that this is in fact possible and I don't have to find some workaround. I just have one more question:

    If I change the descriptor after the USB Setup routine has finished, do I need to do something special or is it enough to call usbfwInit again?

  • Before reinitializing, make sure to call HAL_USB_PULLUP_DISABLE(). This disconnects the device from the USB bus by not pulling the D+ line high. Then call usbfwResetHandler() before usbfwInit(). And finally HAL_USB_PULLUP_ENABLE(). I THINK this is sufficient, but there may be something I'm forgetting (we don't normally do this descriptor change dynamically in the examples we have written).

    Take a look in _hal_uart_usb.c (HalUARTInitUSB() function) to see how the USB is initialized.
  • Thank you very much, this would have taken a long time without you help. I'll try implementing this on monday and will come back if I hit any roadblocks (or I will mark it as answered if not).
  • While I was working through the memory model and the code (especially that of usb_descriptor_parser.c I noticed that the report descriptors are not handled by usb_descriptor_parser but rather by usb_standard_requests.c. Since I really only need to change one report descriptor dynamically I wanted to ask, if there is anything I'm missing, but let me try to explain what I am planning on doing.

    As I see it, usbsrGetDescriptors is asked for the descriptors and works through the usbDescLut, usbDescLutEnd table defined in usb_hid_descriptor.s51 using the usbSetupHeader.value, usbSetupHeader.index to return the descriptor that was asked for. If I would change the behaviour to first scan a table in Memory, which has nulls for all descriptors at the start, which is used if the descriptor is not null, and wrote it to usbSetupData.pBuffer it should work perfectly, shouldn't it?


    It would look something like this:

    typedef struct {
        uint8 valueMsb;            ///< LSB of the \ref USB_SETUP_HEADER.value request parameter
        uint8 valueLsb;            ///< MSB of the \ref USB_SETUP_HEADER.value request parameter
        uint8 indexMsb;            ///< LSB of the \ref USB_SETUP_HEADER.index request parameter
        uint8 indexLsb;            ///< MSB of the \ref USB_SETUP_HEADER.index request parameter
        uint8 *pDescStart;  ///< A pointer to the descriptor to be returned for the given index/value
        uint16 length;            ///< The length of the returned descriptor
    } DESC_LUT_INFO_MEM;
    
    ...
    
    DESC_LUT_INFO_MEM * inMemDescriptorTable[DESCRIPTOR_MEMORY_TABLE_LEN] = {NULL,NULL,NULL,NULL,NULL,NULL};
    
    ...
    
    default:
          
         //printf("%d\n", HI_UINT16(usbSetupHeader.value));
          // Perform a table search (on index and value)
          usbSetupData.pBuffer = NULL;
          uint8 isDescriptorDefinedInMem = FALSE;
          for(n=0;n<DESCRIPTOR_MEMORY_TABLE_LEN;n++)
          {
             if (inMemDescriptorTable[n] != NULL && 
                 (inMemDescriptorTable[n].valueMsb == HI_UINT16(usbSetupHeader.value))
                 && (inMemDescriptorTable[n].valueLsb == LO_UINT16(usbSetupHeader.value))
                 && (inMemDescriptorTable[n].indexMsb == HI_UINT16(usbSetupHeader.index))
                 && (inMemDescriptorTable[n].indexLsb == LO_UINT16(usbSetupHeader.index)) )
             {
                usbSetupData.pBuffer = usbDescriptorMarker.pUsbDescLut[n].pDescStart;
                usbSetupData.bytesLeft = usbDescriptorMarker.pUsbDescLut[n].length;
                isDescriptorDefinedInMem = TRUE;
             }
          }
          if(isDescriptorDefinedInMem == FALSE)
          for (n = 0; n < ((uint16)usbDescriptorMarker.pUsbDescLutEnd - (uint16)usbDescriptorMarker.pUsbDescLut) / sizeof(DESC_LUT_INFO); n++) {
             if ((usbDescriptorMarker.pUsbDescLut[n].valueMsb == HI_UINT16(usbSetupHeader.value))
                 && (usbDescriptorMarker.pUsbDescLut[n].valueLsb == LO_UINT16(usbSetupHeader.value))
                 && (usbDescriptorMarker.pUsbDescLut[n].indexMsb == HI_UINT16(usbSetupHeader.index))
                 && (usbDescriptorMarker.pUsbDescLut[n].indexLsb == LO_UINT16(usbSetupHeader.index)) )
             {
                usbSetupData.pBuffer = usbDescriptorMarker.pUsbDescLut[n].pDescStart;
                usbSetupData.bytesLeft = usbDescriptorMarker.pUsbDescLut[n].length;
             }
          }
       }

    would it work?

  • Maybe - as long as you're only changing the HID report descriptors. It's worth a try.
    You might want to break the for-loops once you get a match on the setup header value and index.
  • One more question:

    Where are DESC_TYPE_HID Descriptors read? They are not requested in usbsrGetDescriptor which makes me wonder where they are read. They are referenced in usbDescLut and the DESC_TYPE_HID is nowhere else requested... Do you know how theses Descriptors are written to USB?