This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Do you the mistake in my USB descriptor?

Hello,
I am trying to add a second MSC interface to my USB device, but despite that the second interface is identified by Windows, I get a problem notification (the respective endpoint is never addressed). I just don't see what's wrong. Do you? Thanks.

/* USB Standard Device Descriptor */
const U8 USB_DeviceDescriptor[] = {
  USB_DEVICE_DESC_SIZE,              /* bLength */
  USB_DEVICE_DESCRIPTOR_TYPE,        /* bDescriptorType */
  WBVAL(0x0200), /* 2.00 */          /* bcdUSB */
  0x00,                              /* bDeviceClass */
  0x00,                              /* bDeviceSubClass */
  0x00,                              /* bDeviceProtocol */
  USB_MAX_PACKET0,                   /* bMaxPacketSize0 */
  WBVAL(0xC251),                     /* idVendor */
  WBVAL(0x1D03),                     /* idProduct */
  WBVAL(0x0100), /* 1.00 */          /* bcdDevice */
  0x01,                              /* iManufacturer */
  0x02,                              /* iProduct */
  0x03,                              /* iSerialNumber */
  0x01                               /* bNumConfigurations: one possible configuration*/
};

/* USB Configuration Descriptor */
/*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor) */
const U8 USB_ConfigDescriptor[] = {
/* Configuration 1 */
  USB_CONFIGUARTION_DESC_SIZE,       /* bLength */
  USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */
  WBVAL(                             /* wTotalLength */
    1*USB_CONFIGUARTION_DESC_SIZE +
    2*USB_INTERFACE_DESC_SIZE     +
    4*USB_ENDPOINT_DESC_SIZE
  ),
  0x02,                              /* bNumInterfaces */
  0x01,                              /* bConfigurationValue: 0x01 is used to select this configuration */
  0x00,                              /* iConfiguration: no string to describe this configuration */
  USB_CONFIG_SELF_POWERED /*|*/       /* bmAttributes */
/*USB_CONFIG_REMOTE_WAKEUP*/,
  USB_CONFIG_POWER_MA(100),          /* bMaxPower, device power consumption is 100 mA */

/* Interface 0, Alternate Setting 0, MSC Class */
  USB_INTERFACE_DESC_SIZE,           /* bLength */
  USB_INTERFACE_DESCRIPTOR_TYPE,     /* bDescriptorType */
  0x01,                              /* bInterfaceNumber */
  0x00,                              /* bAlternateSetting */
  0x02,                              /* bNumEndpoints */
  USB_DEVICE_CLASS_STORAGE,          /* bInterfaceClass */
  MSC_SUBCLASS_SCSI,                 /* bInterfaceSubClass */
  MSC_PROTOCOL_BULK_ONLY,            /* bInterfaceProtocol */
  0x04,                              /* iInterface */

/* Endpoint, EP2 Bulk IN */
  USB_ENDPOINT_DESC_SIZE,            /* bLength */
  USB_ENDPOINT_DESCRIPTOR_TYPE,      /* bDescriptorType */
  USB_ENDPOINT_IN(2),                /* bEndpointAddress */
  USB_ENDPOINT_TYPE_BULK,            /* bmAttributes */
  WBVAL(0x0040),                     /* wMaxPacketSize */
  0x00,                              /* bInterval: ignore for Bulk transfer */

/* Endpoint, EP2 Bulk OUT */
  USB_ENDPOINT_DESC_SIZE,            /* bLength */
  USB_ENDPOINT_DESCRIPTOR_TYPE,      /* bDescriptorType */
  USB_ENDPOINT_OUT(2),               /* bEndpointAddress */
  USB_ENDPOINT_TYPE_BULK,            /* bmAttributes */
  WBVAL(0x0040),                     /* wMaxPacketSize */
  0x00,                              /* bInterval: ignore for Bulk transfer */

/* Interface 1, Alternate Setting 0, MSC Class */
  USB_INTERFACE_DESC_SIZE,           /* bLength */
  USB_INTERFACE_DESCRIPTOR_TYPE,     /* bDescriptorType */
  0x02,                              /* bInterfaceNumber */
  0x00,                              /* bAlternateSetting */
  0x02,                              /* bNumEndpoints */
  USB_DEVICE_CLASS_STORAGE,          /* bInterfaceClass */
  MSC_SUBCLASS_SCSI,                 /* bInterfaceSubClass */
  MSC_PROTOCOL_BULK_ONLY,            /* bInterfaceProtocol */
  0x05,                              /* iInterface */

/* Endpoint, EP3 Bulk IN */
  USB_ENDPOINT_DESC_SIZE,            /* bLength */
  USB_ENDPOINT_DESCRIPTOR_TYPE,      /* bDescriptorType */
  USB_ENDPOINT_IN(3),                /* bEndpointAddress */
  USB_ENDPOINT_TYPE_BULK,            /* bmAttributes */
  WBVAL(0x0040),                     /* wMaxPacketSize */
  0x00,                              /* bInterval: ignore for Bulk transfer */

/* Endpoint, EP3 Bulk OUT */
  USB_ENDPOINT_DESC_SIZE,            /* bLength */
  USB_ENDPOINT_DESCRIPTOR_TYPE,      /* bDescriptorType */
  USB_ENDPOINT_OUT(3),               /* bEndpointAddress */
  USB_ENDPOINT_TYPE_BULK,            /* bmAttributes */
  WBVAL(0x0040),                     /* wMaxPacketSize */
  0x00,                              /* bInterval: ignore for Bulk transfer */

/* Terminator */
  0                                  /* bLength */
};


/* USB String Descriptor (optional) */
const U8 USB_StringDescriptor[] = {
/* Index 0x00: LANGID Codes */
  0x04,                              /* bLength */
  USB_STRING_DESCRIPTOR_TYPE,        /* bDescriptorType */
  WBVAL(0x0409), /* US English */    /* wLANGID */
/* Index 0x01: Manufacturer */
  (13*2 + 2),                        /* bLength (13 Char + Type + lenght) */
  USB_STRING_DESCRIPTOR_TYPE,        /* bDescriptorType */
  'K',0,
  'e',0,
  'i',0,
  'l',0,
  ' ',0,
  'S',0,
  'o',0,
  'f',0,
  't',0,
  'w',0,
  'a',0,
  'r',0,
  'e',0,
/* Index 0x02: Product */
  (19*2 + 2),                        /* bLength ( 19 Char + Type + lenght) */
  USB_STRING_DESCRIPTOR_TYPE,        /* bDescriptorType */
'U',0,//  'K',0,                                         //Device manager => Universal Serial Bus controllers
'S',0,//  'e',0,
'B',0,//  'i',0,
' ',0,//  'l',0,
  ' ',0,
'L',0,//  'M',0,
'P',0,//  'C',0,
'C',0,//  'B',0,
'2',0,//  '2',0,
'4',0,//  '4',0,
'7',0,//  '0',0,
'8',0,//  '0',0,
  ' ',0,
  'M',0,
  'e',0,
  'm',0,
  'o',0,
  'r',0,
  'y',0,
/* Index 0x03: Serial Number */
  (12*2 + 2),                        /* bLength (12 Char + Type + lenght) */
  USB_STRING_DESCRIPTOR_TYPE,        /* bDescriptorType */
  '0',0,                             /* allowed characters are       */
  '0',0,                             /*   0x0030 - 0x0039 ('0'..'9') */
  '0',0,                             /*   0x0041 - 0x0046 ('A'..'F') */
  '1',0,                             /*   length >= 26               */
  'A',0,
  '0',0,
  '0',0,
  '0',0,
  '0',0,
  '0',0,
  '0',0,
  '0',0,
/* Index 0x04: Interface 0, Alternate Setting 0 */
  ( 9*2 + 2),                        /* bLength (6 Char + Type + lenght) */
  USB_STRING_DESCRIPTOR_TYPE,        /* bDescriptorType */
  'I',0,
  '/',0,
  'O',0,
  ' ',0,
  'D',0,
  'r',0,
  'i',0,
  'v',0,
  'e',0,
/* Index 0x05: Interface 1, Alternate Setting 0 */
  ( 12*2 + 2),                        /* bLength (6 Char + Type + lenght) */
  USB_STRING_DESCRIPTOR_TYPE,        /* bDescriptorType */
  'O',0,
  'u',0,
  't',0,
  'p',0,
  'u',0,
  't',0,
  ' ',0,
  'D',0,
  'r',0,
  'i',0,
  'v',0,
  'e',0,
};

Parents
  • Multiple LUN implementation

    Based on this Keil example,
    LPC2368 / LPC2378 USB Mass Storage Device Example (and for LPC2458/2468)
    http://www.keil.com/download/docs/336.asp

    Get_Max_LUN request returns the last LUN supported.
    LUN numbering starts with zero.

    mscuser.c
    
    #define MSC_NUM_OF_LUNS    2                       // support two drives on this MSC IF
    #define MSC_LAST_LUN       (MSC_NUM_OF_LUNS - 1)   // LUN start with zero
    
    /*
     *  MSC Get Max LUN Request Callback
     *   Called automatically on Get Max LUN Request
     *    Parameters:      None (global SetupPacket and EP0Buf)
     *    Return Value:    TRUE - Success, FALSE - Error
     */
    
    BOOL MSC_GetMaxLUN (void) {
      EP0Buf[0] = MSC_LAST_LUN;    // return the last LUN
      return (TRUE);
    }
    

    CBW.bLUN holds the target LUN for the SCSI commands.

    /*
     *  MSC Get Command Block Wrapper Callback
     *    Parameters:      None (global variables)
     *    Return Value:    None
     */
    
    void MSC_GetCBW (void) {
      DWORD n;
    
      for (n = 0; n < BulkLen; n++) {
        *((BYTE *)&CBW + n) = BulkBuf[n];
      }
      if ((BulkLen == sizeof(CBW)) && (CBW.dSignature == MSC_CBW_Signature)) {
        /* Valid CBW */
        CSW.dTag = CBW.dTag;
        CSW.dDataResidue = CBW.dDataLength;
        if ((CBW.bLUN  > MSC_LAST_LUN) || (CBW.bCBLength < 1) || CBW.bCBLength > 16) {
    fail: CSW.bStatus = CSW_CMD_FAILED;
          MSC_SetCSW();
        } else {
          ...
    

    Handle these SCSI command for the specified drive by CBW.bLUN
    If the command does the same process for the two drives, you don't need to modify these handlers.
    mscuser.c
    - MSC_TestUnitReady()
    - MSC_Inquiry()
    - MSC_ModeSense6()
    - MSC_ModeSense10()
    - MSC_ReadFormatCapacity()
    - MSC_ReadCapacity()

    These command handler should be modified to process the target drive by the CBW.bLUN (0 or 1)
    mscuser.c
    - MSC_RequestSense()
    - MSC_RWSetup()
    - MSC_MemoryRead()
    - MSC_MemoryWrite()
    - MSC_MemoryVerify()

    For example, this MSC_MemoryRead() returns different memory location depending on LUN.

    /*
     *  MSC Memory Read Callback
     *   Called automatically on Memory Read Event
     *    Parameters:      None (global variables)
     *    Return Value:    None
     */
    
    void MSC_MemoryRead (void) {
      DWORD n;
      DWORD drive_offset;
    
      switch ( CBW.bLUN ) {
         case 0:  drive_offset = 0;
         case 1:  drive_offset = MSC_MemorySize;
      }
      if (Length > MSC_MAX_PACKET) {
        n = MSC_MAX_PACKET;
      } else {
        n = Length;
      }
    
      if ((Offset + n) > MSC_MemorySize) {
        n = MSC_MemorySize - Offset;
        BulkStage = MSC_BS_DATA_IN_LAST_STALL;
      }
    
      USB_WriteEP(MSC_EP_IN, &Memory[Offset + drive_offset], n);
      Offset += n;
      Length -= n;
    
      CSW.dDataResidue -= n;
    
      if (Length == 0) {
        BulkStage = MSC_BS_DATA_IN_LAST;
      }
    
      if (BulkStage != MSC_BS_DATA_IN) {
        FIO2CLR = LED_RD;            /* Turn Off Read LED */
        CSW.bStatus = CSW_CMD_PASSED;
      }
    }
    

    Tsuneo

Reply
  • Multiple LUN implementation

    Based on this Keil example,
    LPC2368 / LPC2378 USB Mass Storage Device Example (and for LPC2458/2468)
    http://www.keil.com/download/docs/336.asp

    Get_Max_LUN request returns the last LUN supported.
    LUN numbering starts with zero.

    mscuser.c
    
    #define MSC_NUM_OF_LUNS    2                       // support two drives on this MSC IF
    #define MSC_LAST_LUN       (MSC_NUM_OF_LUNS - 1)   // LUN start with zero
    
    /*
     *  MSC Get Max LUN Request Callback
     *   Called automatically on Get Max LUN Request
     *    Parameters:      None (global SetupPacket and EP0Buf)
     *    Return Value:    TRUE - Success, FALSE - Error
     */
    
    BOOL MSC_GetMaxLUN (void) {
      EP0Buf[0] = MSC_LAST_LUN;    // return the last LUN
      return (TRUE);
    }
    

    CBW.bLUN holds the target LUN for the SCSI commands.

    /*
     *  MSC Get Command Block Wrapper Callback
     *    Parameters:      None (global variables)
     *    Return Value:    None
     */
    
    void MSC_GetCBW (void) {
      DWORD n;
    
      for (n = 0; n < BulkLen; n++) {
        *((BYTE *)&CBW + n) = BulkBuf[n];
      }
      if ((BulkLen == sizeof(CBW)) && (CBW.dSignature == MSC_CBW_Signature)) {
        /* Valid CBW */
        CSW.dTag = CBW.dTag;
        CSW.dDataResidue = CBW.dDataLength;
        if ((CBW.bLUN  > MSC_LAST_LUN) || (CBW.bCBLength < 1) || CBW.bCBLength > 16) {
    fail: CSW.bStatus = CSW_CMD_FAILED;
          MSC_SetCSW();
        } else {
          ...
    

    Handle these SCSI command for the specified drive by CBW.bLUN
    If the command does the same process for the two drives, you don't need to modify these handlers.
    mscuser.c
    - MSC_TestUnitReady()
    - MSC_Inquiry()
    - MSC_ModeSense6()
    - MSC_ModeSense10()
    - MSC_ReadFormatCapacity()
    - MSC_ReadCapacity()

    These command handler should be modified to process the target drive by the CBW.bLUN (0 or 1)
    mscuser.c
    - MSC_RequestSense()
    - MSC_RWSetup()
    - MSC_MemoryRead()
    - MSC_MemoryWrite()
    - MSC_MemoryVerify()

    For example, this MSC_MemoryRead() returns different memory location depending on LUN.

    /*
     *  MSC Memory Read Callback
     *   Called automatically on Memory Read Event
     *    Parameters:      None (global variables)
     *    Return Value:    None
     */
    
    void MSC_MemoryRead (void) {
      DWORD n;
      DWORD drive_offset;
    
      switch ( CBW.bLUN ) {
         case 0:  drive_offset = 0;
         case 1:  drive_offset = MSC_MemorySize;
      }
      if (Length > MSC_MAX_PACKET) {
        n = MSC_MAX_PACKET;
      } else {
        n = Length;
      }
    
      if ((Offset + n) > MSC_MemorySize) {
        n = MSC_MemorySize - Offset;
        BulkStage = MSC_BS_DATA_IN_LAST_STALL;
      }
    
      USB_WriteEP(MSC_EP_IN, &Memory[Offset + drive_offset], n);
      Offset += n;
      Length -= n;
    
      CSW.dDataResidue -= n;
    
      if (Length == 0) {
        BulkStage = MSC_BS_DATA_IN_LAST;
      }
    
      if (BulkStage != MSC_BS_DATA_IN) {
        FIO2CLR = LED_RD;            /* Turn Off Read LED */
        CSW.bStatus = CSW_CMD_PASSED;
      }
    }
    

    Tsuneo

Children