/**
  ******************************************************************************
  * @file           : usbd_cdc_if.c
  * @brief          :
  ******************************************************************************
  *
  * Copyright (c) 2017 STMicroelectronics International N.V. 
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without 
  * modification, are permitted, provided that the following conditions are met:
  *
  * 1. Redistribution of source code must retain the above copyright notice, 
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of STMicroelectronics nor the names of other 
  *    contributors to this software may be used to endorse or promote products 
  *    derived from this software without specific written permission.
  * 4. This software, including modifications and/or derivative works of this 
  *    software, must execute solely and exclusively on microcontroller or
  *    microprocessor devices manufactured by or for STMicroelectronics.
  * 5. Redistribution and use of this software other than as permitted under 
  *    this license is void and will automatically terminate your rights under 
  *    this license. 
  *
  * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" 
  * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
  * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
  * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 
  * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
*/

/* Includes ------------------------------------------------------------------*/
#include "usbd_cdc_if.h"
#include "cat_driver.h"
#include "usb_device.h"
#include "usbd_composite.h"
#include "usbd_composite_desc.h"

/* USER CODE BEGIN INCLUDE */
/* USER CODE END INCLUDE */

/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
  * @{
  */

/** @defgroup USBD_CDC 
  * @brief usbd core module
  * @{
  */ 

/** @defgroup USBD_CDC_Private_TypesDefinitions
  * @{
  */ 
/* USER CODE BEGIN PRIVATE_TYPES */
/* USER CODE END PRIVATE_TYPES */ 
/**
  * @}
  */ 

/** @defgroup USBD_CDC_Private_Defines
  * @{
  */ 
/* USER CODE BEGIN PRIVATE_DEFINES */
/* Define size for the receive and transmit buffer over CDC */
/* It's up to user to redefine and/or remove those define */
#define APP_RX_DATA_SIZE  64
#define APP_TX_DATA_SIZE  2048
/* USER CODE END PRIVATE_DEFINES */
/**
  * @}
  */ 
/** @defgroup USBD_CDC_Private_Macros
  * @{
  */ 
/* USER CODE BEGIN PRIVATE_MACRO */
/* USER CODE END PRIVATE_MACRO */

/**
  * @}
  */ 
  
/** @defgroup USBD_CDC_Private_Variables
  * @{
  */


/* Create buffer for reception and transmission           */
/* It's up to user to redefine and/or remove those define */
/* Received Data over USB are stored in this buffer       */
uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];

/* Send Data over USB CDC are stored in this buffer       */
uint8_t UserTxBufferFS   [APP_TX_DATA_SIZE];

uint32_t CDC_Tx_PtrIn  = 0;
uint32_t CDC_Tx_PtrOut = 0;
uint32_t CDC_Tx_Length  = 0;

uint8_t  CDC_Tx_State = 0;

typedef struct
{
    uint32_t speed;
    uint8_t stop;
    uint8_t parity;
    uint8_t bits;
} CdvVcp_LineCoding_t;

static CdvVcp_LineCoding_t CDC_Linecoding =
{
        // 9600 8n1
        .speed = 9600,
        .stop  = 0, // 1 stop bit
        .bits  = 8, // 8 data bits
        .parity = 0, // parity none
};

/* USER CODE BEGIN PRIVATE_VARIABLES */
/* USER CODE END PRIVATE_VARIABLES */

/**
  * @}
  */ 
  
/** @defgroup USBD_CDC_IF_Exported_Variables
  * @{
  */ 
  __IO CdcVcp_CtrlLines_t  cdcvcp_ctrllines;
/* USER CODE BEGIN EXPORTED_VARIABLES */
/* USER CODE END EXPORTED_VARIABLES */

/**
  * @}
  */ 
  
/** @defgroup USBD_CDC_Private_FunctionPrototypes
  * @{
  */
static int8_t CDC_Init_FS     (void);
static int8_t CDC_DeInit_FS   (void);
static int8_t CDC_Control_FS  (uint8_t cmd, uint8_t* pbuf, uint16_t length);
static int8_t CDC_Receive_FS  (uint8_t* pbuf, uint32_t *Len);
static int8_t  CDC_SOF (USBD_HandleTypeDef *pdev);
static int8_t  CDC_DataIn (USBD_HandleTypeDef *pdev);

/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */

/**
  * @}
  */ 
  
USBD_CDC_ItfTypeDef USBD_Interface_fops_FS = 
{
  CDC_Init_FS,
  CDC_DeInit_FS,
  CDC_Control_FS,  
  CDC_Receive_FS,
  CDC_SOF,
  CDC_DataIn
};




/* Private functions ---------------------------------------------------------*/
/**
  * @brief  CDC_Init_FS
  *         Initializes the CDC media low layer over the FS USB IP
  * @param  None
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t CDC_Init_FS(void)
{ 
  /* USER CODE BEGIN 3 */ 
  /* Set Application Buffers */
  USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0);
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);
  return (USBD_OK);
  /* USER CODE END 3 */ 
}

/**
  * @brief  CDC_DeInit_FS
  *         DeInitializes the CDC media low layer
  * @param  None
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t CDC_DeInit_FS(void)
{
  /* USER CODE BEGIN 4 */ 
  return (USBD_OK);
  /* USER CODE END 4 */ 
}

/**
  * @brief  CDC_Control_FS
  *         Manage the CDC class requests
  * @param  cmd: Command code            
  * @param  pbuf: Buffer containing command data (request parameters)
  * @param  length: Number of data to be sent (in bytes)
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t CDC_Control_FS  (uint8_t cmd, uint8_t* pbuf, uint16_t length)
{ 
  /* USER CODE BEGIN 5 */
  switch (cmd)
  {
  case CDC_SEND_ENCAPSULATED_COMMAND:
 
    break;

  case CDC_GET_ENCAPSULATED_RESPONSE:
 
    break;

  case CDC_SET_COMM_FEATURE:
 
    break;

  case CDC_GET_COMM_FEATURE:

    break;

  case CDC_CLEAR_COMM_FEATURE:

    break;

  /*******************************************************************************/
  /* Line Coding Structure                                                       */
  /*-----------------------------------------------------------------------------*/
  /* Offset | Field       | Size | Value  | Description                          */
  /* 0      | dwDTERate   |   4  | Number |Data terminal rate, in bits per second*/
  /* 4      | bCharFormat |   1  | Number | Stop bits                            */
  /*                                        0 - 1 Stop bit                       */
  /*                                        1 - 1.5 Stop bits                    */
  /*                                        2 - 2 Stop bits                      */
  /* 5      | bParityType |  1   | Number | Parity                               */
  /*                                        0 - None                             */
  /*                                        1 - Odd                              */ 
  /*                                        2 - Even                             */
  /*                                        3 - Mark                             */
  /*                                        4 - Space                            */
  /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */
  /*******************************************************************************/
  case CDC_SET_LINE_CODING:
      // just copy the requested line coding
      // we ignore it for now but keep it to
      // satisfy the host when it asks for the current
      // line coding
	memcpy(&CDC_Linecoding,pbuf,sizeof(CDC_Linecoding));
    break;
  case CDC_GET_LINE_CODING:     
    memcpy(pbuf,&CDC_Linecoding,length);
    break;

  case CDC_SET_CONTROL_LINE_STATE:
  {
      // we get wValue here as buffer
      uint16_t wValue = *(uint16_t*)&(pbuf[0]);
      cdcvcp_ctrllines.dtr = (wValue & 0x01)?1:0;
      cdcvcp_ctrllines.rts = (wValue & 0x02)?1:0;
      break;
  }

    break;

  case CDC_SEND_BREAK:
    break;    
    
  default:
    break;
  }

  return (USBD_OK);
  /* USER CODE END 5 */
}

/**
  * @brief  CDC_Receive_FS
  *         Data received over USB OUT endpoint are sent over CDC interface 
  *         through this function.
  *           
  *         @note
  *         This function will block any OUT packet reception on USB endpoint 
  *         untill exiting this function. If you exit this function before transfer
  *         is complete on CDC interface (ie. using DMA controller) it will result 
  *         in receiving more data while previous ones are still not sent.
  *                 
  * @param  Buf: Buffer of data to be received
  * @param  Len: Number of data received (in bytes)
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
    for (uint32_t i = 0; i < *Len; i++)
    {
        CatDriver_InterfaceBufferAddData(Buf[i]);
    }

  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */ 
}

/**
  * @brief  CDC_Transmit_FS
  *         Data send over USB IN endpoint are sent over CDC interface 
  *         through this function.           
  *         @note
  *         
  *                 
  * @param  Buf: Buffer of data to be send
  * @param  Len: Number of data to be send (in bytes)
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL or USBD_BUSY
  */
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
  uint8_t result = USBD_OK;
  /* USER CODE BEGIN 7 */ 
  uint32_t tx_counter = 0;

  while (tx_counter < Len)
  {
      UserTxBufferFS[CDC_Tx_PtrIn] = *(Buf+tx_counter);

      CDC_Tx_PtrIn++;

      /* To avoid buffer overflow */
      if (CDC_Tx_PtrIn >= APP_TX_DATA_SIZE)
      {
          CDC_Tx_PtrIn = 0;
      }

      tx_counter++;
  }
  /* USER CODE END 7 */ 
  return result;
}

/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */

/**
  * @}
  */ 

/**
  * @}
  */ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


static void CDC_InitiateTransmit(USBD_HandleTypeDef *pdev)
{
    uint16_t USB_Tx_ptr;
    uint16_t USB_Tx_length;

    if (CDC_Tx_Length > CDC_DATA_FS_IN_PACKET_SIZE)
    {
        USB_Tx_ptr = CDC_Tx_PtrOut;
        USB_Tx_length = CDC_DATA_FS_IN_PACKET_SIZE;

        CDC_Tx_PtrOut += CDC_DATA_FS_IN_PACKET_SIZE;
        CDC_Tx_Length -= CDC_DATA_FS_IN_PACKET_SIZE;
    }
    else
    {
        USB_Tx_ptr = CDC_Tx_PtrOut;
        USB_Tx_length = CDC_Tx_Length;

        CDC_Tx_PtrOut += CDC_Tx_Length;
        CDC_Tx_Length = 0;
    }

    CDC_Tx_State = 1;

    USBD_CDC_SetTxBuffer(pdev,
            &UserTxBufferFS[USB_Tx_ptr],
            USB_Tx_length);
    /* Prepare the available data buffer to be sent on IN endpoint */
    USBD_CDC_TransmitPacket(pdev);

}

/**
  * @brief  Handle_USBAsynchXfer
  *         Send data to USB
  * @param  pdev: instance
  * @retval None
  */
static void CDC_Handle_USBAsynchXfer (USBD_HandleTypeDef *pdev)
{

    if(CDC_Tx_State != 1)
    {

        if (CDC_Tx_PtrOut == APP_TX_DATA_SIZE)
        {
            CDC_Tx_PtrOut = 0;
        }

        if(CDC_Tx_PtrOut == CDC_Tx_PtrIn)
        {
            CDC_Tx_Length = 0;
            CDC_Tx_State = 0;
        }
        else
        {
            if(CDC_Tx_PtrOut > CDC_Tx_PtrIn) /* rollback */
            {
                CDC_Tx_Length = APP_TX_DATA_SIZE - CDC_Tx_PtrOut;

            }
            else
            {
                CDC_Tx_Length = CDC_Tx_PtrIn - CDC_Tx_PtrOut;

            }
            CDC_InitiateTransmit(pdev);
        }
    }
}

static int8_t  CDC_DataIn (USBD_HandleTypeDef *pdev)
{
    if (CDC_Tx_State == 1)
    {
        if (CDC_Tx_Length == 0)
        {
            CDC_Tx_State = 0;
        }
        else
        {
            CDC_InitiateTransmit(pdev);
        }
    }

    return CDC_Tx_State;
}

static int8_t  CDC_SOF (USBD_HandleTypeDef *pdev)
{
    static uint32_t FrameCount = 0;

    if (FrameCount++ == 2) // every second frame
    {
        /* Reset the frame counter */
        FrameCount = 0;

        /* Check the data to be sent through IN pipe */
        CDC_Handle_USBAsynchXfer(pdev);
    }

    return USBD_OK;
}
