/*********************************************************************************************************//**
 * @file    IAP_HID/IAP_HID_III/Src_AP/ht32_usbd_class.c
 * @version $Rev:: 761          $
 * @date    $Date:: 2025-09-01 #$
 * @brief   The USB Device Class.
 *************************************************************************************************************
 * @attention
 *
 * Firmware Disclaimer Information
 *
 * 1. The customer hereby acknowledges and agrees that the program technical documentation, including the
 *    code, which is supplied by Holtek Semiconductor Inc., (hereinafter referred to as "HOLTEK") is the
 *    proprietary and confidential intellectual property of HOLTEK, and is protected by copyright law and
 *    other intellectual property laws.
 *
 * 2. The customer hereby acknowledges and agrees that the program technical documentation, including the
 *    code, is confidential information belonging to HOLTEK, and must not be disclosed to any third parties
 *    other than HOLTEK and the customer.
 *
 * 3. The program technical documentation, including the code, is provided "as is" and for customer reference
 *    only. After delivery by HOLTEK, the customer shall use the program technical documentation, including
 *    the code, at their own risk. HOLTEK disclaims any expressed, implied or statutory warranties, including
 *    the warranties of merchantability, satisfactory quality and fitness for a particular purpose.
 *
 * <h2><center>Copyright (C) Holtek Semiconductor Inc. All rights reserved</center></h2>
 ************************************************************************************************************/

/* Includes ------------------------------------------------------------------------------------------------*/
#include "ht32.h"
#include "ht32_board_config.h"
#include "usbd_int.h"
#include "ht32_usbd_class.h"
#include "ht32_usbd_descriptor.h"
#include "ap_handler.h"
#include "ap_config.h"
#include "common/ring_buffer.h"

/** @addtogroup HT32_Series_Peripheral_Examples HT32 Peripheral Examples
  * @{
  */

/** @addtogroup IAP_HID IAP HID
  * @{
  */

/** @addtogroup IAP_HID_III IAP HID III (with Encrypt and Command Key)
  * @{
  */

/** @addtogroup IAP_HID_III_AP AP Example
  * @{
  */


/* Private constants ---------------------------------------------------------------------------------------*/
#define HID_RPOT_TYPE_01_INPUT              (0x01)
#define HID_RPOT_TYPE_02_OUTPUT             (0x02)
#define HID_RPOT_TYPE_03_FEATURE            (0x03)

#define HID_IAP_CMDCH_INTOUT                (1)
#define HID_IAP_CMDCH_CTRLFEATURE           (2)

/* Private function prototypes -----------------------------------------------------------------------------*/
static usb_sts_type class_init_handler(void *udev);
static usb_sts_type class_clear_handler(void *udev);
static usb_sts_type class_setup_handler(void *udev, usb_setup_type *setup);
static usb_sts_type class_ept0_tx_handler(void *udev);
static usb_sts_type class_ept0_rx_handler(void *udev);
static usb_sts_type class_in_handler(void *udev, uint8_t ept_num);
static usb_sts_type class_out_handler(void *udev, uint8_t ept_num);
static usb_sts_type class_sof_handler(void *udev);
static usb_sts_type class_event_handler(void *udev, usbd_event_type event);

static void USBDClass_GetReport(void *udev, usb_setup_type *setup);
static void USBDClass_SetReport(void *udev);

static void USBDClass_GetReportIAP(void *udev, usb_setup_type *setup);
static void USBDClass_SetReportIAP(void *udev);
static void USBDClass_SetReportCallBackIAP(u32 uPara);
#if 0
static void USBDClass_EndpointIAPData(void *udev, uint8_t ept_num);
#endif
static void USBDClass_EndpointIAPCmd(void *udev, uint8_t ept_num);

/* Global variables ----------------------------------------------------------------------------------------*/
hid_info_type hid_info;

usbd_class_handler hid_iap_class_handler =
{
  class_init_handler,
  class_clear_handler,
  class_setup_handler,
  class_ept0_tx_handler,
  class_ept0_rx_handler,
  class_in_handler,
  class_out_handler,
  class_sof_handler,
  class_event_handler,
  &hid_info
};

u8 guIAPCmdChannel = HID_IAP_CMDCH_INTOUT;

u8 guIAPIsCmdReady = FALSE;
__ALIGN4 u8 guIAPCmdBuffer[64];

#if 0
__ALIGN4 u8 guIAPDataTemp[64];
__ALIGN4 u8 guIAPDataBuffer[IAP_DATA_BUFFER_SIZE];
Buffer_TypeDef gIAPData;
#endif

__ALIGN4 u8 guIAPResponseTemp[64];
__ALIGN4 u8 guIAPResponseBuffer[IAP_RESPONSE_BUFFER_SIZE];
Buffer_TypeDef gIAPResponse;

#if 0
vu32 gIsIAPDataINEmpty = TRUE;
#endif

extern vs32 gIAPCmdTimeout;

#if 0
#if defined(USE_HT32F49041_SK) || defined(USE_HT32F49163_SK)
extern otg_core_type otg_core_struct;
#endif
#if defined(USE_HT32F49395_SK)
extern usbd_core_type usb_core_dev;
#endif
#endif

extern void Suspend(void);

/* Global Function -----------------------------------------------------------------------------------------*/
/*********************************************************************************************************//**
  * @brief  initialize usb custom hid endpoint.
  * @param  udev: to the structure of usbd_core_type
  * @retval status of usb_sts_type
  ***********************************************************************************************************/
static usb_sts_type class_init_handler(void *udev)
{
  usb_sts_type status = USB_OK;
  usbd_core_type *pudev = (usbd_core_type *)udev;
  hid_info_type *phid = (hid_info_type *)pudev->class_handler->pdata;
  /* open hid iap in endpoint */
  usbd_ept_open(pudev, USBD_HIDIAP_IN_EPT, EPT_INT_TYPE, USBD_HIDIAP_IN_MAXPACKET_SIZE);

  /* open hid iap out endpoint */
  usbd_ept_open(pudev, USBD_HIDIAP_OUT_EPT, EPT_INT_TYPE, USBD_HIDIAP_OUT_MAXPACKET_SIZE);

  /* set out endpoint to receive status */
  usbd_ept_recv(pudev, USBD_HIDIAP_OUT_EPT, phid->g_rxhid_buff, USBD_HIDIAP_OUT_MAXPACKET_SIZE);

  #if 0
  Buffer_Init(&gIAPData, guIAPDataBuffer, sizeof(guIAPDataBuffer));
  #endif
  Buffer_Init(&gIAPResponse, guIAPResponseBuffer, sizeof(guIAPResponseBuffer));

  return status;
}

/*********************************************************************************************************//**
  * @brief  clear endpoint or other state.
  * @param  udev: to the structure of usbd_core_type
  * @retval status of usb_sts_type
  ***********************************************************************************************************/
static usb_sts_type class_clear_handler(void *udev)
{
  usb_sts_type status = USB_OK;
  usbd_core_type *pudev = (usbd_core_type *)udev;

  /* close hid iap  in endpoint */
  usbd_ept_close(pudev, USBD_HIDIAP_IN_EPT);

  /* close hid iap  out endpoint */
  usbd_ept_close(pudev, USBD_HIDIAP_OUT_EPT);

  return status;
}

/*********************************************************************************************************//**
  * @brief  usb device class setup request handler.
  * @param  udev: to the structure of usbd_core_type
  * @param  setup: setup packet
  * @retval status of usb_sts_type
  ***********************************************************************************************************/
static usb_sts_type class_setup_handler(void *udev, usb_setup_type *setup)
{
  usb_sts_type status = USB_OK;
  usbd_core_type *pudev = (usbd_core_type *)udev;
  hid_info_type *phid = (hid_info_type *)pudev->class_handler->pdata;
  uint16_t len;
  uint8_t *buf;

  switch(setup->bmRequestType & USB_REQ_TYPE_RESERVED)
  {
    /* class request */
    case USB_REQ_TYPE_CLASS:
      switch(setup->bRequest)
      {
        case HID_REQ_SET_PROTOCOL:
          phid->hid_protocol = (uint8_t)setup->wValue;
          break;
        case HID_REQ_GET_PROTOCOL:
          usbd_ctrl_send(pudev, (uint8_t *)&phid->hid_protocol, 1);
          break;
        case HID_REQ_SET_IDLE:
          phid->hid_set_idle = (uint8_t)(setup->wValue >> 8);
          break;
        case HID_REQ_GET_IDLE:
          usbd_ctrl_send(pudev, (uint8_t *)&phid->hid_set_idle, 1);
          break;
        case HID_REQ_SET_REPORT:
          phid->hid_state = HID_REQ_SET_REPORT;
          phid->hid_value = (uint8_t)(setup->wValue >> 8);
          phid->hid_id = (uint8_t)setup->wValue;
          phid->hid_len = setup->wLength;
          phid->hid_inf = setup->wIndex;
          usbd_ctrl_recv(pudev, phid->hid_set_report, setup->wLength);
          break;
        case HID_REQ_GET_REPORT:
          USBDClass_GetReport(pudev, setup);
          break;
        default:
          usbd_ctrl_unsupport(pudev);
          break;
      }
      break;
    /* standard request */
    case USB_REQ_TYPE_STANDARD:
      switch(setup->bRequest)
      {
        case USB_STD_REQ_GET_DESCRIPTOR:
          if(setup->wValue >> 8 == HID_REPORT_DESC)
          {
            len = MIN(USBD_HIDIAP_SIZ_REPORT_DESC, setup->wLength);
            buf = (uint8_t *)g_usbd_hidiap_report;
            usbd_ctrl_send(pudev, (uint8_t *)buf, len);
          }
          else if(setup->wValue >> 8 == HID_DESCRIPTOR_TYPE)
          {
            len = MIN(9, setup->wLength);
            buf = (uint8_t *)g_hidiap_usb_desc;
            usbd_ctrl_send(pudev, (uint8_t *)buf, len);
          }
          else
          {
            usbd_ctrl_unsupport(pudev);
          }
          break;
        case USB_STD_REQ_GET_INTERFACE:
          usbd_ctrl_send(pudev, (uint8_t *)&phid->alt_setting, 1);
          break;
        case USB_STD_REQ_SET_INTERFACE:
          phid->alt_setting = setup->wValue;
          break;
        case USB_STD_REQ_CLEAR_FEATURE:
          break;
        case USB_STD_REQ_SET_FEATURE:
          break;
        default:
          usbd_ctrl_unsupport(pudev);
          break;
      }
      break;
    default:
      usbd_ctrl_unsupport(pudev);
      break;
  }
  return status;
}

/*********************************************************************************************************//**
  * @brief  usb device endpoint 0 in status stage complete
  * @param  udev: to the structure of usbd_core_type
  * @retval status of usb_sts_type
  ***********************************************************************************************************/
static usb_sts_type class_ept0_tx_handler(void *udev)
{
  usb_sts_type status = USB_OK;

  /* ...user code... */

  return status;
}

/*********************************************************************************************************//**
  * @brief  usb device endpoint 0 out status stage complete
  * @param  udev: to the structure of usbd_core_type
  * @retval status of usb_sts_type
  ***********************************************************************************************************/
static usb_sts_type class_ept0_rx_handler(void *udev)
{
  usb_sts_type status = USB_OK;
  usbd_core_type *pudev = (usbd_core_type *)udev;
  hid_info_type *phid = (hid_info_type *)pudev->class_handler->pdata;
//  uint32_t recv_len = usbd_get_recv_len(pudev, 0);
  /* ...user code... */
  if(phid->hid_state == HID_REQ_SET_REPORT)
  {
    /* hid buffer process */
    phid->hid_state = 0;

    USBDClass_SetReport(udev);
  }

  return status;
}

/*********************************************************************************************************//**
  * @brief  usb device transmision complete handler
  * @param  udev: to the structure of usbd_core_type
  * @param  ept_num: endpoint number
  * @retval status of usb_sts_type
  ***********************************************************************************************************/
static usb_sts_type class_in_handler(void *udev, uint8_t ept_num)
{
  usb_sts_type status = USB_OK;

  #if 0
  USBDClass_EndpointIAPData(udev, ept_num);
  #endif

  return status;
}

/*********************************************************************************************************//**
  * @brief  usb device endpoint receive data
  * @param  udev: to the structure of usbd_core_type
  * @param  ept_num: endpoint number
  * @retval status of usb_sts_type
  ***********************************************************************************************************/
static usb_sts_type class_out_handler(void *udev, uint8_t ept_num)
{
  usb_sts_type status = USB_OK;
  usbd_core_type *pudev = (usbd_core_type *)udev;
  hid_info_type *phid = (hid_info_type *)pudev->class_handler->pdata;

  USBDClass_EndpointIAPCmd(udev, ept_num);

  /* start receive next packet */
  usbd_ept_recv(pudev, USBD_HIDIAP_OUT_EPT, phid->g_rxhid_buff, USBD_HIDIAP_OUT_MAXPACKET_SIZE);

  return status;
}

/*********************************************************************************************************//**
  * @brief  usb device sof handler
  * @param  udev: to the structure of usbd_core_type
  * @retval status of usb_sts_type
  ***********************************************************************************************************/
static usb_sts_type class_sof_handler(void *udev)
{
  usb_sts_type status = USB_OK;

  /* ...user code... */

  return status;
}

/*********************************************************************************************************//**
  * @brief  usb device event handler
  * @param  udev: to the structure of usbd_core_type
  * @param  event: usb device event
  * @retval status of usb_sts_type
  ***********************************************************************************************************/
static usb_sts_type class_event_handler(void *udev, usbd_event_type event)
{
  usb_sts_type status = USB_OK;
  usbd_core_type *pudev = (usbd_core_type *)udev;
  hid_info_type *phid = (hid_info_type *)pudev->class_handler->pdata;
  switch(event)
  {
    case USBD_RESET_EVENT:

      /* ...user code... */
      #if 0
      phid->hid_reset_flag = 1;
      #endif

      break;
    case USBD_SUSPEND_EVENT:

      /* ...user code... */
      phid->hid_suspend_flag = 1;

      break;
    case USBD_WAKEUP_EVENT:
      /* ...user code... */

      break;
    default:
      break;
  }
  return status;
}

/*********************************************************************************************************//**
  * @brief  usb device class send report
  * @param  udev: to the structure of usbd_core_type
  * @param  report: report buffer
  * @param  len: report length
  * @retval status of usb_sts_type
  ***********************************************************************************************************/
usb_sts_type usb_iap_class_send_report(void *udev, uint8_t *report, uint16_t len)
{
  usb_sts_type status = USB_OK;
  usbd_core_type *pudev = (usbd_core_type *)udev;

  if(usbd_connect_state_get(pudev) == USB_CONN_STATE_CONFIGURED)
    usbd_ept_send(pudev, USBD_HIDIAP_IN_EPT, report, len);

  return status;
}

/* Private functions ---------------------------------------------------------------------------------------*/
/*********************************************************************************************************//**
  * @brief  USB Class main routine.
  * @retval None
  ***********************************************************************************************************/
void USBDClass_MainRoutine(void)
{
  IAPUser_CmdProcess();

  #ifdef USB_LOW_POWER_WAKUP
  Suspend();
  #endif

  return;
}

/*********************************************************************************************************//**
  * @brief  USB User level command process.
  * @retval None
  ***********************************************************************************************************/
void IAPUser_CmdProcess(void)
{
  u32 uResponseLength;

  if (guIAPIsCmdReady == TRUE)
  {
    uResponseLength = IAP_CMDHandler(&guIAPCmdBuffer[0]);

    if (Buffer_Write(&gIAPResponse, &guIAPCmdBuffer[0], uResponseLength) != uResponseLength)
    {
      #if (AP_DEBUG_MODE == 1)
      // Buffer problem
      while(1);
      #endif
    }

    gIAPCmdTimeout = IAP_CMD_TIMEOUT_BUFFER_RELOAD;

    guIAPIsCmdReady = FALSE;
  }

  if (gIAPCmdTimeout == 0)
  {
    Buffer_Discard(&gIAPResponse);
    gIAPCmdTimeout = IAP_CMD_TIMEOUT_DISABLE;
    return;
  }
}

#if 0
/*********************************************************************************************************//**
  * @brief  IAP User level send data.
  * @param  puData: Pointer of dada buffer for write
  * @param  uLength: Write length
  * @retval Total length sent by this function
  ***********************************************************************************************************/
u32 IAPUser_SendData(u8 *puData, u32 uLength)
{
  u32 uLen = uLength;

  #if defined(USE_HT32F49041_SK) || defined(USE_HT32F49163_SK)
  usbd_core_type *pudev = &otg_core_struct.dev;
  #endif
  #if defined(USE_HT32F49395_SK)
  usbd_core_type *pudev = &usb_core_dev;
  #endif

  if (guIAPCmdChannel == HID_IAP_CMDCH_INTOUT)
  {
    while (gIsIAPDataINEmpty == FALSE){};

    gIsIAPDataINEmpty = FALSE;
    usb_iap_class_send_report(pudev, puData, uLength);
  }
  else
  {
    while ((IAP_DATA_BUFFER_SIZE - Buffer_GetLength(&gIAPData)) < uLength){};

    uLen = Buffer_Write(&gIAPData, puData, uLength);
    #if (IAP_DEBUG_MODE == 1)
    if (uLen != uLength)
    {
      // Buffer problem
      while(1);
    }
    #endif
  }

  #if 0
  if (uLen != uLength)
  {
    while (1) {}; // Data overflow
  }
  #endif

  return uLen;
}
#endif

/*********************************************************************************************************//**
  * @brief  USB Device Class Request - GET_REPORT
  * @param  udev: pointer of USB Device
  * @param  setup: setup packet
  * @retval None
  ***********************************************************************************************************/
static void USBDClass_GetReport(void *udev, usb_setup_type *setup)
{
  usbd_core_type *pudev = (usbd_core_type *)udev;

  #if 0
  u32 value = (uint8_t)(setup->wValue >> 8);
  u32 ID = (uint8_t)setup->wValue;
  u32 len = setup->wLength;
  u32 inf = setup->wIndex;
  #endif

  USBDClass_GetReportIAP(pudev, setup);

  #if 0
  switch (value)
  {
    case HID_RPOT_TYPE_01_INPUT:
    {
      if (inf == 0)
      {

      }
      break;
    }
    case HID_RPOT_TYPE_03_FEATURE:
    {
      if (inf == 0)
      {

      }
      break;
    }
  }
  #endif

  return;
}

/*********************************************************************************************************//**
  * @brief  USB Device Class Request - SET_REPORT
  * @param  udev: pointer of USB Device
  * @retval None
  ***********************************************************************************************************/
static void USBDClass_SetReport(void *udev)
{
  #if 0
  usbd_core_type *pudev = (usbd_core_type *)udev;
  hid_info_type *phid = (hid_info_type *)pudev->class_handler->pdata;
  u32 value = phid->hid_value;
  u32 ID = phid->hid_id;
  u32 len = phid->hid_len;
  u32 inf = phid->hid_inf;
  #endif

  USBDClass_SetReportIAP(udev);

  #if 0
  switch (value)
  {
    case HID_RPOT_TYPE_02_OUTPUT:
    {
      if (inf == 0)
      {

      }
      break;
    }
    case HID_RPOT_TYPE_03_FEATURE:
    {
      if (inf == 0)
      {

      }
      break;
    }
  }
  #endif

  return;
}

/*********************************************************************************************************//**
  * @brief  USB Device Class Request - GET_REPORT for IAP
  * @param  udev: pointer of USB Device
  * @param  setup: setup packet
  * @retval None
  ***********************************************************************************************************/
static void USBDClass_GetReportIAP(void *udev, usb_setup_type *setup)
{
  usbd_core_type *pudev = (usbd_core_type *)udev;
  u32 value = (uint8_t)(setup->wValue >> 8);
  u32 len = setup->wLength;
  u32 inf = setup->wIndex;

  if (inf == IAP_HID_INF)
  {
    switch (value)
    {
      case HID_RPOT_TYPE_01_INPUT:
      {
        if (Buffer_isEmpty(&gIAPResponse) == FALSE)
        {
          u32 uResponseLen;
          u32 i;
          uResponseLen = Buffer_Read(&gIAPResponse, guIAPResponseTemp, 64);
          for (i = len - 1; i >= uResponseLen; i--)
          {
            guIAPResponseTemp[i] = 0;
          }
          usbd_ctrl_send(pudev, guIAPResponseTemp, len);
        }
        else
        {
          // Data not ready, need to response STALL.
          usbd_ctrl_unsupport(pudev);
        }
        break;
      }
      case HID_RPOT_TYPE_03_FEATURE:
      {
        #if 0
        if (Buffer_GetLength(&gIAPData) >= 64)
        {
          Buffer_Read(&gIAPData, guIAPDataTemp, 64);
          usbd_ctrl_send(pudev,  guIAPDataTemp, len);
        }
        else
        {
          // Data not ready, need to response STALL.
          usbd_ctrl_unsupport(pudev);
        }
        #endif
        break;
      }
    }
  }

  return;
}

/*********************************************************************************************************//**
  * @brief  USB Device Class Request - SET_REPORT for IAP
  * @param  udev: pointer of USB Device
  * @retval None
  ***********************************************************************************************************/
static void USBDClass_SetReportIAP(void *udev)
{
  usbd_core_type *pudev = (usbd_core_type *)udev;
  hid_info_type *phid = (hid_info_type *)pudev->class_handler->pdata;
  u32 value = phid->hid_value;
  u32 len = phid->hid_len;
  u32 inf = phid->hid_inf;

  if ((inf == IAP_HID_INF) && (value == HID_RPOT_TYPE_03_FEATURE))
  {
    {
      uint8_t i = 0;

      for (i = 0; i < len; i++)
      {
        guIAPCmdBuffer[i] = phid->hid_set_report[i];
      }
      USBDClass_SetReportCallBackIAP(len);
    }
  }

  return;
}

/*********************************************************************************************************//**
  * @brief  USB Device SET_REPORT call Back function for IAP Command
  * @param  uPara: Parameter for SET_REPORT call back function (SET_REPORT wLength)
  * @retval None
  ***********************************************************************************************************/
static void USBDClass_SetReportCallBackIAP(u32 uPara)
{
  guIAPCmdChannel = HID_IAP_CMDCH_CTRLFEATURE;
  guIAPIsCmdReady = TRUE;

  return;
}

/*********************************************************************************************************//**
  * @brief  USB Class Endpoint handler for IAP Command
  * @param  udev: to the structure of usbd_core_type
  * @param  ept_num: endpoint number
  * @retval None
  ***********************************************************************************************************/
static void USBDClass_EndpointIAPCmd(void *udev, uint8_t ept_num)
{
  uint8_t i = 0;
  usbd_core_type *pudev = (usbd_core_type *)udev;
  hid_info_type *phid = (hid_info_type *)pudev->class_handler->pdata;

  uint32_t recv_len = usbd_get_recv_len(pudev, ept_num);

  for (i = 0; i < recv_len; i++)
  {
    guIAPCmdBuffer[i] = phid->g_rxhid_buff[i];
  }

  guIAPCmdChannel = HID_IAP_CMDCH_INTOUT;
  guIAPIsCmdReady = TRUE;

  return;
}

#if 0
/*********************************************************************************************************//**
  * @brief  USB Class Endpoint handler for IAP Data
  * @param  udev: to the structure of usbd_core_type
  * @param  ept_num: endpoint number
  * @retval None
  ***********************************************************************************************************/
static void USBDClass_EndpointIAPData(void *udev, uint8_t ept_num)
{
  gIsIAPDataINEmpty = TRUE;
  return;
}
#endif


/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */
