/**
 * @file        usbh_board.c
 *
 * @brief       This file provides firmware functions to USB board
 *
 * @version     V1.0.0
 *
 * @date        2023-12-01
 *
 * @attention
 *
 *  Copyright (C) 2023 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 "usbh_board.h"

/* Private includes *******************************************************/
#include "usbh_core.h"
#include "apm32f4xx_device_cfg.h"

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

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

/* Private variables ******************************************************/
HCD_HandleTypeDef husbHost;

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

/* External variables *****************************************************/

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

/**
 * @brief   Init USB hardware
 *
 * @param   usbInfo: USB core information
 *
 * @retval  None
 */
void USBH_HardwareInit(USBH_INFO_T* usbInfo)
{
    if (usbInfo->hostSpeed == USBH_SPEED_FS)
    {
        /* Link data */
        husbHost.pData              = usbInfo;
        usbInfo->dataPoint          = &husbHost;

        husbHost.Instance           = USB_OTG_FS;
        husbHost.Init.Host_channels = 8;
        husbHost.Init.speed         = HCD_SPEED_FULL;
        husbHost.Init.dma_enable    = DISABLE;
        husbHost.Init.phy_itface    = HCD_PHY_EMBEDDED;
        husbHost.Init.Sof_enable    = DISABLE;
        if (DAL_HCD_Init(&husbHost) != DAL_OK)
        {
            Error_Handler( );
        }

        USBH_ConfigTimer(usbInfo, DAL_HCD_GetCurrentFrame(&husbHost));
    }
    else if (usbInfo->hostSpeed == USBH_SPEED_HS)
    {
        /* Link data */
        husbHost.pData                      = usbInfo;
        usbInfo->dataPoint                  = &husbHost;

        husbHost.Instance                   = USB_OTG_HS;
        husbHost.Init.Host_channels         = 12;
        husbHost.Init.speed                 = HCD_SPEED_HIGH;
        husbHost.Init.dma_enable            = DISABLE;
        husbHost.Init.phy_itface            = USB_OTG_EMBEDDED_PHY;
        husbHost.Init.Sof_enable            = DISABLE;
        husbHost.Init.low_power_enable      = DISABLE;
        husbHost.Init.vbus_sensing_enable   = DISABLE;
        husbHost.Init.use_external_vbus     = DISABLE;
        if (DAL_HCD_Init(&husbHost) != DAL_OK)
        {
            Error_Handler( );
        }

        USBH_ConfigTimer(usbInfo, DAL_HCD_GetCurrentFrame(&husbHost));
    }

    USBH_StartCallback(usbInfo);
}

/**
 * @brief  USB hardware reset
 *
 * @param  usbInfo: USB core information
 *
 * @retval None
 */
void USBH_HardwareReset(USBH_INFO_T* usbInfo)
{
    DAL_HCD_DeInit(usbInfo->dataPoint);
}

/**
 * @brief   USB host start event callback function
 *
 * @param   usbInfo: USB core information
 *
 * @retval  None
 */
void USBH_StartCallback(USBH_INFO_T* usbInfo)
{
    DAL_HCD_Start(usbInfo->dataPoint);
}

/**
 * @brief   USB stop host event callback function
 *
 * @param   usbInfo: USB core information
 *
 * @retval  None
 */
void USBH_StopHostCallback(USBH_INFO_T* usbInfo)
{
    DAL_HCD_Stop(usbInfo->dataPoint);
}

/**
 * @brief   USB host read speed event callback function
 *
 * @param   usbInfo: USB core information
 *
 * @retval  speed
 */
uint8_t USBH_ReadSpeedCallback(USBH_INFO_T* usbInfo)
{
    USBH_DEVICE_SPEED_T speed = USBH_DEVICE_SPEED_FS;

    switch (DAL_HCD_GetCurrentSpeed(usbInfo->dataPoint))
    {
        case 0 :
            speed = USBH_DEVICE_SPEED_HS;
            break;

        case 1 :
            speed = USBH_DEVICE_SPEED_FS;
            break;

        case 2 :
            speed = USBH_DEVICE_SPEED_LS;
            break;

        default:
            speed = USBH_DEVICE_SPEED_FS;
            break;
    }

    return  speed;
}

/**
 * @brief   USB host reset event callback function
 *
 * @param   usbInfo: USB core information
 *
 * @retval  None
 */
void USBH_ResetCallback(USBH_INFO_T* usbInfo)
{
    DAL_HCD_ResetPort(usbInfo->dataPoint);
}

/**
 * @brief   USB host read size of last xfer callback function
 *
 * @param   usbInfo : USB core information
 *
 * @param   channelNum: channel number
 *
 * @retval  xfer size
 */
uint32_t USBH_ReadLastXferSizeCallback(USBH_INFO_T* usbInfo, uint8_t channelNum)
{
    return (uint32_t)DAL_HCD_HC_GetXferCount(usbInfo->dataPoint, channelNum);
}

/**
 * @brief   USB host config the channel to transfer event callback function
 *
 * @param   usbInfo : USB core information
 *
 * @param   channelNum : channel numer
 *
 * @param   endPointNum : end point number
 *
 * @param   devAddr : USB device address
 *
 * @param   devSpeed : USB device speed
 *
 * @param   epType : end point type
 *
 * @param   packetMaxSize : max size of packet
 *
 * @retval  None
 */
void USBH_OpenChannelCallback(USBH_INFO_T* usbInfo, uint8_t channelNum, \
                              uint8_t endPointNum, uint8_t devAddr, \
                              uint8_t devSpeed, uint8_t epType, uint16_t packetMaxSize)
{
    DAL_HCD_HC_Init(usbInfo->dataPoint, channelNum, endPointNum, devAddr, devSpeed, epType, packetMaxSize);
}

/**
 * @brief   USB host close channel callback function
 *
 * @param   usbInfo : USB core information
 *
 * @param   channelNum: channel number
 *
 * @retval  None
 */
void USBH_CloseChannelCallback(USBH_INFO_T* usbInfo, uint8_t channelNum)
{
    DAL_HCD_HC_Halt(usbInfo->dataPoint, channelNum);
}

/**
 * @brief   USB submit URB event callback function
 *
 * @param   usbInfo : USB core information
 *
 * @param   chNum : channel number
 *
 * @param   dir : channel direction
 *
 * @param   epType : endpoint type
 *
 * @param   tokenType : tokenType
 *
 * @param   buffer : URB data
 *
 * @param   length : length of URB data
 *
 * @param   pingStatus : ping status
 *
 * @retval  None
 */
void USBH_UrbSubmitCallback(USBH_INFO_T* usbInfo, uint8_t chNum, uint8_t dir, \
                            uint8_t epType, uint8_t tokenType, uint8_t* buffer, \
                            uint16_t length, uint8_t pingStatus)
{
    DAL_HCD_HC_SubmitRequest(usbInfo->dataPoint, chNum, dir, \
                           epType, tokenType, buffer, \
                           length, pingStatus);
}

/**
 * @brief   USB host read URB status event callback function
 *
 * @param   usbInfo : USB core information
 *
 * @param   channelNum : channel number
 *
 * @retval  URB status
 */
uint8_t USBH_ReadUrbStatusCallback(USBH_INFO_T* usbInfo, uint8_t channelNum)
{
    USB_OTG_URBStateTypeDef DAL_URBStatus;
    uint8_t USBH_URBStatus;
    
    DAL_URBStatus = DAL_HCD_HC_GetURBState(usbInfo->dataPoint, channelNum);
    
    /* Change DAL URB to USBH URB */
    switch(DAL_URBStatus)
    {
        case URB_IDLE:
            USBH_URBStatus = USB_URB_IDLE;
            break;
        
        case URB_DONE:
            USBH_URBStatus = USB_URB_OK;
            break;
        
        case URB_NOTREADY:
            USBH_URBStatus = USB_URB_NOREADY;
            break;
        
        case URB_NYET:
            USBH_URBStatus = USB_URB_NYET;
            break;
        
        case URB_ERROR:
            USBH_URBStatus = USB_URB_ERROR;
            break;
        
        case URB_STALL:
            USBH_URBStatus = USB_URB_STALL;
            break;
        
        default:
            USBH_URBStatus = USB_URB_IDLE;
            break;
    }
    
    return USBH_URBStatus;
}

/**
 * @brief   USB host configure current toggle of channel callback function
 *
 * @param   usbInfo : USB core information
 *
 * @param   channelNum: channel number
 *
 * @param   toggle: toggle
 *
 * @retval  xfer size
 */
void USBH_ConfigToggleCallback(USBH_INFO_T* usbInfo, uint8_t channelNum, uint8_t toggle)
{
    DAL_HCD_ConfigToggle(usbInfo->dataPoint, channelNum, toggle);
}

/**
 * @brief   USB host read current toggle of channel callback function
 *
 * @param   usbInfo : USB core information
 *
 * @param   channelNum: channel number
 *
 * @retval  xfer size
 */
uint8_t USBH_ReadToggleCallback(USBH_INFO_T* usbInfo, uint8_t channelNum)
{
    return DAL_HCD_ReadToggle(usbInfo->dataPoint, channelNum);
}

/**
 * @brief  SOF callback
 *
 * @param  hhcd: HCD handle
 *
 * @retval None
 */
void DAL_HCD_SOF_Callback(HCD_HandleTypeDef *hhcd)
{
    USBH_IncTimer(hhcd->pData);
}

/**
 * @brief  SOF callback
 *
 * @param  hhcd: HCD handle
 *
 * @retval None
 */
void DAL_HCD_Connect_Callback(HCD_HandleTypeDef *hhcd)
{
    USBH_Connect(hhcd->pData);
}

/**
 * @brief  SOF callback
 *
 * @param  hhcd: HCD handle
 *
 * @retval None
 */
void DAL_HCD_Disconnect_Callback(HCD_HandleTypeDef *hhcd)
{
    USBH_Disconnect(hhcd->pData);
}

/**
 * @brief  Notify URB state change callback
 *
 * @param  hhcd: HCD handle
 *
 * @param  chnum: channel number
 *
 * @param  urb_state: state
 *
 * @retval None
 */
void DAL_HCD_HC_NotifyURBChange_Callback(HCD_HandleTypeDef *hhcd, uint8_t chnum, HCD_URBStateTypeDef urb_state)
{

}

/**
 * @brief  Port Port Enabled callback
 *
 * @param  hhcd: HCD handle
 *
 * @retval None
 */
void DAL_HCD_PortEnabled_Callback(HCD_HandleTypeDef *hhcd)
{
    USBH_PortEnable(hhcd->pData);
}

/**
  * @brief  Port Port Disabled callback
  *
  * @param  hhcd: HCD handle
  *
  * @retval None
  */
void DAL_HCD_PortDisabled_Callback(HCD_HandleTypeDef *hhcd)
{
    USBH_PortDisable(hhcd->pData);
}

/**
 * @brief     USB host delay callback function
 *
 * @param     tick: delay time
 * 
 * @retval    None
 */
void USBH_DelayCallback(uint32_t tick)
{
    DAL_Delay(tick);
}
