/*********************************************************************************************************//**
 * @file    IAP_HID/IAP_HID_III/Src_AP/main.c
 * @version $Rev:: 761          $
 * @date    $Date:: 2025-09-01 #$
 * @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 "ht32_usbd_class.h"
#include "ht32_usbd_descriptor.h"
#include "ap_handler.h"
#include "ap_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_AP AP Example
  * @{
  */


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

void USBD_Configuration(void);
#ifdef USB_LOW_POWER_WAKUP
void Suspend(void);
void system_clock_recover(void);
void usb_low_power_wakeup_config(void);
#endif

void ShowTestMenu(void);
void FunctionHandler(u32 uCommand);

void IAPUser_ShowVersion(void);

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

vs32 gIAPCmdTimeout = IAP_CMD_TIMEOUT_DISABLE;

/* Global functions ----------------------------------------------------------------------------------------*/
/*********************************************************************************************************//**
  * @brief  Main program.
  * @retval None
  ***********************************************************************************************************/
int main(void)
{
  nvic_vector_table_set(NVIC_VECTTAB_FLASH, IAP_APFLASH_START);     /* Set the Vector Table Offset          */

  system_clock_config();

  ht32_board_init();

  UxART_Configuration();

  SysTick_Configuration();

  USBD_Configuration();

  AP_Init();

  IAPUser_ShowVersion();
  ShowTestMenu();

  while (1)
  {
    USBDClass_MainRoutine();

    if (usart_flag_get(PRINT_UART, USART_RDBF_FLAG) == SET)
    {
      u32 input;
      input = usart_data_receive(PRINT_UART);
      printf("\r\n\r\n");
      FunctionHandler(input);
      ShowTestMenu();
    }
  }
}

/*********************************************************************************************************//**
  * @brief  IAP User level reset.
  * @retval None
  ***********************************************************************************************************/
void IAPUser_Reset(void)
{
  /*--------------------------------------------------------------------------------------------------------*/
  /* 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(100);
  NVIC_SystemReset();
}

/*********************************************************************************************************//**
  * @brief  Show Test menu.
  * @retval None
  ***********************************************************************************************************/
void ShowTestMenu(void)
{
  printf("\r\n\r\n");
  printf(" Application\r\n");
  printf("--------------------------------\r\n");
  printf(" [1] Start IAP mode\r\n");
  printf(" [2] Hello World\r\n");
  printf(" [3] Enable Write Protection\r\n");
}

/*********************************************************************************************************//**
  * @brief  Function Handler.
  * @param  uCommand
  * @retval None
  ***********************************************************************************************************/
void FunctionHandler(u32 uCommand)
{
  unsigned int i;

  switch (uCommand)
  {
    case '1':
    {
      /*----------------------------------------------------------------------------------------------------*/
      /* Set BOOT_MODE as BOOT_MODE_IAP and trigger a reset to start IAP mode.                              */
      /*----------------------------------------------------------------------------------------------------*/
      BOOT_MODE = BOOT_MODE_IAP;
      IAPUser_Reset();
      break;
    }
    case '2':
    {
      printf("\r\n");
      for (i = 0; i < 10; i++)
      {
        printf("Hello World %02d!\r\n", i);
      }
      break;
    }
    case '3':
    {
      if (flash_fap_status_get() == SET)
      {
        printf("Flash Access Protection is enabled. Can not modify user system data.\r\n");
      }
      else
      {
        flash_unlock();

        if (flash_user_system_data_erase() == FLASH_OPERATE_DONE)
        {
          u32 epp = 0xFFFFFFFF;
          if (flash_epp_set(&epp) == FLASH_OPERATE_DONE)
          {
            printf("Write Protection enabled. Trigger reset to reload user system data.\r\n\n");
            NVIC_SystemReset();
          }
          else
          {
            printf("Write Protection enable operation fail.\r\n\n");
          }
        }
        else
        {
          printf("Flash USD erase fail.\r\n\n");
        }
      }

      break;
    }
  }
}

/*********************************************************************************************************//**
  * @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
  }
}

/*********************************************************************************************************//**
  * @brief  Show version.
  * @retval None
  ***********************************************************************************************************/
void IAPUser_ShowVersion(void)
{
  u32 i, len;
  u32 veraddr = IAP_VERFLASH_START;
  u8 ver[12];

  len = rw(veraddr);
  if (len == 0xFFFFFFFF)
  {
    printf("Version invalid!\r\n");
    return;
  }
  len &= 0x0000FFFF;
  if (len > 12)
  {
    printf("Version length invalid!\r\n");
    return;
  }

  printf("Version = ");

  veraddr += 0x4;
  for (i = 0; i < len; i += 4)
  {
    *(u32 *)(&ver[i]) = rw(veraddr + i);
  }

  ver[len] = 0;

  printf("%s\r\n", ver);
}

#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
}

/*********************************************************************************************************//**
  * @brief  Suspend function which enter Low Power
  * @param  uPara: Parameter for Call back function
  * @retval None
  ***********************************************************************************************************/
void Suspend(void)
{
  __IO uint32_t delay_index = 0;
  __IO uint32_t systick_index = 0;

  #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

  hid_info_type *phid = (hid_info_type *)pudev->class_handler->pdata;

  if (phid->hid_suspend_flag == 1)
  {
    /* save systick register configuration */
    systick_index = SysTick->CTRL;
    systick_index &= ~((uint32_t)0xFFFFFFFE);

    /* disable systick */
    SysTick->CTRL &= (uint32_t)0xFFFFFFFE;

    /* congfig the voltage regulator mode */
    pwc_voltage_regulate_set(PWC_REGULATOR_LOW_POWER);

    /* enter deep sleep mode */
    pwc_deep_sleep_mode_enter(PWC_DEEP_SLEEP_ENTER_WFI);

    /* wait clock stable */
    for(delay_index = 0; delay_index < 600; delay_index++)
    {
      __NOP();
    }

    /* restore systick register configuration */
    SysTick->CTRL |= systick_index;

    system_clock_recover();

    phid->hid_suspend_flag = 0;
  }
}

/*********************************************************************************************************//**
  * @brief  system clock recover
  * @retval None
  ***********************************************************************************************************/
void system_clock_recover(void)
{
  /* enable external high-speed crystal oscillator - hext */
  crm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE);

  /* wait till hext is ready */
  while(crm_hext_stable_wait() == ERROR);

  /* enable pll */
  crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE);

  /* wait till pll is ready */
  while(crm_flag_get(CRM_PLL_STABLE_FLAG) == RESET);

  #if defined(USE_HT32F49041_SK)
  /* usb divider reset */
  crm_usb_div_reset();
  #endif

  #if defined(USE_HT32F49163_SK) || defined(USE_HT32F49395_SK)
  /* enable auto step mode */
  crm_auto_step_mode_enable(TRUE);
  #endif

  /* select pll as system clock source */
  crm_sysclk_switch(CRM_SCLK_PLL);

  /* wait till pll is used as system clock source */
  while(crm_sysclk_switch_status_get() != CRM_SCLK_PLL);
}
#endif

/*********************************************************************************************************//**
  * @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  UxART configuration.
  * @retval None
  ***********************************************************************************************************/
void UxART_Configuration(void)
{
  uart_print_init(115200);

  #if defined(USE_HT32F49041_SK) || defined(USE_HT32F49163_SK)
  // initialize UxART RX MUX config.
  {
    gpio_init_type 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_pins = HTCFG_RX_PIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init(HTCFG_RX_GPIO, &gpio_init_struct);

    gpio_pin_mux_config(HTCFG_RX_GPIO, HTCFG_RX_PIN_SOURCE, HTCFG_RX_PIN_MUX_NUM);
  }
  #endif

  usart_receiver_enable(PRINT_UART, TRUE);
}

/*********************************************************************************************************//**
  * @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);
}


/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */
