/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    ux_device_cdc_acm.c
  * @author  MCD Application Team
  * @brief   USBX Device CDC ACM applicative source file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2020-2021 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "ux_device_cdc_acm.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "main.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

#define APP_RX_DATA_SIZE   2048
#define APP_TX_DATA_SIZE   2048

/* Rx/TX flag */
#define RX_NEW_RECEIVED_DATA      0x01
#define TX_NEW_TRANSMITTED_DATA   0x02

/* Data length for vcp */
#define VCP_WORDLENGTH8  8
#define VCP_WORDLENGTH9  9

/* the minimum baudrate */
#define MIN_BAUDRATE     9600

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

extern uint8_t      USBDeviceVComConnected;

UX_SLAVE_CLASS_CDC_ACM  *device_cdc_acm;

#if defined ( __ICCARM__ ) /* IAR Compiler */
#pragma location = ".UsbxAppSection"
#elif defined ( __CC_ARM ) || defined(__ARMCC_VERSION) /* ARM Compiler 5/6 */
__attribute__((section(".UsbxAppSection")))
#elif defined ( __GNUC__ ) /* GNU Compiler */
__attribute__((section(".UsbxAppSection")))
#endif
uint8_t UserRxBufferDevice[APP_RX_DATA_SIZE];
uint32_t UserRxBuffOffsetDevice;
uint32_t UserRxBuffSizeDevice;
uint8_t UserTxBufferDevice[APP_TX_DATA_SIZE];
uint32_t UserTxBuffOffsetDevice;
uint32_t UserTxBuffSizeDevice;
uint8_t UserTxTransmittingDevice;

extern TX_EVENT_FLAGS_GROUP DeviceReceiveEventFlags;
extern TX_EVENT_FLAGS_GROUP DeviceTransmitEventFlags;

typedef struct
{
  uint32_t BaudRate;
  uint32_t WordLength;
  uint32_t StopBits;
  uint32_t Parity;
  uint32_t HwFlowCtl;
} UART_CONFIG;

UART_CONFIG uart_config;

UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER CDC_VCP_LineCoding =
{
  115200, /* baud rate */
  0x00,   /* stop bits-1 */
  0x00,   /* parity - none */
  0x08    /* nb. of bits 8 */
};

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
static void USBD_CDC_VCP_Config(UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER *);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  USBD_CDC_ACM_Activate
  *         This function is called when insertion of a CDC ACM device.
  * @param  cdc_acm_instance: Pointer to the cdc acm class instance.
  * @retval none
  */
VOID USBD_CDC_ACM_Activate(VOID *cdc_acm_instance)
{
  /* USER CODE BEGIN USBD_CDC_ACM_Activate */
  UX_PARAMETER_NOT_USED(cdc_acm_instance);
//  printf(" *** %s *** \n", __FUNCTION__);

  /* Save the CDC instance */
  device_cdc_acm = (UX_SLAVE_CLASS_CDC_ACM*) cdc_acm_instance;

  uart_config.BaudRate = MIN_BAUDRATE;
  uart_config.WordLength = UART_WORDLENGTH_8B;
  uart_config.StopBits = UART_STOPBITS_1;
  uart_config.Parity = UART_PARITY_NONE;
  uart_config.HwFlowCtl = UART_HWCONTROL_NONE;

  /* Get default UART parameters */
  CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_baudrate = uart_config.BaudRate;

  /* Set the UART data type : only 8bits and 9bits are supported */
  switch (uart_config.WordLength)
  {
    case UART_WORDLENGTH_8B:
    {
      /* Set UART data bit to 8 */
      CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_data_bit = VCP_WORDLENGTH8;
      break;
    }

    case UART_WORDLENGTH_9B:
    {
      /* Set UART data bit to 9 */
      CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_data_bit = VCP_WORDLENGTH9;
      break;
    }

    default :
    {
      /* By default set UART data bit to 8 */
      CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_data_bit = VCP_WORDLENGTH8;
      break;
    }
  }

  /* Get UART Parity */
  CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_parity = uart_config.Parity; //uart_handler->Init.Parity;

  /* Get UART StopBits */
  CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_stop_bit = uart_config.StopBits; //uart_handler->Init.StopBits;

  /* Set device class_cdc_acm with default parameters */
  if (ux_device_class_cdc_acm_ioctl(device_cdc_acm, UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING,
                                    &CDC_VCP_LineCoding) != UX_SUCCESS)
  {
    Error_Handler();
  }

  USBDeviceVComConnected = 1;
  USBD_VComAttached();
//  printf(" *** %s *** complete\n", __FUNCTION__);
  /* USER CODE END USBD_CDC_ACM_Activate */

  return;
}

/**
  * @brief  USBD_CDC_ACM_Deactivate
  *         This function is called when extraction of a CDC ACM device.
  * @param  cdc_acm_instance: Pointer to the cdc acm class instance.
  * @retval none
  */
VOID USBD_CDC_ACM_Deactivate(VOID *cdc_acm_instance)
{
  /* USER CODE BEGIN USBD_CDC_ACM_Deactivate */
  UX_PARAMETER_NOT_USED(cdc_acm_instance);

  USBDeviceVComConnected = 0;
  USBD_VComUnattached();

  /* Reset the cdc acm instance */
  device_cdc_acm = UX_NULL;

//  printf(" *** %s *** \n", __FUNCTION__);
  /* USER CODE END USBD_CDC_ACM_Deactivate */

  return;
}

/**
  * @brief  USBD_CDC_ACM_ParameterChange
  *         This function is invoked to manage the CDC ACM class requests.
  * @param  cdc_acm_instance: Pointer to the cdc acm class instance.
  * @retval none
  */
VOID USBD_CDC_ACM_ParameterChange(VOID *cdc_acm_instance)
{
  /* USER CODE BEGIN USBD_CDC_ACM_ParameterChange */
  UX_PARAMETER_NOT_USED(cdc_acm_instance);

  ULONG request;
  UX_SLAVE_TRANSFER *transfer_request;
  UX_SLAVE_DEVICE *device;

  /* Get the pointer to the device */
  device = &_ux_system_slave -> ux_system_slave_device;

  /* Get the pointer to the transfer request associated with the control endpoint */
  transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request;

  request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST);

  switch (request)
  {
    case UX_SLAVE_CLASS_CDC_ACM_SET_LINE_CODING :

      /* Get the Line Coding parameters */
      if (ux_device_class_cdc_acm_ioctl(device_cdc_acm, UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING,
                                        &CDC_VCP_LineCoding) != UX_SUCCESS)
      {
        Error_Handler();
      }

      /* Check if baudrate < 9600) then set it to 9600 */
      if (CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_baudrate < MIN_BAUDRATE)
      {
        CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_baudrate = MIN_BAUDRATE;

        /* Set the new configuration of ComPort */
        USBD_CDC_VCP_Config(&CDC_VCP_LineCoding);
      }
      else
      {
        /* Set the new configuration of ComPort */
        USBD_CDC_VCP_Config(&CDC_VCP_LineCoding);
      }

      break;

    case UX_SLAVE_CLASS_CDC_ACM_GET_LINE_CODING :

      /* Set the Line Coding parameters */
      if (ux_device_class_cdc_acm_ioctl(device_cdc_acm, UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING,
                                        &CDC_VCP_LineCoding) != UX_SUCCESS)
      {
        Error_Handler();
      }

      break;

    case UX_SLAVE_CLASS_CDC_ACM_SET_CONTROL_LINE_STATE :
    default :
      break;
  }

  /* USER CODE END USBD_CDC_ACM_ParameterChange */

  return;
}

/* USER CODE BEGIN 1 */

/**
  * @brief  Function implementing usbx_cdc_acm_thread_entry.
  * @param  thread_input: Not used
  * @retval none
  */
VOID usbx_cdc_acm_read_thread_entry(ULONG thread_input)
{
  ULONG actual_length;
  //ULONG senddataflag = 0;
  UX_SLAVE_DEVICE *device;
  ULONG actual_flags;

  UX_PARAMETER_NOT_USED(thread_input);
//  printf(" *** %s *** \n", __FUNCTION__);

  device = &_ux_system_slave->ux_system_slave_device;

  while (1)
  {
    /* Check if device is configured */
    if ((device->ux_slave_device_state == UX_DEVICE_CONFIGURED) && (device_cdc_acm != UX_NULL))
    {

#ifndef UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE

      /* Set transmission_status to UX_FALSE for the first time */
      device_cdc_acm -> ux_slave_class_cdc_acm_transmission_status = UX_FALSE;

#endif /* UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE */

//      printf(" -- %s read started\n", __FUNCTION__);
//      OutputDebug(" DEVICE <<< USBD recvstart");
      /* Read the received data in blocking mode */
      ux_device_class_cdc_acm_read(device_cdc_acm, (UCHAR *)UserRxBufferDevice, 64,
                                   &actual_length);
//      printf(" -- %s read complete %d\n", __FUNCTION__, (signed) (actual_length));
      if (actual_length > 0)
      {
        UserRxBuffOffsetDevice = 0;
        UserRxBuffSizeDevice = actual_length;
//        OutputDebugNumber(" DEVICE <<< USBD recv=", actual_length);

        /* Callback */
        VComDeviceReceiveReady();
        //printf("%s %d bytes received\n", __FUNCTION__, (int) (actual_length));
        /* Send the data via UART */
        //if (HAL_UART_Transmit_DMA(uart_handler, (uint8_t *)UserRxBufferDevice, actual_length) != HAL_OK)      @@@
        //{
        //  Error_Handler();
        //}

        /* Wait until the requested flag TX_NEW_TRANSMITTED_DATA is received */
        //if (tx_event_flags_get(&TransmitEventFlags, TX_NEW_TRANSMITTED_DATA, TX_OR_CLEAR,
        //                       &senddataflag, TX_WAIT_FOREVER) != TX_SUCCESS)
        //{
        //  Error_Handler();
        //}
        if (tx_event_flags_get(&DeviceReceiveEventFlags, 0x00000001, TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER) != TX_SUCCESS)
        {
          Error_Handler();
        }
      }
      else
      {
//        OutputDebug(" DEVICE RECV NO DATA");
        /* Sleep thread for 10ms if no data received */
        tx_thread_sleep(MS_TO_TICK(10));
      }
    }
    else
    {
      UserRxBuffOffsetDevice = 0;
      UserRxBuffSizeDevice = 0;

      /* Sleep thread for 10ms */
      tx_thread_sleep(MS_TO_TICK(10));
    }
  }
}

/**
  * @brief  Function implementing usbx_cdc_acm_write_thread_entry.
  * @param  thread_input: Not used
  * @retval none
  */
VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input)
{
  //ULONG receivedataflag = 0;
  UX_SLAVE_DEVICE *device;
  ULONG actual_flags;
  ULONG actual_length;

  UX_PARAMETER_NOT_USED(thread_input);
//  printf(" *** %s *** \n", __FUNCTION__);

  device = &_ux_system_slave->ux_system_slave_device;

  while (1)
  {
    /* Wait until the requested flag RX_NEW_RECEIVED_DATA is received */
    //if (tx_event_flags_get(&TransmitEventFlags, RX_NEW_RECEIVED_DATA, TX_OR_CLEAR,
    //                       &receivedataflag, TX_WAIT_FOREVER) != TX_SUCCESS)
    //{
    //  Error_Handler();
    //}

#ifndef UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE

    /* Set transmission_status to UX_FALSE for the first time */
    device_cdc_acm -> ux_slave_class_cdc_acm_transmission_status = UX_FALSE;

#endif

    if (tx_event_flags_get(&DeviceTransmitEventFlags, 0x00000001, TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER) != TX_SUCCESS)
    {
      Error_Handler();
    }

    while (UserTxBuffOffsetDevice < UserTxBuffSizeDevice && (device->ux_slave_device_state == UX_DEVICE_CONFIGURED) && (device_cdc_acm != UX_NULL))
    {
//      printf(" -- %s write started\n", __FUNCTION__);
      /* Send data over the class cdc_acm_write */
//      OutputDebugNumber(" DEVICE >>> USBD xmitstart=", UserTxBuffSizeDevice - UserTxBuffOffsetDevice);
      if (ux_device_class_cdc_acm_write(device_cdc_acm, (UCHAR*) (&UserTxBufferDevice[UserTxBuffOffsetDevice]),
                                        UserTxBuffSizeDevice - UserTxBuffOffsetDevice, &actual_length) == UX_SUCCESS)
      {
//        OutputDebugNumber(" DEVICE >>> USBD xmit=", actual_length);
        UserTxBuffOffsetDevice += actual_length;
      }
      else
      {
//        OutputDebug(" DEVICE XMIT ERROR");
        tx_thread_sleep(MS_TO_TICK(10));
      }
//      printf(" -- %s write complete %d\n", __FUNCTION__, (signed) (actual_length));
    }
    UserTxBuffOffsetDevice = 0;
    UserTxBuffSizeDevice = 0;
    UserTxTransmittingDevice = 0;

    /* Callback */
    VComDeviceTransmitReady();
  }
}

signed VComDeviceReceive(void* Buff, signed Size)
{
  signed Count = UserRxBuffSizeDevice - UserRxBuffOffsetDevice;

  if (Count > Size) Count = Size;
  if (Count == 0) return 0;
//  printf("%s %d\n", __FUNCTION__, Count);
//  OutputDebugNumber(" -- VComD recv=", Count);
  memcpy(Buff, &UserRxBufferDevice[UserRxBuffOffsetDevice], Count);
  UserRxBuffOffsetDevice += Count;
  if (UserRxBuffOffsetDevice == UserRxBuffSizeDevice)
  {
    if (tx_event_flags_set(&DeviceReceiveEventFlags, 0x00000001, TX_OR) != TX_SUCCESS)
    {
      Error_Handler();
    }
  }
  return Count;
}

signed VComDeviceTransmit(void* Buff, signed Size)
{
  signed Count = sizeof(UserTxBufferDevice) - UserTxBuffSizeDevice;

  if (UserTxTransmittingDevice) return 0;
  if (Count > Size) Count = Size;
  if (Count == 0) return 0;
//  printf("%s %d\n", __FUNCTION__, Count);
//  OutputDebugNumber(" -- VComD xmit=", Count);
  memcpy(&UserTxBufferDevice[UserTxBuffSizeDevice], Buff, Count);
  UserTxBuffSizeDevice += Count;
  return Count;
}

void VComDeviceFlush(void)
{
  if (UserTxTransmittingDevice) return;
  if (UserTxBuffSizeDevice == 0) return;
//  printf("%s %d\n", __FUNCTION__, (signed) (UserTxBuffSizeDevice));
  UserTxTransmittingDevice = 1;
  if (tx_event_flags_set(&DeviceTransmitEventFlags, 0x00000001, TX_OR) != TX_SUCCESS)
  {
    Error_Handler();
  }
}

/**
  * @brief  USBD_CDC_VCP_Config
            Configure the COM Port with the parameters received from host.
  * @param  UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER: linecoding struct.
  * @param  CDC_VCP_LineCoding: CDC VCP line coding.
  * @retval none
  */
static VOID USBD_CDC_VCP_Config(UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER
                                *CDC_VCP_LineCoding)
{
//  printf(" *** %s *** \n", __FUNCTION__);
  /* Deinitialization UART */
  //if (HAL_UART_DeInit(uart_handler) != HAL_OK)
  //{
  //  /* Deinitialization Error */
  //  Error_Handler();
  //}

  /* Check stop bit parameter */
  switch (CDC_VCP_LineCoding->ux_slave_class_cdc_acm_parameter_stop_bit)
  {
    case 0:

      /* Set the UART Stop bit to 1 */
      uart_config.StopBits = UART_STOPBITS_1;

      break;

    case 2:

      /* Set the UART Stop bit to 2 */
      uart_config.StopBits = UART_STOPBITS_2;

      break;

    default :

      /* By default set the UART Stop bit to 1 */
      uart_config.StopBits = UART_STOPBITS_1;

      break;
  }

  /* Check parity parameter */
  switch (CDC_VCP_LineCoding->ux_slave_class_cdc_acm_parameter_parity)
  {
    case 0:

      /* Set the UART parity bit to none */
      uart_config.Parity = UART_PARITY_NONE;

      break;

    case 1:

      /* Set the UART parity bit to ODD */
      uart_config.Parity = UART_PARITY_ODD;

      break;

    case 2:

      /* Set the UART parity bit to even */
      uart_config.Parity = UART_PARITY_EVEN;

      break;

    default :

      /* By default set the UART parity bit to none */
      uart_config.Parity = UART_PARITY_NONE;

      break;
  }

  /* Set the UART data type : only 8bits and 9bits is supported */
  switch (CDC_VCP_LineCoding->ux_slave_class_cdc_acm_parameter_data_bit)
  {
    case 0x07:

      /* With this configuration a parity (Even or Odd) must be set */
      uart_config.WordLength = UART_WORDLENGTH_8B;

      break;

    case 0x08:

      if (uart_config.Parity == UART_PARITY_NONE)
      {
        uart_config.WordLength = UART_WORDLENGTH_8B;
      }
      else
      {
        uart_config.WordLength = UART_WORDLENGTH_9B;
      }

      break;

    default :

      uart_config.WordLength = UART_WORDLENGTH_8B;

      break;
  }

  /* Get the UART baudrate from VCP */
  uart_config.BaudRate = CDC_VCP_LineCoding->ux_slave_class_cdc_acm_parameter_baudrate;

  /* Set the UART Hw flow control to none */
  uart_config.HwFlowCtl = UART_HWCONTROL_NONE;

  /* Set the UART mode */
  //uart_config.Mode = UART_MODE_TX_RX;

  /* Set the UART sampling */
  //uart_config.OverSampling = UART_OVERSAMPLING_16;

  /* Initialization UART */
  //if (HAL_UART_Init(uart_handler) != HAL_OK)
  //{
  //  /* Initialization Error */
  //  Error_Handler();
  //}

  /* Start reception: provide the buffer pointer with offset and the buffer size */
  //HAL_UART_Receive_IT(uart_handler, (uint8_t *)(UserTxBufferDevice + UserTxBufPtrIn), 1);      @@@
//  printf(" *** %s *** complete\n", __FUNCTION__);
}

/* USER CODE END 1 */
