/**
 * @file        main.c
 *
 * @brief       Main program body
 *
 * @version     V1.0.0
 *
 * @date        2024-08-01
 *
 * @attention
 *
 *  Copyright (C) 2024-2025 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

/* Includes ***************************************************************/
#include "main.h"

/* Private includes *******************************************************/
#include "apm32f4xx_device_cfg.h"
#include "usb_iap.h"
#include "bsp_flash.h"
#include "usbh_msc.h"

/* Private macro **********************************************************/

/* Private typedef ********************************************************/

/* Private variables ******************************************************/
const char* userAppErrorTable[] =
{
    "USER_APP_OK",
    "USER_APP_ERR_FATFS",
    "USER_APP_ERR_DISK_WP",
    "USER_APP_ERR_FSIZE_OVER",
    "USER_APP_ERR_FMC_ERASE",
    "USER_APP_ERR_FMC_PG",
    "USER_APP_ERR_FMC_WP",
    "USER_APP_ERR_DISK_DISCONNECTED",
};

static uint8_t userAppState = USER_APP_JUMP;

/* Private function prototypes ********************************************/

/* External variables *****************************************************/
extern USB_HOST_INFO_T hostInfo;
extern USBH_INFO_T gUsbHostFS;

/* External functions *****************************************************/

/**
 * @brief   Main program
 *
 * @param   None
 *
 * @retval  None
 */
int main(void)
{
    uint32_t timeout = 0;

    /* Device configuration */
    DAL_DeviceConfig();

    /* Flash unlock */
    FLASH_FlashInit();

    printf(">> USB FW Upgrade Bootloader\r\n");
    printf(">> Waitting for the U disk to be inserted for firmware upgrade\r\n");
    printf(">> Please press KEY1 to enter firmware upgrade mode\r\n");

    while (1)
    {
        if (!BOARD_BUTTON_GetState(BUTTON_KEY1))
        {
            DAL_Delay(10);
            if (!BOARD_BUTTON_GetState(BUTTON_KEY1))
            {
                userAppState = USER_APP_INIT;

                while (!BOARD_BUTTON_GetState(BUTTON_KEY1));
                printf("\r\nEnter firmware upgrade mode\r\n");
                break;
            }
        }
        DAL_Delay(10);
        timeout++;
        if ((timeout % 10)  == 0)
        {
            printf(">");
        }

        if (timeout >= 200)
        {
            printf("\r\n");
            timeout = 0;
            USB_IAP_Jump2App();
            break;
        }
    }

    /* Infinite loop */
    while (1)
    {
        USB_HostProcess();
        USB_HostUserApplication();
    }
}

/**
 * @brief   User application
 *
 * @param   None
 *
 * @retval  None
 */
void USB_HostUserApplication(void)
{
    uint32_t timeout;

    switch (hostInfo.state)
    {
        case USBH_APP_CONNECTION:
            /* To avoid the enumeration error */
            if (((gUsbHostFS.xferState == USBH_XFER_WAITING) && \
                ((gUsbHostFS.hostEnumState == USBH_ENUM_IDLE) || \
                 (gUsbHostFS.hostEnumState == USBH_ENUM_GET_DEV_DESC))) ||
                 ((gUsbHostFS.hostState == USBH_HOST_ABORT) && (gUsbHostFS.devInfo.rstCnt > USBH_DEVICE_RESET_MAX_CNT)))
                 {
                     timeout = DAL_GetTick();

                    if (timeout - hostInfo.timer > 1000)
                    {
                        USBH_USR_DBG("USB host enumeration timeout");
                        hostInfo.state = USBH_APP_ERROR;
                    }
                 }
            break;

        case USBH_APP_DISCONNECTION:
            break;

        case USBH_APP_READY:
            if (gUsbHostFS.devInfo.connectedStatus == ENABLE)
            {
                switch (userAppState)
                {
                    case USER_APP_INIT:
                        printf(">> USER_APP_FS_INIT\r\n");

                        /* Initialises the File System*/
                        if (f_mount(&fatfs, IAP_DISK_PATH, 0) != FR_OK)
                        {
                            USER_ErrorHandler(USER_APP_ERR_FATFS);
                        }

                        /* Flash Disk is write protected */
                        if (USBH_MSC_ReadDevWP(&gUsbHostFS, 0) == ENABLE)
                        {
                            USER_ErrorHandler(USER_APP_ERR_DISK_WP);
                        }

                        /* Go to IAP menu */
                        userAppState = USER_APP_RUN;
                        break;

                    case USER_APP_RUN:
                        printf(">> USER_APP_IAP\r\n");
                        printf(">> Please do not unplug the U disk\r\n");
                        /* Writes Flash memory */
                        USB_IAP_Download();

                        /* Reads all flash memory */
                        USB_IAP_Upload();

                        /* Go to JUMP menu */
                        userAppState = USER_APP_JUMP;
                        break;

                    case USER_APP_JUMP:
                        printf(">> USER_APP_JUMP\r\n");
                        /* Jumps to user application code located in the internal Flash memory */
                        USB_IAP_Jump2App();
                        break;
                }
            }
            break;

        case USBH_APP_ERROR:
            USBH_USR_ERR("USB host error, retry to enumerate the device");
            hostInfo.state = USBH_APP_IDLE;

            USB_HostReset();

            USB_HostInitalize();
            break;

        default:
            break;
    }
}

/*!
 * @brief       USB error handler
 *
 * @param       errCode: error code
 *
 * @retval      None
 */
__weak void USER_ErrorHandler(uint8_t errCode)
{
    while (1)
    {
        printf("%s\r\n", userAppErrorTable[errCode]);
        BOARD_LED_Toggle(LED2);
        DAL_Delay(500);
    }
}
