/*********************************************************************************************************//**
 * @file    IAP_HID/IAP_HID_III/Src_IAP/iap_main.c
 * @version $Rev:: 762          $
 * @date    $Date:: 2025-09-02 #$
 * @brief   The main program of HID IAP example.
 *************************************************************************************************************
 * @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.h"
#include "ht32_board_config.h"
#include "usbd_int.h"
#include "iap_ht32_usbd_class.h"
#include "iap_ht32_usbd_descriptor.h"
#include "iap_handler.h"
#include "iap_config.h"

#include "common\ht32_misc.h"

/* Dependency check ----------------------------------------------------------------------------------------*/
#if defined(__HT32F490x1_H)
#define MIN_HT32_FWLIB_VER              (0x01001002) //0xmmnnnrrr -> Vm.n.r
#define MIN_HT32_FWLIB_SVN              (0x99)
#endif
#if defined(__HT32F491x3_H)
#define MIN_HT32_FWLIB_VER              (0x01000006) //0xmmnnnrrr -> Vm.n.r
#define MIN_HT32_FWLIB_SVN              (0x154)
#endif
#if defined(__HT32F493x5_H)
#define MIN_HT32_FWLIB_VER              (0x01001009) //0xmmnnnrrr -> Vm.n.r
#define MIN_HT32_FWLIB_SVN              (0x152)
#endif
#include "ht32_dependency.h" // Not exist means the version of HT32 Firmware Library is older than the module required.

/** @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_IAP IAP Example
  * @{
  */


/* Private function prototypes -----------------------------------------------------------------------------*/
void SysTick_Configuration(void);
void USBD_Configuration(void);

#ifdef USB_LOW_POWER_WAKUP
void usb_low_power_wakeup_config(void);
#endif

void usb_clock48m_select(usb_clk48_s clk_s);
void usb_gpio_config(void);

/* Global variables ----------------------------------------------------------------------------------------*/
#if defined(USE_HT32F49041_SK) || defined(USE_HT32F49163_SK)
otg_core_type otg_core_struct;
#endif
#if defined(USE_HT32F49395_SK)
usbd_core_type usb_core_dev;
#endif

#if (IAP_TIMEOUT_EN == 1)
s32 gIAPTimeout = IAP_TIMEOUT;
#endif
#if (IAP_DOUBLE_RESET_EN == 1)
s32 gDoubleTimeout = IAP_DOUBLE_TIMEOUT;
#endif
vs32 gIAPCmdTimeout = IAP_CMD_TIMEOUT_DISABLE;

/* Global functions ----------------------------------------------------------------------------------------*/
/*********************************************************************************************************//**
  * @brief  Main program.
  * @retval None
  ***********************************************************************************************************/
int main(void)
{
  system_clock_config();

  ht32_board_init();

  #if (IAP_DEBUG_MODE == 1)
  uart_print_init(115200);
  #endif

  IAPUser_BootProcess();

  SysTick_Configuration();

  IAP_Init();

  #if (EXT_FLASH_ENABLE == 1)
  #if 0
  /* !!! NOTICE !!!
     Notice that External Flash is not yet supported.
  */
  if (SPI_FLASH_Init() == TRUE)
  {
    SPI_FLASH_WriteStatus(0x00);
  }
  #endif
  #endif

  #if (USER_CMD_DEMO == 1)
  ht32_led_init(LED2);
  ht32_led_init(LED3);
  ht32_led_init(LED4);
  ht32_led_off(LED2);
  ht32_led_off(LED3);
  ht32_led_off(LED4);
  #endif

  USBD_Configuration();               /* USB device configuration                                           */

  while (1)
  {
    USBDClass_MainRoutine();
  }
}

/*********************************************************************************************************//**
  * @brief  IAP User level boot process.
  * @retval None
  ***********************************************************************************************************/
void IAPUser_BootProcess(void)
{
  #if 0
  /* !!! NOTICE !!!
        Modify it if you want to use another GPIO pin.
  */
  /*--------------------------------------------------------------------------------------------------------*/
  /* Example that using GPIO to force the IAP mode.                                                         */
  /* GPIO = 0: Normal boot flow, GPIO = 1: IAP mode                                                         */
  /*--------------------------------------------------------------------------------------------------------*/
  {
    gpio_init_type gpio_init_struct;
    crm_periph_clock_enable(USER_BUTTON_CRM_CLK, TRUE);
    gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
    gpio_init_struct.gpio_pins = USER_BUTTON_PIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_DOWN;
    gpio_init(USER_BUTTON_PORT, &gpio_init_struct);
  }
  if ( gpio_input_data_bit_read(USER_BUTTON_PORT, USER_BUTTON_PIN) == RESET)
  #endif
  {
    #if (IAP_TIMEOUT_EN == 1)
    if (BOOT_MODE == BOOT_MODE_AP)
    {
      /*----------------------------------------------------------------------------------------------------*/
      /* Start user application when                                                                        */
      /*   1. IO = 1 and                                                                                    */
      /*   2. BOOT_MODE == BOOT_MODE_AP (Default IAP) and                                                   */
      /*   3. SP and PC of user's application is in range                                                   */
      /*----------------------------------------------------------------------------------------------------*/
      IAP_JumpToAP(IAP_APFLASH_START);
    }
    #elif (IAP_DOUBLE_RESET_EN == 1)
    if (CRM->ctrlsts_bit.nrstf == TRUE && CRM->ctrlsts_bit.swrstf == FALSE)
    {
      /* External IO reset.                                                                                 */
      if (HARDWAVE_RESET == HARDWAVE_RESET_FIRST)
      {
        /* The second external IO reset.                                                                    */
        HARDWAVE_RESET = 0;
      }
      else
      {
        /* The first external IO reset, stay in IAP mode.                                                   */
        HARDWAVE_RESET = HARDWAVE_RESET_FIRST;
      }
      crm_flag_clear(CRM_ALL_RESET_FLAG);
    }
    else
    {
      if (BOOT_MODE != BOOT_MODE_IAP)
      {
        crm_flag_clear(CRM_SW_RESET_FLAG);
        /*--------------------------------------------------------------------------------------------------*/
        /* Start user application when                                                                      */
        /*   1. BOOT_MODE != BOOT_MODE_IAP (Default AP) and                                                 */
        /*   2. SP and PC of user's application is in range                                                 */
        /*--------------------------------------------------------------------------------------------------*/
        IAP_JumpToAP(IAP_APFLASH_START);
      }
    }
    #else
    if (BOOT_MODE != BOOT_MODE_IAP)
    {
      /*----------------------------------------------------------------------------------------------------*/
      /* Start user application when                                                                        */
      /*   1. IO = 1 and                                                                                    */
      /*   2. BOOT_MODE != BOOT_MODE_IAP (Default AP) and                                                   */
      /*   3. SP and PC of user's application is in range                                                   */
      /*----------------------------------------------------------------------------------------------------*/
      IAP_JumpToAP(IAP_APFLASH_START);
    }
    #endif
  }

  /*--------------------------------------------------------------------------------------------------------*/
  /* Start IAP mode                                                                                         */
  /*   1. IO = 0 or                                                                                         */
  /*   2. BOOT_MODE = BOOT_MODE_IAP                                                                         */
  /*--------------------------------------------------------------------------------------------------------*/

  BOOT_MODE = 0;
}

/*********************************************************************************************************//**
  * @brief  IAP User level reset.
  * @retval None
  ***********************************************************************************************************/
void IAPUser_Reset(void)
{
  delay_ms(5); // Must be > 1 ms
  /*--------------------------------------------------------------------------------------------------------*/
  /* Disconnect USB and wait for PC aware it                                                                */
  /*--------------------------------------------------------------------------------------------------------*/
  #if defined(USE_HT32F49041_SK) || defined(USE_HT32F49163_SK)
  NVIC_DisableIRQ(OTG_IRQ);
  usbd_disconnect(&otg_core_struct.dev);
  #elif defined(USE_HT32F49395_SK)
  NVIC_DisableIRQ(USBFS_L_CAN1_RX0_IRQn);
  usbd_disconnect(&usb_core_dev);
  #else
  #ERROR "Not Support."
  #endif
  delay_ms(1000);
  NVIC_SystemReset();
}

/*********************************************************************************************************//**
  * @brief  SysTick Configuration.
  * @retval None
  ***********************************************************************************************************/
void SysTick_Configuration(void)
{
  #define TIMER_BASE                    (SystemCoreClock * 1 / 1000)  /* 1 ms                               */

  SYSTICK_ClockSourceConfig(SYSTICK_SRC_FCLK);
  SYSTICK_SetReloadValue(TIMER_BASE);
  SYSTICK_IntConfig(TRUE);
  SYSTICK_CounterCmd(SYSTICK_COUNTER_CLEAR);
  SYSTICK_CounterCmd(SYSTICK_COUNTER_ENABLE);
}

/*********************************************************************************************************//**
  * @brief  Configure the USB.
  * @retval None
  ***********************************************************************************************************/
void USBD_Configuration(void)
{
  /* select usb 48m clcok source */
  usb_clock48m_select(USB_CLK_HEXT);

  /* enable pwc and bpr clock */
  crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);

  #ifdef USB_LOW_POWER_WAKUP
  usb_low_power_wakeup_config();
  #endif

  #if defined(USE_HT32F49041_SK) || defined(USE_HT32F49163_SK)
  /* usb gpio config */
  usb_gpio_config();

  /* enable otgfs clock */
  crm_periph_clock_enable(OTG_CLOCK, TRUE);

  /* enable otgfs irq */
  nvic_irq_enable(OTG_IRQ, 0, 0);

  /* init usb */
  usbd_init(&otg_core_struct,
            USB_FULL_SPEED_CORE_ID,
            USB_ID,
            &hid_iap_class_handler,
            &hid_iap_desc_handler);
  #elif defined(USE_HT32F49395_SK)
  /* enable usb clock */
  crm_periph_clock_enable(CRM_USB_PERIPH_CLOCK, TRUE);

  /* enable usb interrupt */
  nvic_irq_enable(USBFS_L_CAN1_RX0_IRQn, 0, 0);

  /* usb core init */
  usbd_core_init(&usb_core_dev, USB, &hid_iap_class_handler, &hid_iap_desc_handler, 0);

  /* enable usb pull-up */
  usbd_connect(&usb_core_dev);
  #else
  #ERROR "Not Support."
  #endif
}

/*********************************************************************************************************//**
  * @brief  usb 48M clock select.
  * @param  clk_s:USB_CLK_HICK, USB_CLK_HEXT
  * @retval None
  ***********************************************************************************************************/
void usb_clock48m_select(usb_clk48_s clk_s)
{
  if(clk_s == USB_CLK_HICK)
  {
    crm_usb_clock_source_select(CRM_USB_CLOCK_SOURCE_HICK);

    /* enable the acc calibration ready interrupt */
    crm_periph_clock_enable(CRM_ACC_PERIPH_CLOCK, TRUE);

    /* update the c1\c2\c3 value */
    acc_write_c1(7980);
    acc_write_c2(8000);
    acc_write_c3(8020);

    /* open acc calibration */
    acc_calibration_mode_enable(ACC_CAL_HICKTRIM, TRUE);
  }
  else
  {
    #if defined(USE_HT32F49041_SK)
    /* usb divider reset */
    crm_usb_div_reset();

    switch(system_core_clock)
    {
      /* 48MHz */
      case 48000000:
        crm_usb_clock_div_set(CRM_USB_DIV_1);
        break;

      /* 72MHz */
      case 72000000:
        crm_usb_clock_div_set(CRM_USB_DIV_1_5);
        break;

      /* 96MHz */
      case 96000000:
        crm_usb_clock_div_set(CRM_USB_DIV_2);
        break;

      default:
        break;
    }
    #elif defined(USE_HT32F49163_SK)
    switch(system_core_clock)
    {
      /* 48MHz */
      case 48000000:
        crm_usb_clock_div_set(CRM_USB_DIV_2);
        break;

      /* 72MHz */
      case 72000000:
        crm_usb_clock_div_set(CRM_USB_DIV_3);
        break;

      /* 96MHz */
      case 96000000:
        crm_usb_clock_div_set(CRM_USB_DIV_4);
        break;

      /* 120MHz */
      case 120000000:
        crm_usb_clock_div_set(CRM_USB_DIV_5);
        break;

      /* 144MHz */
      case 144000000:
        crm_usb_clock_div_set(CRM_USB_DIV_6);
        break;

      default:
        break;
    }
    #elif defined(USE_HT32F49395_SK)
    switch(system_core_clock)
    {
      /* 48MHz */
      case 48000000:
        crm_usb_clock_div_set(CRM_USB_DIV_1);
        break;

      /* 72MHz */
      case 72000000:
        crm_usb_clock_div_set(CRM_USB_DIV_1_5);
        break;

      /* 96MHz */
      case 96000000:
        crm_usb_clock_div_set(CRM_USB_DIV_2);
        break;

      /* 120MHz */
      case 120000000:
        crm_usb_clock_div_set(CRM_USB_DIV_2_5);
        break;

      /* 144MHz */
      case 144000000:
        crm_usb_clock_div_set(CRM_USB_DIV_3);
        break;

      /* 168MHz */
      case 168000000:
        crm_usb_clock_div_set(CRM_USB_DIV_3_5);
        break;

      /* 192MHz */
      case 192000000:
        crm_usb_clock_div_set(CRM_USB_DIV_4);
        break;

      default:
        break;
    }
    #else
    #ERROR "Not Support."
    #endif
  }
}

#if defined(USE_HT32F49041_SK) || defined(USE_HT32F49163_SK)
/*********************************************************************************************************//**
  * @brief  this function config gpio.
  * @retval None
  ***********************************************************************************************************/
void usb_gpio_config(void)
{
  gpio_init_type gpio_init_struct;

  crm_periph_clock_enable(OTG_PIN_GPIO_CLOCK, TRUE);
  gpio_default_para_init(&gpio_init_struct);

  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;

#ifdef USB_SOF_OUTPUT_ENABLE
  crm_periph_clock_enable(OTG_PIN_SOF_GPIO_CLOCK, TRUE);
  gpio_init_struct.gpio_pins = OTG_PIN_SOF;
  gpio_init(OTG_PIN_SOF_GPIO, &gpio_init_struct);
  gpio_pin_mux_config(OTG_PIN_GPIO, OTG_PIN_SOF_SOURCE, OTG_PIN_MUX);
#endif

  /* otgfs use vbus pin */
#ifndef USB_VBUS_IGNORE
  gpio_init_struct.gpio_pins = OTG_PIN_VBUS;
  gpio_init_struct.gpio_pull = GPIO_PULL_UP;
  gpio_pin_mux_config(OTG_PIN_GPIO, OTG_PIN_VBUS_SOURCE, OTG_PIN_MUX);
  gpio_init(OTG_PIN_GPIO, &gpio_init_struct);
#endif
}
#endif

#ifdef USB_LOW_POWER_WAKUP
/*********************************************************************************************************//**
  * @brief  usb low power wakeup interrupt config.
  * @retval None
  ***********************************************************************************************************/
void usb_low_power_wakeup_config(void)
{
  exint_init_type exint_init_struct;

  #if defined(USE_HT32F49041_SK) || defined(USE_HT32F49163_SK)
  crm_periph_clock_enable(CRM_SCFG_PERIPH_CLOCK, TRUE);
  #endif
  exint_default_para_init(&exint_init_struct);

  exint_init_struct.line_enable = TRUE;
  exint_init_struct.line_mode = EXINT_LINE_INTERRUPT;
  exint_init_struct.line_select = EXINT_LINE_18;
  exint_init_struct.line_polarity = EXINT_TRIGGER_RISING_EDGE;
  exint_init(&exint_init_struct);

  #if defined(USE_HT32F49163_SK)
  nvic_irq_enable(OTG_WKUP_IRQ, 0, 0);
  #endif

  #if defined(USE_HT32F49395_SK)
  nvic_irq_enable(USBFSWakeUp_IRQn, 0, 0);
  #endif
}
#endif

/*********************************************************************************************************//**
  * @brief  usb delay millisecond function.
  * @param  ms: number of millisecond delay
  * @retval None
  ***********************************************************************************************************/
void usb_delay_ms(uint32_t ms)
{
  /* user can define self delay function */
  delay_ms(ms);
}

/*********************************************************************************************************//**
  * @brief  usb delay microsecond function.
  * @param  us: number of microsecond delay
  * @retval None
  ***********************************************************************************************************/
void usb_delay_us(uint32_t us)
{
  /* user can define self delay function */
  delay_us(us);
}


/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */
