#include "usbd_usbtmc_desc.h"
#include "usbd_usbtmc.h"
#include "usbd_desc.h"
#include "usbd_ctlreq.h"

static const uint8_t * USBD_USBTMC_GetDeviceDescriptor( USBD_SpeedTypeDef speed , uint16_t *length);  
//static const uint8_t * USBD_USBTMC_GetLangIDStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length); 
static const uint8_t * USBD_USBTMC_GetManufacturerStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length);  
static const uint8_t * USBD_USBTMC_GetProductStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length);  
static const uint8_t * USBD_USBTMC_GetSerialStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length);  
static const uint8_t * USBD_USBTMC_GetConfigurationStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length);  
static const uint8_t * USBD_USBTMC_GetInterfaceStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length); 
static const uint8_t * USBD_USBTMC_GetCfgDesc( USBD_SpeedTypeDef speed ,uint16_t *length );

static const uint8_t * USBD_USBTMC_GetMsOsStringDescriptor (USBD_SpeedTypeDef speed, uint16_t *length);
static const uint8_t * USBD_USBTMC_GetExtCompatIdOsFeatureDescriptor(USBD_SpeedTypeDef speed, uint16_t * length);
static const uint8_t * USBD_USBTMC_GetExtPropertiesOsFeatureDescriptor(USBD_SpeedTypeDef speed, uint16_t * length);


#define USBD_USBTMC_LANGID_STRING             1033

const USBD_DescriptorsTypeDef USBD_USBTMC_DescriptorHandlers = {

#if USBD_GETDESCRIPTORS_ALTMETHOD == 1
  // default handlers
  USBD_GETDESCRIPTOR_DEFAULT_HANDLERS_SET,
#endif
   // overrided handlers:
   .GetDeviceDescriptor                 = USBD_USBTMC_GetDeviceDescriptor,
 //.GetLangIDStrDescriptor              = USBD_USBTMC_GetLangIDStrDescriptor,
   .GetManufacturerStrDescriptor        = USBD_USBTMC_GetManufacturerStrDescriptor,
   .GetProductStrDescriptor             = USBD_USBTMC_GetProductStrDescriptor,
 // .GetSerialStrDescriptor              = USBD_USBTMC_GetSerialStrDescriptor,
   .GetConfigurationStrDescriptor       = USBD_USBTMC_GetConfigurationStrDescriptor,
   .GetInterfaceStrDescriptor           = USBD_USBTMC_GetInterfaceStrDescriptor, 
   .GetConfigDescriptor                 = USBD_USBTMC_GetCfgDesc,
   .GetMsOsStringDescriptor             = USBD_USBTMC_GetMsOsStringDescriptor,
   .GetExtCompatIdOsFeatureDescriptor   = USBD_USBTMC_GetExtCompatIdOsFeatureDescriptor,
   .GetExtPropertiesOsFeatureDescriptor = USBD_USBTMC_GetExtPropertiesOsFeatureDescriptor,
};

#if defined ( __ICCARM__ ) /* IAR Compiler */
  #pragma data_alignment=4
#endif /* defined ( __ICCARM__ ) */
/** USB standard device descriptor. */
static __ALIGN_BEGIN const uint8_t USBD_USBTMC_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{
  USB_LEN_DEV_DESC,           /*bLength */
  USB_DESC_TYPE_DEVICE,       /*bDescriptorType*/
  LOBYTE(0x0200),             /*bcdUSB */
  HIBYTE(0x0200),             /*bcdUSB */         
  0x00,                      /*bDeviceClass*/       // 0x02,
  0x00,                      /*bDeviceSubClass*/    // 0x02,
  0x00,                      /*bDeviceProtocol*/    // 0x00,
  USB_MAX_EP0_SIZE,           /*bMaxPacketSize*/
  LOBYTE(USB_VID),           /*idVendor*/
  HIBYTE(USB_VID),           /*idVendor*/
  LOBYTE(USB_PID_USBTMC),    /*idProduct*/
  HIBYTE(USB_PID_USBTMC),    /*idProduct*/
  LOBYTE(0x0200),             /*bcdDevice rel. 2.00*/
  HIBYTE(0x0200),
  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*/
};

/**
  * @brief  Return the device descriptor
  * @param  speed : Current device speed
  * @param  length : Pointer to data length variable
  * @retval Pointer to descriptor buffer
  */
static const uint8_t * USBD_USBTMC_GetDeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  *length = sizeof(USBD_USBTMC_DeviceDesc);
  return USBD_USBTMC_DeviceDesc;
}

#define USB_USBTMC_CONFIG_DESC_SIZ                     39

/* USBTMC device Configuration Descriptor */
static __ALIGN_BEGIN const uint8_t USBD_USBTMC_CfgDesc[USB_USBTMC_CONFIG_DESC_SIZ] __ALIGN_END =
{
    /* CONFIGURATION */
  /*Configuration Descriptor*/
  0x09,   /* bLength: Configuration Descriptor size */
  USB_DESC_TYPE_CONFIGURATION,      /* bDescriptorType: Configuration */
  USB_USBTMC_CONFIG_DESC_SIZ,       /* wTotalLength: # of returned bytes */
  0x00,
  0x01,   /* bNumInterfaces: 1 interface */
  0x01,   /* bConfigurationValue: Configuration value */
  USBD_IDX_CONFIG_STR,   /* iConfiguration: Index of string descriptor describing the configuration */
  0x80,   /* bmAttributes: no options */
  0x32,   /* MaxPower 0 mA */
  
  /*---------------------------------------------------------------------------*/
  /* INTERFACE */
  /*Data class interface descriptor*/
  0x09,   /* bLength: Interface Descriptor size */
  USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
  0x00,   /* bInterfaceNumber: Number of Interface */
  0x00,   /* bAlternateSetting: Alternate setting */
  0x03,   /* bNumEndpoints: BulkIn+BulkOut+IntIn */
  
  0xFE,   /* bInterfaceClass: 0xFE; USBTMC spec. rev 1.0, 2003, p. 33, 5.5 Interface Descriptor */
  0x03,   /* bInterfaceSubClass: 0x03; USBTMC spec. rev 1.0, 2003, p. 33, 5.5 Interface Descriptor */
  0x01,   /* bInterfaceProtocol: 0x01; USBTMC spec. rev 1.0, 2003, p. 33, 5.5 Interface Descriptor */

  USBD_IDX_INTERFACE_STR,   /* iInterface: */  

  /*---------------------------------------------------------------------------*/
  /* BULKOUT Endpoint */
  0x07,   /* bLength: Endpoint Descriptor size */ 
  USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */
  USBTMC_BULKOUT_EP, /* bEndpointAddress */ 
  0x02, /* bmAttributes: Bulk */
  LOBYTE( USBTMC_DATAOUT_MAX_PACKET_SIZE), /* wMaxPacketSize, Low byte */
  HIBYTE( USBTMC_DATAOUT_MAX_PACKET_SIZE), /* wMaxPacketSize, High byte */
  0x00, /* bInterval */

  /*---------------------------------------------------------------------------*/
  /* BULKIN Endpoint */
  0x07,   /* bLength: Endpoint Descriptor size */ 
  USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */
  USBTMC_BULKIN_EP, /* bEndpointAddress */ 
  0x02, /* bmAttributes: Bulk */
  LOBYTE( USBTMC_DATAIN_MAX_PACKET_SIZE), /* wMaxPacketSize, Low byte */
  HIBYTE( USBTMC_DATAIN_MAX_PACKET_SIZE), /* wMaxPacketSize, High byte */
  0x00, /* bInterval */

  /*---------------------------------------------------------------------------*/
  /* INTERRUPTIN Endpoint */
  0x07,   /* bLength: Endpoint Descriptor size */ 
  USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */
  USBTMC_INTERRUPTIN_EP, /* bEndpointAddress */ 
  0x03, /* bmAttributes: Interrupt */
  LOBYTE( USBTMC_INTIN_MAX_PACKET_SIZE), /* wMaxPacketSize, Low byte */
  HIBYTE( USBTMC_INTIN_MAX_PACKET_SIZE), /* wMaxPacketSize, High byte */
  0x08, /* bInterval: VALUE = 2 ^ (bInterval-1);   1<=bInterval<=16, see USB 2.0 Spec, Table 9-13. Standard Endpoint Descriptor (Continued) */ 
};

/**
  * @brief  USBD_USBTMC_GetCfgDesc 
  *         Return configuration descriptor
  * @param  speed : current device speed
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
static const uint8_t * USBD_USBTMC_GetCfgDesc(USBD_SpeedTypeDef speed,  uint16_t *length)
{
  *length = sizeof (USBD_USBTMC_CfgDesc);
  return USBD_USBTMC_CfgDesc;
}

/* USB Standard Device Qualifier Descriptor */
static __ALIGN_BEGIN const uint8_t USBD_USBTMC_DevQual[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END =
{
  USB_LEN_DEV_QUALIFIER_DESC,
  USB_DESC_TYPE_DEVICE_QUALIFIER,
  0x00,              // bcdUSB
  0x02,              // bcdUSB
  0x00,              // bDeviceClass
  0x00,              // bDeviceSubClass
  0x00,              // bDeviceProtocol
  USB_MAX_EP0_SIZE,  // bMaxPacketSize0
  0x01,              // bNumConfigurations
  0x00,              // bReserved
};

#if defined ( __ICCARM__ ) /* IAR Compiler */
  #pragma data_alignment=4
#endif /* defined ( __ICCARM__ ) */

/** USB lang indentifier descriptor. */
/*
static __ALIGN_BEGIN const uint8_t USBD_USBTMC_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END =
{
     USB_LEN_LANGID_STR_DESC,
     USB_DESC_TYPE_STRING,
     LOBYTE(USBD_USBTMC_LANGID_STRING),
     HIBYTE(USBD_USBTMC_LANGID_STRING)
};
*/
/**
  * @brief  Return the LangID string descriptor
  * @param  speed : Current device speed
  * @param  length : Pointer to data length variable
  * @retval Pointer to descriptor buffer
  */ 
/*        
static const uint8_t * USBD_USBTMC_GetLangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  *length = sizeof(USBD_USBTMC_LangIDDesc);
  return USBD_USBTMC_LangIDDesc;
}
*/
#if defined(CMT)
//static const uint8_t USBD_USBTMC_MANUFACTURER_STRING[] = { 122, USB_DESC_TYPE_STRING,
//  'C', 0, 'o', 0, 'p', 0, 'p', 0, 'e', 0, 'r', 0, ' ', 0, 'M', 0, 'o', 0, 'u', 0,  'n', 0, 't', 0, 'a', 0, 'i', 0, 'n', 0, ' ', 0, 'T', 0, 'e', 0, 'c', 0, 'h', 0, 'n', 0, 'o', 0, 'l', 0, 'o', 0, 'g', 0, 'i', 0, 'e', 0, 's', 0,  ' ', 0, '(', 0, 'h', 0, 't', 0,  't', 0, 'p', 0, ':', 0, '/', 0, '/', 0, 'c', 0, 'o', 0, 'p', 0, 'p', 0, 'e', 0, 'r', 0, 'm', 0, 'o', 0, 'u', 0,  'n', 0, 't', 0, 'a', 0, 'i', 0, 'n', 0, 't', 0, 'e', 0, 'c', 0, 'h', 0, '.', 0, 'c', 0, 'o', 0, 'm', 0, ')', 0, 
//};
static const uint8_t USBD_USBTMC_MANUFACTURER_STRING[] = { 8, USB_DESC_TYPE_STRING,
  'C', 0, 'M', 0, 'T', 0,
};
#elif defined(PLANAR)
static const uint8_t USBD_USBTMC_MANUFACTURER_STRING[] = { 68, USB_DESC_TYPE_STRING,
  'P', 0,'l', 0,'a', 0,'n', 0,'a', 0,'r', 0,' ', 0,'(', 0,'h', 0,'t', 0, 't', 0,'p', 0,':', 0,'/', 0,'/', 0,'w', 0,'w', 0,'w', 0,'.', 0,'p', 0,'l', 0, 'a', 0, 'n', 0, 'a', 0, 'r', 0, 'c', 0, 'h', 0, 'e', 0, 'l', 0, '.', 0, 'r', 0, 'u', 0, ')', 0,
};
#else
#error Specify PLANAR or CMT macro in project settings!
#endif

/**
  * @brief  Return the manufacturer string descriptor
  * @param  speed : Current device speed
  * @param  length : Pointer to data length variable
  * @retval Pointer to descriptor buffer
  */
static const uint8_t * USBD_USBTMC_GetManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  *length = sizeof(USBD_USBTMC_MANUFACTURER_STRING);
  return USBD_USBTMC_MANUFACTURER_STRING;
}

static const uint8_t USBD_USBTMC_PRODUCT_STRING[] = { 38, USB_DESC_TYPE_STRING, 
  'M', 0,'K', 0,'2', 0,'0', 0,'7', 0,'-', 0,'0', 0, '4', 0, 'H', 0, ' ', 0,  ' ', 0, ' ', 0, ' ', 0,' ', 0,' ', 0,' ', 0,' ', 0,' ', 0,                         
};

/**
  * @brief  Return the product string descriptor
  * @param  speed : Current device speed
  * @param  length : Pointer to data length variable
  * @retval Pointer to descriptor buffer
  */
static const uint8_t * USBD_USBTMC_GetProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  *length = sizeof(USBD_USBTMC_PRODUCT_STRING);
  return USBD_USBTMC_PRODUCT_STRING;
}

#if USBD_GETDESCRIPTORS_ALTMETHOD == 0
static const uint8_t * USBD_USBTMC_GetMsOsStringDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
#else
const uint8_t * USBD_USBTMC_GetMsOsStringDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
#endif
{
  *length = 0;
  return 0;
}

#if USBD_GETDESCRIPTORS_ALTMETHOD == 0
static const uint8_t * USBD_USBTMC_GetExtCompatIdOsFeatureDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
#else
const uint8_t * USBD_USBTMC_GetExtCompatIdOsFeatureDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
#endif
{
  *length = 0;
  return 0;
}

#if USBD_GETDESCRIPTORS_ALTMETHOD == 0
static const uint8_t * USBD_USBTMC_GetExtPropertiesOsFeatureDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
#else
const uint8_t * USBD_USBTMC_GetExtPropertiesOsFeatureDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
#endif
{
  *length = 0;
  return 0;
}

// // Dynamic Serial Number Descriptor is implemented.
// //
// #define MAX_USB_SERIAL_NUMBER_LEN     8
// #pragma pack( push, 1 )
// static union
// {
//     uint8_t raw[0];
//     struct
//     {
//         uint8_t bLength;
//         uint8_t bType;
//         uint8_t aSerial[ (MAX_USB_SERIAL_NUMBER_LEN) * 2 ];
//     };
// }
// sUSBSerialNumberDescriptor;
// #pragma pack(pop)

// /**
//   * @brief  Return the serial number string descriptor
//   * @param  speed : Current device speed
//   * @param  length : Pointer to data length variable
//   * @retval Pointer to descriptor buffer
//   */
// static const uint8_t * USBD_USBTMC_GetSerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
// { 
//   uint8_t * pDest = sUSBSerialNumberDescriptor.aSerial;
//   const uint8_t * pSrc = ACMClass->properties.serialNumber;
// 
//   size_t n = strlen( pSrc );
// 
//   if( n * 2 > sizeof( sUSBSerialNumberDescriptor.aSerial) )
//   {
//       n = sizeof( sUSBSerialNumberDescriptor.aSerial) / 2;
//   }
// 
//   sUSBSerialNumberDescriptor.bType = USB_DESC_TYPE_STRING;
//   sUSBSerialNumberDescriptor.bLength = *length = sizeof(sUSBSerialNumberDescriptor.bLength)
//                                                + sizeof(sUSBSerialNumberDescriptor.bType)
//                                                + 2 * n;
//   while( n > 0 )
//   {
//       n--;
//       *pDest = *pSrc;
//       pSrc++; pDest++;
//       *pDest = '\0';
//       pDest++;
//   }
//   
//   return sUSBSerialNumberDescriptor.raw;
// }

static const uint8_t USBD_USBTMC_CONFIGURATION_STRING[] = { 94, USB_DESC_TYPE_STRING,
  'U',0,'S',0,'B',0,'T',0,'M',0,'C',0,'-',0,'U',0,'S',0,'B',0,'4',0,'8',0,'8',0,' ',0,
  '(',0,'T',0,'e',0,'s',0,'t',0,' ',0,'a',0,'n',0,'d',0,' ',0,
  'M',0,'e',0,'a',0,'s',0,'u',0,'r',0,'e',0,'m',0,'e',0,'n',0,'t',0,' ',0,
  'I',0,'n',0,'t',0,'e',0,'r',0,'f',0,'a',0,'c',0,'e',0,')',0
};

/**
  * @brief  Return the configuration string descriptor
  * @param  speed : Current device speed
  * @param  length : Pointer to data length variable
  * @retval Pointer to descriptor buffer
  */
const uint8_t * USBD_USBTMC_GetConfigurationStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{    
  *length = sizeof(USBD_USBTMC_CONFIGURATION_STRING);
  return USBD_USBTMC_CONFIGURATION_STRING;
}

static const uint8_t USBD_USBTMC_INTERFACE_STRING[] = { 94, USB_DESC_TYPE_STRING,
  'U',0,'S',0,'B',0,'T',0,'M',0,'C',0,'-',0,'U',0,'S',0,'B',0,'4',0,'8',0,'8',0,' ',0,
  '(',0,'T',0,'e',0,'s',0,'t',0,' ',0,'a',0,'n',0,'d',0,' ',0,
  'M',0,'e',0,'a',0,'s',0,'u',0,'r',0,'e',0,'m',0,'e',0,'n',0,'t',0,' ',0,
  'I',0,'n',0,'t',0,'e',0,'r',0,'f',0,'a',0,'c',0,'e',0,')',0
};

/**
  * @brief  Return the interface string descriptor
  * @param  speed : Current device speed
  * @param  length : Pointer to data length variable
  * @retval Pointer to descriptor buffer
  */
static const uint8_t * USBD_USBTMC_GetInterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{  
  *length = sizeof(USBD_USBTMC_INTERFACE_STRING);
  return USBD_USBTMC_INTERFACE_STRING;
}