/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * version: 1.0 demo (7.02.2015)
 */

#include "usbd_core.h"
#include "usbd_desc.h"
#include "usbd_conf.h"
#include "usbd_rndis_core.h"
#include "common_config.h"

#define USB_DEVICE_DESCRIPTOR_TYPE              0x01
#define USB_STRING_DESCRIPTOR_TYPE              0x03
#define USB_SIZ_STRING_LANGID                   4
#define USB_SIZ_DEVICE_DESC                     0x12

usbd_desc_t *get_device_descriptor(void);
usbd_desc_t *get_device_qualifier(void);
usbd_desc_t *get_device_configuration(void);
usbd_desc_t *get_device_other_speed(void);
usbd_desc_t *get_device_lang_id(void);
usbd_desc_t *get_device_manufacturer_string(void);
usbd_desc_t *get_device_product_string(void);
usbd_desc_t *get_device_serial_string(void);
usbd_desc_t *get_device_config_string(void);
usbd_desc_t *get_device_interface_string(void);

uint16_t usbd_unicode_convert(uint8_t *string, uint8_t *unicode_buf);
static void usbd_int_to_unicode (uint32_t value , uint8_t *pbuf , uint8_t len);

extern usbd_core_type usb_core_dev;
extern uint8_t usbd_cdc_CfgDesc[];

usbd_desc_handler USR_desc =
{
    get_device_descriptor,
    get_device_qualifier,
    get_device_configuration,
    get_device_other_speed,
    get_device_lang_id,
    get_device_manufacturer_string,
    get_device_product_string,
    get_device_serial_string,
    get_device_interface_string,
    get_device_config_string,  
};


#if defined ( __ICCARM__ ) /* iar compiler */
  #pragma data_alignment=4
#endif
uint8_t USBD_StrDesc[USB_MAX_STR_DESC_SIZ];


#if defined ( __ICCARM__ ) /* iar compiler */
  #pragma data_alignment=4
#endif
ALIGNED_HEAD uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] ALIGNED_TAIL =
{
	18,                                 // bLength = 18 bytes.
	USB_DEVICE_DESCRIPTOR_TYPE,         // bDescriptorType = DEVICE
	0x00, 0x02,                         // bcdUSB          = 1.1 0x10,0x01  2.0 0x00,0x02
	0xE0,                               // bDeviceClass    = Communication Device Class
	0x00,                               // bDeviceSubClass = Unused at this time.
	0x00,                               // bDeviceProtocol = Unused at this time.
	0x40,                               // bMaxPacketSize0 = EP0 buffer size..
	LBYTE(USBD_VID), HBYTE(USBD_VID),   // Vendor ID
	LBYTE(USBD_PID), HBYTE(USBD_PID),   // Product ID
	0xFF, 0xFF,                         // bcdDevice 
	USB_MFC_STRING,
	USB_PRODUCT_STRING,
	USB_SERIAL_STRING,
	1
};

#if defined ( __ICCARM__ ) /* iar compiler */
  #pragma data_alignment=4
#endif
/* USB Standard Device Descriptor */
ALIGNED_HEAD uint8_t USBD_DeviceQualifierDesc[USB_DEVICE_QUALIFIER_DESC_LEN] ALIGNED_TAIL =
{
  USB_DEVICE_QUALIFIER_DESC_LEN,
  USB_DESCIPTOR_TYPE_DEVICE_QUALIFIER,
  0x00,
  0x02,
  0x00,
  0x00,
  0x00,
  0x40,
  0x01,
  0x00,
};

#if defined ( __ICCARM__ ) /* iar compiler */
  #pragma data_alignment=4
#endif
/* USB Standard Device Descriptor */
ALIGNED_HEAD uint8_t USBD_LangIDDesc[USB_SIZ_STRING_LANGID] ALIGNED_TAIL =
{
     USB_SIZ_STRING_LANGID,         
     USB_DESCIPTOR_TYPE_STRING,       
     LBYTE(USBD_LANGID_STRING),
     HBYTE(USBD_LANGID_STRING), 
};

/* device descriptor */
usbd_desc_t device_descriptor =
{
  USB_DEVICE_DESC_LEN,
  USBD_DeviceDesc
};

/* device qualifier */
usbd_desc_t device_qualifier =
{
  USB_DEVICE_QUALIFIER_DESC_LEN,
  USBD_DeviceQualifierDesc
};

/* langid descriptor */
usbd_desc_t langid_descriptor =
{
  USB_SIZ_STRING_LANGID,
  USBD_LangIDDesc
};

usbd_desc_t vp_desc;

/**
  * @brief  standard usb unicode convert
  * @param  string: source string
  * @param  unicode_buf: unicode buffer
  * @retval length                        
  */
uint16_t usbd_unicode_convert(uint8_t *string, uint8_t *unicode_buf)
{
  uint16_t str_len = 0, id_pos = 2;
  uint8_t *tmp_str = string;
  
  while(*tmp_str != '\0')
  {
    str_len ++;
    unicode_buf[id_pos ++] = *tmp_str ++;
    unicode_buf[id_pos ++] = 0x00;
  }
  
  str_len = str_len * 2 + 2;
  unicode_buf[0] = str_len;
  unicode_buf[1] = USB_DESCIPTOR_TYPE_STRING;
  
  return str_len;
}

/**
  * @brief  usb int convert to unicode
  * @param  value: int value
  * @param  pbus: unicode buffer
  * @param  len: length
  * @retval none                        
  */
static void usbd_int_to_unicode (uint32_t value , uint8_t *pbuf , uint8_t len)
{
  uint8_t idx = 0;
  
  for( idx = 0 ; idx < len ; idx ++)
  {
    if( ((value >> 28)) < 0xA )
    {
      pbuf[ 2 * idx] = (value >> 28) + '0';
  }
  else
  {
      pbuf[2 * idx] = (value >> 28) + 'A' - 10; 
    }
    
    value = value << 4;
    
    pbuf[2 * idx + 1] = 0;
  }
}

/**
  * @brief  get device descriptor
  * @param  none
  * @retval usbd_desc                         
  */
usbd_desc_t *get_device_descriptor(void)
{
  DBG printf("DeviceDescriptor\r\n");
  
  return &device_descriptor;
}

usbd_desc_t *get_device_qualifier(void)
{
  DBG printf("DeviceQulifier\r\n");
  
  return &device_qualifier;
}

usbd_desc_t *get_device_configuration(void)
{ 
  vp_desc.descriptor = usbd_rndis_get_cfg(0, &vp_desc.length);
  return &vp_desc;
}

usbd_desc_t *get_device_other_speed(void)
{
    return NULL;
}

/**
  * @brief  get lang id descriptor
  * @param  none
  * @retval usbd_desc                         
  */
usbd_desc_t *get_device_lang_id(void)
{
  DBG printf("LangIDStrDescriptor\r\n");
  return &langid_descriptor;
}

/**
  * @brief  get product descriptor
  * @param  none
  * @retval usbd_desc                         
  */
usbd_desc_t *get_device_product_string(void)
{
  DBG printf("ProductStrDescriptor\r\n");
  
  static usbd_desc_t device_product_string_disc;
  
  device_product_string_disc.length = usbd_unicode_convert((uint8_t *)USBD_PRODUCT_FS_STRING, USBD_StrDesc);
  device_product_string_disc.descriptor = USBD_StrDesc;  
  return &device_product_string_disc;
}

/**
  * @brief  get manufacturer descriptor
  * @param  none
  * @retval usbd_desc                         
  */
usbd_desc_t *get_device_manufacturer_string(void)
{
  DBG printf("ManufacturerStrDescriptor\r\n");
  
  static usbd_desc_t device_manufacturer_string_disc;
  
  device_manufacturer_string_disc.length = usbd_unicode_convert((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc);
  device_manufacturer_string_disc.descriptor = USBD_StrDesc;
  
  return &device_manufacturer_string_disc;
}

/**
  * @brief  get serial descriptor
  * @param  none
  * @retval usbd_desc                         
  */
usbd_desc_t *get_device_serial_string(void)
{
  DBG printf("SerialStrDescriptor\r\n");
  
  static usbd_desc_t device_serial_string_disc;
  
  device_serial_string_disc.length = usbd_unicode_convert((uint8_t *)USBD_SERIALNUMBER_FS_STRING, USBD_StrDesc);
  device_serial_string_disc.descriptor = USBD_StrDesc;
  return &device_serial_string_disc;
}

/**
  * @brief  get device config descriptor
  * @param  none
  * @retval usbd_desc                         
  */
usbd_desc_t *get_device_config_string(void)
{
  DBG printf("ConfigStrDescriptor\r\n");
  
  static usbd_desc_t device_config_string_disc;
  
  device_config_string_disc.length = usbd_unicode_convert((uint8_t *)USBD_CONFIGURATION_FS_STRING, USBD_StrDesc);
  device_config_string_disc.descriptor = USBD_StrDesc;
  return &device_config_string_disc;
}

/**
  * @brief  get interface descriptor
  * @param  none
  * @retval usbd_desc                         
  */
usbd_desc_t *get_device_interface_string(void)
{
  DBG printf("InterfaceStrDescriptor\r\n");
 
  static usbd_desc_t device_interface_string_disc;
  
  device_interface_string_disc.length = usbd_unicode_convert((uint8_t *)USBD_INTERFACE_FS_STRING, USBD_StrDesc);
  device_interface_string_disc.descriptor = USBD_StrDesc;
  return &device_interface_string_disc;
}
