AnsweredAssumed Answered

STM32Cube USB Composite device CDC + MSC

Question asked by Lori B. on Jul 25, 2016
Latest reply on Apr 4, 2018 by Hau Lam
Hi everybody! I'm working on a project based on STM32F217 on which I'd like to integrate both CDC and MSC classes on a single composite device.
Starting from my STM32Cube CDC based I worked to make it an MSC based project and everything works fine. The problem obviously is when I try to make a composite device.

Since there are no example from ST based on the HAL about composite devices (...)  and I'm no USB expert al all, I need some help to make it work.

First, I wrote my CDC_MSC wrapper class, putting MSC interface as first class and CDC as second and using IAP.

usbd_cdc_msc.c

#define MSC_INTERFACE 0x00
#define CDC_INTERFACE 0x01  
 
 
#define USB_CDC_MSC_CONFIG_DESC_SIZ                 (USB_CDC_CONFIG_DESC_SIZ + 8 + USB_MSC_CONFIG_DESC_SIZ - 9)
 
 
/* USB Standard Device Descriptor */
__ALIGN_BEGIN static uint8_t USBD_CDC_MSC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END =
{
  USB_LEN_DEV_QUALIFIER_DESC,
  USB_DESC_TYPE_DEVICE_QUALIFIER,
  0x00,
  0x02,
  0x00,
  0x00,
  0x00,
  0x40,
  0x01,
  0x00,
};
 
/* CDC interface class callbacks structure */
USBD_ClassTypeDef  USBD_CDC_MSC =
{
  USBD_CDC_MSC_Init,
  USBD_CDC_MSC_DeInit,
  USBD_CDC_MSC_Setup,
  NULL,                 /* EP0_TxSent, */
  USBD_CDC_MSC_EP0_RxReady,
  USBD_CDC_MSC_DataIn,
  USBD_CDC_MSC_DataOut,
  NULL,
  NULL,
  NULL,    
  USBD_CDC_MSC_GetHSCfgDesc, 
  USBD_CDC_MSC_GetFSCfgDesc,   
  USBD_CDC_MSC_GetOtherSpeedCfgDesc,
  USBD_CDC_MSC_GetDeviceQualifierDescriptor,
};
 
 
 
/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN uint8_t USBD_CDC_MSC_CfgFSDesc[USB_CDC_MSC_CONFIG_DESC_SIZ] __ALIGN_END =
{
  /*Configuration Descriptor*/
  0x09,   /* bLength: Configuration Descriptor size */
  USB_DESC_TYPE_CONFIGURATION,      /* bDescriptorType: Configuration */
        USB_CDC_MSC_CONFIG_DESC_SIZ,                /* wTotalLength:no of returned bytes */
  0x00,
        0x03,   /* bNumInterfaces: 3 interfaces (2 for CDC, 1 for MSC)*/
  0x01,   /* bConfigurationValue: Configuration value */
  0x00,   /* iConfiguration: Index of string descriptor describing the configuration */
  0xC0,   /* bmAttributes: self powered */
  0x32,   /* MaxPower 0 mA */
   
     
     
     
     /********************  Mass Storage interface ********************/
  0x09,   /* bLength: Interface Descriptor size */
  0x04,   /* bDescriptorType: */
  MSC_INTERFACE,   /* bInterfaceNumber: Number of Interface */
  0x00,   /* bAlternateSetting: Alternate setting */
  0x02,   /* bNumEndpoints*/
  0x08,   /* bInterfaceClass: MSC Class */
  0x06,   /* bInterfaceSubClass : SCSI transparent command set*/
  0x50,   /* nInterfaceProtocol */
  0x00,          /* iInterface: */
     
  /********************  Mass Storage Endpoints ********************/
  0x07,   /*Endpoint descriptor length = 7*/
  0x05,   /*Endpoint descriptor type */
  MSC_EPIN_ADDR,   /*Endpoint address (IN, address 1) */
  0x02,   /*Bulk endpoint type */
  0x40,
  0x00,
  0x00,   /*Polling interval in milliseconds */
   
  0x07,   /*Endpoint descriptor length = 7 */
  0x05,   /*Endpoint descriptor type */
  MSC_EPOUT_ADDR,   /*Endpoint address (OUT, address 1) */
  0x02,   /*Bulk endpoint type */
  0x40,
  0x00,
  0x00,     /*Polling interval in milliseconds*/
     
     
     
     
      /******** /IAD should be positioned just before the CDC interfaces ******
                IAD to associate the two CDC interfaces */
   
  0x08, /* bLength */
  0x0B, /* bDescriptorType */
  0x01, /* bFirstInterface */
  0x02, /* bInterfaceCount */
  0x02, /* bFunctionClass */
  0x02, /* bFunctionSubClass */
  0x01, /* bFunctionProtocol */
  0x00, /* iFunction (Index of string descriptor describing this function) */
     
     
     
  /*---------------------------------------------------------------------------*/
    /*Interface Descriptor */
  0x09,   /* bLength: Interface Descriptor size */
  USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: Interface */
  /* Interface descriptor type */
  CDC_INTERFACE,   /* bInterfaceNumber: Number of Interface */
  0x00,   /* bAlternateSetting: Alternate setting */
  0x01,   /* bNumEndpoints: One endpoints used */
  0x02,   /* bInterfaceClass: Communication Interface Class */
  0x02,   /* bInterfaceSubClass: Abstract Control Model */
  0x01,   /* bInterfaceProtocol: Common AT commands */
  0x01,   /* iInterface: */
   
  /*Header Functional Descriptor*/
  0x05,   /* bLength: Endpoint Descriptor size */
  0x24,   /* bDescriptorType: CS_INTERFACE */
  0x00,   /* bDescriptorSubtype: Header Func Desc */
  0x10,   /* bcdCDC: spec release number */
  0x01,
   
  /*Call Management Functional Descriptor*/
  0x05,   /* bFunctionLength */
  0x24,   /* bDescriptorType: CS_INTERFACE */
  0x01,   /* bDescriptorSubtype: Call Management Func Desc */
  0x00,   /* bmCapabilities: D0+D1 */
        0x02,   /* bDataInterface: 2 */
   
  /*ACM Functional Descriptor*/
  0x04,   /* bFunctionLength */
  0x24,   /* bDescriptorType: CS_INTERFACE */
  0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
  0x02,   /* bmCapabilities */
   
  /*Union Functional Descriptor*/
  0x05,   /* bFunctionLength */
  0x24,   /* bDescriptorType: CS_INTERFACE */
  0x06,   /* bDescriptorSubtype: Union func desc */
        0x01,   /* bMasterInterface: Communication class interface */
        0x02,   /* bSlaveInterface0: Data Class Interface */
   
  /*Endpoint 2 Descriptor*/
  0x07,                           /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,   /* bDescriptorType: Endpoint */
  CDC_CMD_EP,                     /* bEndpointAddress */
  0x03,                           /* bmAttributes: Interrupt */
  LOBYTE(CDC_CMD_PACKET_SIZE),     /* wMaxPacketSize: */
  HIBYTE(CDC_CMD_PACKET_SIZE),
        0xFF,                           /* bInterval: */
  /*---------------------------------------------------------------------------*/
   
  /*Data class interface descriptor*/
  0x09,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
        0x02,   /* bInterfaceNumber: Number of Interface */
  0x00,   /* bAlternateSetting: Alternate setting */
  0x02,   /* bNumEndpoints: Two endpoints used */
  0x0A,   /* bInterfaceClass: CDC */
  0x00,   /* bInterfaceSubClass: */
  0x00,   /* bInterfaceProtocol: */
  0x00,   /* iInterface: */
   
  /*Endpoint OUT Descriptor*/
  0x07,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
  CDC_OUT_EP,                        /* bEndpointAddress */
  0x02,                              /* bmAttributes: Bulk */
  LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
  HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
  0x00,                              /* bInterval: ignore for Bulk transfer */
   
  /*Endpoint IN Descriptor*/
  0x07,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
  CDC_IN_EP,                         /* bEndpointAddress */
  0x02,                              /* bmAttributes: Bulk */
  LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
  HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
  0x00,                               /* bInterval: ignore for Bulk transfer */
     
     
     
      
} ;
 
 
 
 
/**
  * @brief  USBD_CDC_Init
  *         Initialize the CDC interface
  * @param  pdev: device instance
  * @param  cfgidx: Configuration index
  * @retval status
  */
static uint8_t  USBD_CDC_MSC_Init (USBD_HandleTypeDef *pdev,
                               uint8_t cfgidx)
{
  uint8_t ret = 0;
     
    /*CDC*/
    USBD_CDC_Init (pdev, cfgidx);
     
    /*MSC*/
    USBD_MSC_Init (pdev, cfgidx);
     
    return ret;
}
 
/**
  * @brief  USBD_CDC_Init
  *         DeInitialize the CDC layer
  * @param  pdev: device instance
  * @param  cfgidx: Configuration index
  * @retval status
  */
static uint8_t  USBD_CDC_MSC_DeInit (USBD_HandleTypeDef *pdev,
                                 uint8_t cfgidx)
{
  uint8_t ret = 0;
   
    /*CDC*/
    USBD_CDC_DeInit (pdev, cfgidx);
     
    /*MSC*/
    USBD_MSC_DeInit (pdev, cfgidx);
   
  return ret;
}
 
/**
  * @brief  USBD_CDC_Setup
  *         Handle the CDC specific requests
  * @param  pdev: instance
  * @param  req: usb requests
  * @retval status
  */
static uint8_t  USBD_CDC_MSC_Setup (USBD_HandleTypeDef *pdev,
                                USBD_SetupReqTypedef *req)
{
   
    if(req->wIndex == CDC_INTERFACE)
    {
        return (USBD_CDC_Setup (pdev, req));
    }
    else //if (req->wIndex == MSC_INTERFACE)
    {
        return (USBD_MSC_Setup (pdev, req));
    }
}
 
/**
  * @brief  USBD_CDC_DataIn
  *         Data sent on non-control IN endpoint
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
static uint8_t  USBD_CDC_MSC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum)
{
   if (epnum == (CDC_IN_EP&~0x80) )
  {
    return (USBD_CDC_DataIn(pdev, epnum));
  }
  else
  {
    return (USBD_MSC_DataIn(pdev, epnum));
  }
}
 
/**
  * @brief  USBD_CDC_DataOut
  *         Data received on non-control Out endpoint
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
static uint8_t  USBD_CDC_MSC_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
{     
  /*DataOut can be for MSC or CDC */
   
  if (epnum == (CDC_OUT_EP&~0x80) )
  {
    return (USBD_CDC_DataOut(pdev, epnum));
  }
  else
  {
    return (USBD_MSC_DataOut(pdev, epnum));
  }  
}
 
 
 
/**
  * @brief  USBD_CDC_DataOut
  *         Data received on non-control Out endpoint
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
static uint8_t  USBD_CDC_MSC_EP0_RxReady (USBD_HandleTypeDef *pdev)
{
    /*RxReady processing needed for CDC only*/
  return USBD_CDC_EP0_RxReady (pdev);
}
 
/**
  * @brief  USBD_CDC_GetFSCfgDesc
  *         Return configuration descriptor
  * @param  speed : current device speed
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
static uint8_t  *USBD_CDC_MSC_GetFSCfgDesc (uint16_t *length)
{
  *length = sizeof (USBD_CDC_MSC_CfgFSDesc);
  return USBD_CDC_MSC_CfgFSDesc;
}
 
/**
  * @brief  USBD_CDC_GetHSCfgDesc
  *         Return configuration descriptor
  * @param  speed : current device speed
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
static uint8_t  *USBD_CDC_MSC_GetHSCfgDesc (uint16_t *length)
{
  *length = sizeof (USBD_CDC_MSC_CfgHSDesc);
  return USBD_CDC_MSC_CfgHSDesc;
}
 
/**
  * @brief  USBD_CDC_GetCfgDesc
  *         Return configuration descriptor
  * @param  speed : current device speed
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
static uint8_t  *USBD_CDC_MSC_GetOtherSpeedCfgDesc (uint16_t *length)
{
  *length = sizeof (USBD_CDC_MSC_OtherSpeedCfgDesc);
  return USBD_CDC_MSC_OtherSpeedCfgDesc;
}
 
/**
* @brief  DeviceQualifierDescriptor
*         return Device Qualifier descriptor
* @param  length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t  *USBD_CDC_MSC_GetDeviceQualifierDescriptor (uint16_t *length)
{
  *length = sizeof (USBD_CDC_MSC_DeviceQualifierDesc);
  return USBD_CDC_MSC_DeviceQualifierDesc;
}

Then, in usbd_cdc.h and usbd_msc.h I changed the endpoints.

usbd_cdc.h
#define CDC_IN_EP                                   0x83        //  0x81            /* EP1 for data IN */
#define CDC_OUT_EP                                  0x03    //  0x01        /* EP1 for data OUT */
#define CDC_CMD_EP                                  0x82                            /* EP2 for CDC commands */
 
/* CDC Endpoints parameters: you can fine tune these values depending on the needed baudrates and performance. */
#define CDC_DATA_HS_MAX_PACKET_SIZE                 512  /* Endpoint IN & OUT Packet size */
#define CDC_DATA_FS_MAX_PACKET_SIZE                 64  /* Endpoint IN & OUT Packet size */
#define CDC_CMD_PACKET_SIZE                         8  /* Control Endpoint Packet size */
 
#define USB_CDC_CONFIG_DESC_SIZ                     67
#define CDC_DATA_HS_IN_PACKET_SIZE                  CDC_DATA_HS_MAX_PACKET_SIZE
#define CDC_DATA_HS_OUT_PACKET_SIZE                 CDC_DATA_HS_MAX_PACKET_SIZE
 
#define CDC_DATA_FS_IN_PACKET_SIZE                  CDC_DATA_FS_MAX_PACKET_SIZE
#define CDC_DATA_FS_OUT_PACKET_SIZE                 CDC_DATA_FS_MAX_PACKET_SIZE


usbd_msc.h
#define MSC_MAX_FS_PACKET            0x40
#define MSC_MAX_HS_PACKET            0x200
 
#define BOT_GET_MAX_LUN              0xFE
#define BOT_RESET                    0xFF
#define USB_MSC_CONFIG_DESC_SIZ      32
  
 
#define MSC_EPIN_ADDR               /* 0x83 */  0x81
#define MSC_EPOUT_ADDR             /*  0x03 */  0x01

So In my usbd_desc.c I changed the PID and some descriptors.

 usbd_desc.c
#define USBD_VID     0x0483
 
//#define USBD_PID_FS     0x5740 //CDC
//#define USBD_PID_FS     0x572A //MSC
#define USBD_PID_FS             0x5741
 
#define USBD_LANGID_STRING                  0x0409
#define USBD_MANUFACTURER_STRING     "STMicroelectronics"
#define USBD_PRODUCT_STRING_FS                  "STM32 Composite"
/* USER CODE BEGIN SERIALNUMBER_STRING_FS */
#define USBD_SERIALNUMBER_STRING_FS         "000000000001"
/* USER CODE END SERIALNUMBER_STRING_FS */
#define USBD_CONFIGURATION_STRING_FS        "Composite Config"
#define USBD_INTERFACE_STRING_FS                "Composite Interface"


__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
          {
               0x12,                       /*bLength */
               USB_DESC_TYPE_DEVICE,       /*bDescriptorType*/
               0x00,                       /* bcdUSB */  
               0x02,
               0xEF,     //0x00,    0xEF                    /*bDeviceClass*/
               0x02,     //0x00,    0x02                /*bDeviceSubClass*/
               0x01,     //0x00,   0x01          /*bDeviceProtocol*/
               USB_MAX_EP0_SIZE,          /*bMaxPacketSize*/
               LOBYTE(USBD_VID),           /*idVendor*/
               HIBYTE(USBD_VID),           /*idVendor*/
               LOBYTE(USBD_PID_FS),           /*idVendor*/
               HIBYTE(USBD_PID_FS),           /*idVendor*/
               0x00,                       /*bcdDevice rel. 2.00*/
               0x02,
               USBD_IDX_MFC_STR,           /*Index of manufacturer  string*/
               USBD_IDX_PRODUCT_STR,       /*Index of product string*/
               USBD_IDX_SERIAL_STR,        /*Index of serial number string*/
               USBD_MAX_NUM_CONFIGURATION  /*bNumConfigurations*/
          } ; 
     /* USB_DeviceDescriptor */



Now the hard point, I'm pretty shure I have to do something about the rx and tx fifo in the USBD_LL_Init() function, but I need some help since I can't understand very well what's going on.
The commented part are the original ones from my CDC only project. This is a point where I need some help for shure.

usb_conf.c
if (pdev->id == DEVICE_FS)
    {
        /* Link The driver to the stack */ 
        hpcd_USB_OTG_FS.pData = pdev;
        pdev->pData = &hpcd_USB_OTG_FS;
         
        hpcd_USB_OTG_FS.Instance = USB_OTG_FS;
        hpcd_USB_OTG_FS.Init.dev_endpoints = 7;
        hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL;
        hpcd_USB_OTG_FS.Init.dma_enable = DISABLE;
        hpcd_USB_OTG_FS.Init.ep0_mps = DEP0CTL_MPS_64;
        hpcd_USB_OTG_FS.Init.phy_itface = PCD_PHY_EMBEDDED;
        hpcd_USB_OTG_FS.Init.Sof_enable = DISABLE;
        hpcd_USB_OTG_FS.Init.low_power_enable = DISABLE;
        hpcd_USB_OTG_FS.Init.vbus_sensing_enable = DISABLE;
        hpcd_USB_OTG_FS.Init.use_dedicated_ep1 = DISABLE;
        HAL_PCD_Init(&hpcd_USB_OTG_FS);
 
//      HAL_PCD_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80);
//      HAL_PCD_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);
//      HAL_PCD_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x80);
         
        HAL_PCD_SetRxFiFo(&hpcd_USB_OTG_FS, 0x100);
        HAL_PCD_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);
        HAL_PCD_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x80);      
        HAL_PCD_SetTxFiFo(&hpcd_USB_OTG_FS, 2, 0x40);
        HAL_PCD_SetTxFiFo(&hpcd_USB_OTG_FS, 3, 0x80);
  }

usb_conf.h
/*---------- -----------*/
#define USBD_MAX_NUM_INTERFACES     3
/*---------- -----------*/
#define USBD_MAX_NUM_CONFIGURATION     1
/*---------- -----------*/
#define USBD_MAX_STR_DESC_SIZ     64
/*---------- -----------*/
#define USBD_SUPPORT_USER_STRING     0
/*---------- -----------*/
#define USBD_DEBUG_LEVEL     0
/*---------- -----------*/
#define USBD_LPM_ENABLED     0
/*---------- -----------*/
#define USBD_SELF_POWERED     1
/*---------- -----------*/
#define USBD_CDC_INTERVAL     1000
/*---------- -----------*/
#define MSC_MEDIA_PACKET     512


Now, in my main I initialize the usb device by doing so

USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
         
        //USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC);
        //USBD_RegisterClass(&hUsbDeviceFS, &USBD_MSC);
        USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC_MSC);
         
        USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);
    //  USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS);
         
        USBD_Start(&hUsbDeviceFS);

I still have some work to do on how to Register both the interfaces, since calling USBD_MSC_RegisterStorage() ovverides the MSC function and vice-versa (any help on this is welcome too), but for now I would be happy to see one interface working at the time.

The problem is that I can use correctly the virtual com port when i set the original PID=0x5470, while switching to 0x5471 (also changing it in the .inf file and reinstalling the driver) doesn't work. 

The MSC interface instead seems a little bit more reliable and almost performs well when I leave the TxFifo and RxFifo function to their default value, so once again I think that the major problem is in that 3/4 lines.

Do you see some other big problems in my code? Since it is my first try with something particular with USB, I could be missing something really big.
Any help would be appreciated. Thank you!

Outcomes