Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Custom USB device shows up as 2 separate devices when driver not installed

Hi there,

As mentioned in an earlier ticket I'm developing a successor to one of our products based on the nRF9160. Since it doesn't have USB we've added the nRF52820 as a USB-to-UART bridge.
Since our old product uses 2 USB interfaces with 2 endpoints each I've developed a custom UBSD class based on the CDC_ACM class.
So far everything works, with the exception of the webusb support, but that is not that urgent.

The problem is that today a colleague of mine informed me about a problem, when the device is connected the first time and our device driver isn't installed.
Instead of showing up as one device it shows up as 2 devices with the name of the interfaces. This also leads to some problems when using the device on Linux. Since it shows up as 2 devices our programs won't work properly.

Does anyone have an idea what the problem could be?
I suspect that I made a config error in sdk_config.h or the feed_descriptors function(see below).

static bool customclass_feed_descriptors(app_usbd_class_descriptor_ctx_t* p_ctx, app_usbd_class_inst_t const* p_inst,
                                  uint8_t* p_buff, size_t max_size)
{
  static uint8_t ifaces                       = 0;
  ifaces                                      = app_usbd_class_iface_count_get(p_inst);
  app_usbd_customclass_t const* p_customclass = customclass_get(p_inst);

  APP_USBD_CLASS_DESCRIPTOR_BEGIN(p_ctx, p_buff, max_size);

  static uint8_t i = 0;

  for(i = 0; i < ifaces; i++)
  {
    /* INTERFACE DESCRIPTOR */
    APP_USBD_CLASS_DESCRIPTOR_WRITE(0x09);                          // bLength
    APP_USBD_CLASS_DESCRIPTOR_WRITE(APP_USBD_DESCRIPTOR_INTERFACE); // bDescriptorType = Interface

    static app_usbd_class_iface_conf_t const* p_cur_iface = NULL;
    p_cur_iface                                           = app_usbd_class_iface_get(p_inst, i);

    APP_USBD_CLASS_DESCRIPTOR_WRITE(app_usbd_class_iface_number_get(p_cur_iface));   // bInterfaceNumber
    APP_USBD_CLASS_DESCRIPTOR_WRITE(0x00);                                           // bAlternateSetting
    APP_USBD_CLASS_DESCRIPTOR_WRITE(app_usbd_class_iface_ep_count_get(p_cur_iface)); // bNumEndpoints
    APP_USBD_CLASS_DESCRIPTOR_WRITE(0xFF); // bInterfaceClass - Vendor specific class
    APP_USBD_CLASS_DESCRIPTOR_WRITE(0x00); // bInterfaceSubClass
    APP_USBD_CLASS_DESCRIPTOR_WRITE(i);    // bInterfaceProtocol - 0 for data and 1 for debug
    APP_USBD_CLASS_DESCRIPTOR_WRITE(APP_USBD_STRINGS_USER_START_IDX + i); // iInterface

    /* ENDPOINT DESCRIPTORS */
    static uint8_t endpoints = 0;
    endpoints                = app_usbd_class_iface_ep_count_get(p_cur_iface);

    static uint8_t j = 0;

    for(j = 0; j < endpoints; j++)
    {
      APP_USBD_CLASS_DESCRIPTOR_WRITE(0x07);                         // bLength
      APP_USBD_CLASS_DESCRIPTOR_WRITE(APP_USBD_DESCRIPTOR_ENDPOINT); // bDescriptorType = Endpoint

      static app_usbd_class_ep_conf_t const* p_cur_ep = NULL;
      p_cur_ep                                        = app_usbd_class_iface_ep_get(p_cur_iface, j);
      APP_USBD_CLASS_DESCRIPTOR_WRITE(app_usbd_class_ep_address_get(p_cur_ep)); // bEndpointAddress

      APP_USBD_CLASS_DESCRIPTOR_WRITE(APP_USBD_DESCRIPTOR_EP_ATTR_TYPE_BULK); // bmAttributes
      APP_USBD_CLASS_DESCRIPTOR_WRITE(LSB_16(NRF_DRV_USBD_EPSIZE));           // wMaxPacketSize LSB
      APP_USBD_CLASS_DESCRIPTOR_WRITE(MSB_16(NRF_DRV_USBD_EPSIZE));           // wMaxPacketSize MSB
      APP_USBD_CLASS_DESCRIPTOR_WRITE(0x00);                                  // bInterval
    }
  }
  APP_USBD_CLASS_DESCRIPTOR_END();
}

With kind regards
Johannes

PS: I'm using the nRF5 SDK version 17.1.0.

Parents Reply Children
  • Hi Håkon,

    I just found the culprit.

    The problem was that bDeviceClass was set to 0. This resulted in this strange behaviour.
    After changing bDeviceClass to 0xFF it works fine, even installing the device driver works smoothly now.

    When initially developing the bridge we knew about this difference but didn't think that it would cause such a problem.
    Back then we also didn't want to modify the SDK but as it seems we need to do just that.

    Thanks for the tip with lsusb. Because of this we saw that bDeviceClass is the only difference.

    My solution is the following:
    Changing APP_USBD_CODE_DEVICE_DESCRIPTOR in app_usbd_core.c to:

    #define APP_USBD_CORE_DEVICE_DESCRIPTOR  {                                                               \
       .bLength = sizeof(app_usbd_descriptor_device_t),    /* descriptor size */                             \
       .bDescriptorType = APP_USBD_DESCRIPTOR_DEVICE,      /* descriptor type */                             \
       .bcdUSB = APP_USBD_BCD_VER_MAKE(2,0,0),             /* USB BCD version: 2.0 */                        \
       .bDeviceClass = APP_USBD_DEVICE_CLASS,              /* device class */                                \
       .bDeviceSubClass = APP_USBD_DEVICE_SUB_CLASS,       /* device subclass */                             \
       .bDeviceProtocol = APP_USBD_DEVICE_PROTOCOL,        /* device protocol */                             \
       .bMaxPacketSize0 = NRF_DRV_USBD_EPSIZE,             /* endpoint size: fixed to: NRF_DRV_USBD_EPSIZE*/ \
       .idVendor = APP_USBD_VID,                           /* Vendor ID*/                                    \
       .idProduct = APP_USBD_PID,                          /* Product ID*/                                   \
       .bcdDevice = APP_USBD_BCD_VER_MAKE(                 /* Device version BCD */                          \
           APP_USBD_DEVICE_VER_MAJOR,                                                                        \
           APP_USBD_DEVICE_VER_MINOR,                                                                        \
           APP_USBD_DEVICE_VER_SUB),                                                                         \
       .iManufacturer = APP_USBD_STRING_ID_MANUFACTURER,   /* String ID: manufacturer */                     \
       .iProduct = APP_USBD_STRING_ID_PRODUCT,             /* String ID: product */                          \
       .iSerialNumber = APP_USBD_STRING_ID_SERIAL,         /* String ID: serial */                           \
       .bNumConfigurations = 1                             /* Fixed value: only one configuration supported*/\
    }

    and adding these lines to sdk_config.h:

    #ifndef APP_USBD_DEVICE_CLASS
    #define APP_USBD_DEVICE_CLASS 0xFF
    #endif
    
    #ifndef APP_USBD_DEVICE_SUB_CLASS
    #define APP_USBD_DEVICE_SUB_CLASS 0x00
    #endif
    
    #ifndef APP_USBD_DEVICE_PROTOCOL
    #define APP_USBD_DEVICE_PROTOCOL 0x00
    #endif

    Kind regards,
    Johannes

Related