/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    ux_host_cdc_acm.c
  * @author  MCD Application Team
  * @brief   USBX Host 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_host_cdc_acm.h"

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

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

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define APP_RX_DATA_SIZE             2048U
#define BLOCK_SIZE                   64U
/* USER CODE END PD */

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

/* USER CODE END PM */

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

extern UX_HOST_CLASS_CDC_ACM    *host_cdc_acm;
extern TX_EVENT_FLAGS_GROUP     ux_app_EventFlag;
extern TX_EVENT_FLAGS_GROUP     HostReceiveEventFlags;
extern TX_EVENT_FLAGS_GROUP     HostTransmitEventFlags;

/* UX Host CDC ACM Sending */
uint8_t                         UserTxBufferHost[APP_RX_DATA_SIZE]; // using same size as receive buffer
uint32_t                        UserTxBuffOffsetHost;
uint32_t                        UserTxBuffSizeHost;
uint8_t                         UserTxTransmittingHost;

/* UX Host CDC ACM Reception */
UX_HOST_CLASS_CDC_ACM_RECEPTION cdc_acm_reception;
ULONG                           block_reception_count;
uint8_t                         block_reception_size[APP_RX_DATA_SIZE / BLOCK_SIZE];
uint16_t                        RxSzeIdx;
uint8_t                         UserRxBufferHost[APP_RX_DATA_SIZE];
uint32_t                        UserRxBuffOffsetHost;
uint32_t                        UserRxBuffSizeHost;

#if defined ( __ICCARM__ ) /* IAR Compiler */
#pragma location =".UsbHpcdSection"
#elif defined ( __CC_ARM ) || defined(__ARMCC_VERSION) /* ARM Compiler 5/6 */
__attribute__((section(".UsbHpcdSection")))
#elif defined ( __GNUC__ ) /* GNU Compiler */
__attribute__((section(".UsbHpcdSection")))
#endif
static UCHAR UserRxBuffer[APP_RX_DATA_SIZE];

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

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

/* USER CODE END 0 */

/* USER CODE BEGIN 1 */

/**
  * @brief  Function implementing cdc_acm_send_app_thread_entry.
  * @param  thread_input: Not used
  * @retval none
  */
VOID cdc_acm_send_app_thread_entry(ULONG thread_input)
{
  UINT  status;
  ULONG actual_flags;
  ULONG tx_request_length;
  ULONG tx_actual_length;

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

  while (1)
  {
    /* Wait until the requested flag is received */
    if (tx_event_flags_get(&HostTransmitEventFlags, 0x00000001, TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER) != TX_SUCCESS)
    {
      Error_Handler();
    }

    while ((host_cdc_acm != NULL) && (host_cdc_acm -> ux_host_class_cdc_acm_state ==  UX_HOST_CLASS_INSTANCE_LIVE))
    {
      tx_request_length = UserTxBuffSizeHost - UserTxBuffOffsetHost;
//      if (tx_request_length > 64) tx_request_length = 64;               // LIMIT TO 64 BYTES WHICH DIDN'T HELP!
//      printf(" -- %s transmit started\n", __FUNCTION__);
//      OutputDebugNumber("   HOST >>> USBH xmitstart=", tx_request_length);
      /* Start sending data */
      status = _ux_host_class_cdc_acm_write(host_cdc_acm, &UserTxBufferHost[UserTxBuffOffsetHost], tx_request_length, &tx_actual_length);
      if (status == UX_SUCCESS)
      {
//        printf(" -- %s transmit complete %d\n", __FUNCTION__, (signed) (tx_actual_length));
//        OutputDebugNumber("   HOST >>> USBH xmit=", tx_actual_length);
        UserTxBuffOffsetHost += tx_actual_length;
        if (UserTxBuffOffsetHost == UserTxBuffSizeHost) break;
      }
      else
      {
//        OutputDebugNumber("   HOST XMIT ERROR=", status);
        tx_thread_sleep(MS_TO_TICK(10));
      }
    }

    UserTxBuffOffsetHost = 0;
    UserTxBuffSizeHost = 0;
    UserTxTransmittingHost = 0;

    /* Callback */
    VComHostTransmitReady();
  }
}

/**
  * @brief  Function implementing cdc_acm_recieve_app_thread_entry.
  * @param  thread_input: Not used
  * @retval none
  */
VOID cdc_acm_recieve_app_thread_entry(ULONG thread_inputg)
{
  UINT     status;
  ULONG    receive_dataflag = 0;
  UCHAR    *read_data_pointer = NULL;
  ULONG    read_block_count = 0;
  uint16_t read_data_block_count = 0;
  uint16_t count = 0;
  ULONG actual_flags;

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

  while (1)
  {
    /* Check the CDC class state */
    if ((host_cdc_acm != NULL) &&
        (host_cdc_acm -> ux_host_class_cdc_acm_state ==  UX_HOST_CLASS_INSTANCE_LIVE))
    {
      if (cdc_acm_reception.ux_host_class_cdc_acm_reception_state != UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_STARTED)
      {
        /* Get a pointer to the USB user buffer reception */
        read_data_pointer = UserRxBuffer;

        /* Set the block size parameter reception */
        cdc_acm_reception.ux_host_class_cdc_acm_reception_block_size = BLOCK_SIZE;

        /* Set the buffer for reception */
        cdc_acm_reception.ux_host_class_cdc_acm_reception_data_buffer = (UCHAR *)UserRxBuffer;

        /* Set the size of the data reception buffer */
        cdc_acm_reception.ux_host_class_cdc_acm_reception_data_buffer_size = APP_RX_DATA_SIZE;

        /* Set the callback for each reception transfer completion */
        cdc_acm_reception.ux_host_class_cdc_acm_reception_callback = cdc_acm_reception_callback;

        /* Start reception */
        status = ux_host_class_cdc_acm_reception_start(host_cdc_acm, &cdc_acm_reception);

        /* Check status to starting data reception */
        if (status == UX_SUCCESS)
        {
//          printf(" -- %s ready to receive\n", __FUNCTION__);
//          OutputDebug("   HOST USBH read to receive");
          USBH_UsrLog(" -- Ready to receive data \n");
        }
        else
        {
//          printf(" -- %s unable to start reception\n", __FUNCTION__);
          USBH_UsrLog(" -- Unable to start reception\n");
          tx_thread_sleep(MS_TO_TICK(10));
        }
      }
      else if (cdc_acm_reception.ux_host_class_cdc_acm_reception_state == UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_STARTED)
      {
        /* Wait until the requested flag NEW_RECEIVED_DATA is received */
        if (tx_event_flags_get(&ux_app_EventFlag, NEW_RECEIVED_DATA, TX_OR_CLEAR,
                               &receive_dataflag, TX_WAIT_FOREVER) != TX_SUCCESS)
        {
          Error_Handler();
        }

        while ((read_block_count < block_reception_count) && (host_cdc_acm != NULL))
        {
          /* Check if read_data_pointer reached end of user buffer */
          if (read_data_pointer >= cdc_acm_reception.ux_host_class_cdc_acm_reception_data_buffer +
              cdc_acm_reception.ux_host_class_cdc_acm_reception_data_buffer_size)
          {
            read_data_pointer = cdc_acm_reception.ux_host_class_cdc_acm_reception_data_buffer;

            /* Reinitialize block reception size index */
            read_data_block_count = 0;
          }

          if (block_reception_size[read_data_block_count] > 0)
          {
//            printf(" -- %s receive complete %d\n", __FUNCTION__, (signed) (block_reception_size[read_data_block_count]));
//            OutputDebugNumber("   HOST <<< USBH recv=", block_reception_size[read_data_block_count]);
            memcpy(UserRxBufferHost, read_data_pointer, block_reception_size[read_data_block_count]);
            UserRxBuffOffsetHost = 0;
            UserRxBuffSizeHost = block_reception_size[read_data_block_count];

            /* Callback */
            VComHostReceiveReady();

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

          /* Display the received data */
//          for (count = 0; count < block_reception_size[read_data_block_count]; count++)
//          {
//            USBH_UsrLog("%c", *read_data_pointer);
//            read_data_pointer++;
//          }

          /* Move to next block reception */
          read_block_count++;

          /* Move to the next block reception buffer */
          read_data_pointer += (BLOCK_SIZE - count);

          /* Move to the next block reception size */
          read_data_block_count++;
        }
      }
      else
      {
        tx_thread_sleep(MS_TO_TICK(10));
      }
    }
    else
    {
      tx_thread_sleep(MS_TO_TICK(10));
    }
  }
}

/**
  * @brief  cdc_acm_reception_callback.
  *         This callback is invoked to notify reception transfer completion.
  * @param  host_cdc_acm: class instance.
  * @param  status: reception status.
  * @param  reception_buffer: reception buffer pointer.
  * @param  reception_size: block size reception.
  * @retval none
  */
VOID cdc_acm_reception_callback(struct UX_HOST_CLASS_CDC_ACM_STRUCT *host_cdc_acm,
                                UINT status, UCHAR *reception_buffer, ULONG reception_size)
{
  /* Block reception count */
  block_reception_count++;

  /* Save block reception size */
  block_reception_size[RxSzeIdx] = reception_size;

  /* Move to the next block reception size */
  RxSzeIdx++;

  /* check if tail has reached end of user buffer */
  if (cdc_acm_reception.ux_host_class_cdc_acm_reception_data_tail + cdc_acm_reception.ux_host_class_cdc_acm_reception_block_size >=
      cdc_acm_reception.ux_host_class_cdc_acm_reception_data_buffer + cdc_acm_reception.ux_host_class_cdc_acm_reception_data_buffer_size)
  {
    /* Move back to the beginning  */
    cdc_acm_reception.ux_host_class_cdc_acm_reception_data_tail = cdc_acm_reception.ux_host_class_cdc_acm_reception_data_buffer;

    /* Reinitialize reception block size index */
    RxSzeIdx = 0U;
  }
  else
  {
    /* Program the tail to be after the current buffer */
    cdc_acm_reception.ux_host_class_cdc_acm_reception_data_tail += cdc_acm_reception.ux_host_class_cdc_acm_reception_block_size;
  }

  /* Set NEW_RECEIVED_DATA flag */
  if (tx_event_flags_set(&ux_app_EventFlag, NEW_RECEIVED_DATA, TX_OR) != TX_SUCCESS)
  {
    Error_Handler();
  }
}

signed VComHostReceive(void* Buff, signed Size)
{
  signed Count = UserRxBuffSizeHost - UserRxBuffOffsetHost;

  if (Count > Size) Count = Size;
  if (Count == 0) return 0;
//  printf("%s %d\n", __FUNCTION__, Count);
//  OutputDebugNumber(" -- VComH recv=", Count);
  memcpy(Buff, &UserRxBufferHost[UserRxBuffOffsetHost], Count);
  UserRxBuffOffsetHost += Count;
  if (UserRxBuffOffsetHost == UserRxBuffSizeHost)
  {
    if (tx_event_flags_set(&HostReceiveEventFlags, 0x00000001, TX_OR) != TX_SUCCESS)
    {
      Error_Handler();
    }
  }
  return Count;
}

signed VComHostTransmit(void* Buff, signed Size)
{
  signed Count = sizeof(UserTxBufferHost) - UserTxBuffSizeHost;

  if (UserTxTransmittingHost) return 0;
  if (Count > Size) Count = Size;
  if (Count == 0) return 0;
//  printf("%s %d\n", __FUNCTION__, Count);
//  OutputDebugNumber(" -- VComH xmit=", Count);
  memcpy(&UserTxBufferHost[UserTxBuffSizeHost], Buff, Count);
  UserTxBuffSizeHost += Count;
  return Count;
}

void VComHostFlush(void)
{
  if (UserTxTransmittingHost) return;
  if (UserTxBuffSizeHost == 0) return;
//  printf("%s %d\n", __FUNCTION__, (signed) (UserTxBuffSizeHost));
  UserTxTransmittingHost = 1;
  if (tx_event_flags_set(&HostTransmitEventFlags, 0x00000001, TX_OR) != TX_SUCCESS)
  {
    Error_Handler();
  }
}
/* USER CODE END 1 */
