/**
 ******************************************************************************
 * @file    usbd_core.c
 * @author  MCD Application Team
 * @version V2.0.0
 * @date    18-February-2014
 * @brief   This file provides all the USBD core functions.
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT 2014 STMicroelectronics</center></h2>
 *
 * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *        http://www.st.com/software_license_agreement_liberty_v2
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "semphr.h"

#include "stm32f4xx.h"
#include "usbd_core.h"

#include "uart/debug.h"
#include "uart/uart.h"

/** @addtogroup STM32_USBD_DEVICE_LIBRARY
 * @{
 */


/** @defgroup USBD_CORE
 * @brief usbd core module
 * @{
 */

/** @defgroup USBD_CORE_Private_TypesDefinitions
 * @{
 */
/**
 * @}
 */


/** @defgroup USBD_CORE_Private_Defines
 * @{
 */

/**
 * @}
 */


/** @defgroup USBD_CORE_Private_Macros
 * @{
 */
/**
 * @}
 */




/** @defgroup USBD_CORE_Private_FunctionPrototypes
 * @{
 */

/**
 * @}
 */

/** @defgroup USBD_CORE_Private_Variables
 * @{
 */

/**
 * @}
 */

/** @defgroup USBD_CORE_Private_Functions
 * @{
 */

/**
 * @brief  USBD_Init
 *         Initailizes the device stack and load the class driver
 * @param  pdev: device instance
 * @param  core_address: USB OTG core ID
 * @param  pdesc: Descriptor structure address
 * @param  id: Low level core index
 * @retval None
 */
USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id)
{
    /* Check whether the USB Host handle is valid */
    if(pdev == NULL)
    {
        USBD_ErrLog("Invalid Device handle");
        return USBD_FAIL;
    }

    /* Unlink previous class*/
    if(pdev->pClass != NULL)
    {
        pdev->pClass = NULL;
    }

    /* Assign USBD Descriptors */
    if(pdesc != NULL)
    {
        pdev->pDesc = pdesc;
    }

    /* Set Device initial State */
    pdev->dev_state  = USBD_STATE_DEFAULT;
    pdev->id = id;

    /* Initialize low level driver */
    USBD_LL_Init(pdev);

    return USBD_OK;
}

/**
 * @brief  USBD_DeInit
 *         Re-Initialize th device library
 * @param  pdev: device instance
 * @retval status: status
 */
USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev)
{
    /* Set Default State */
    pdev->dev_state  = USBD_STATE_DEFAULT;

    /* Free Class Resources */
    pdev->pClass->DeInit(pdev, pdev->dev_config);

    /* Stop the low level driver  */
    USBD_LL_Stop(pdev);

    /* Initialize low level driver */
    USBD_LL_DeInit(pdev);
    NVIC_DisableIRQ(OTG_FS_IRQn);
    return USBD_OK;
}


/**
 * @brief  USBD_RegisterClass
 *         Link class driver to Device Core.
 * @param  pDevice : Device Handle
 * @param  pclass: Class handle
 * @retval USBD Status
 */
USBD_StatusTypeDef  USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass)
{
    USBD_StatusTypeDef   status = USBD_OK;
    if(pclass != 0)
    {
        /* link the class tgo the USB Device handle */
        pdev->pClass = pclass;
        status = USBD_OK;
    }
    else
    {
        debug_printStr("!USBD_RegisterClass\r\n");
        status = USBD_FAIL;
    }

    return status;
}

/**
 * @brief  USBD_Start
 *         Start the USB Device Core.
 * @param  pdev: Device Handle
 * @retval USBD Status
 */
USBD_StatusTypeDef  USBD_Start  (USBD_HandleTypeDef *pdev)
{

    /* Start the low level driver  */
    USBD_LL_Start(pdev);

    return USBD_OK;
}

/**
 * @brief  USBD_Stop
 *         Stop the USB Device Core.
 * @param  pdev: Device Handle
 * @retval USBD Status
 */
USBD_StatusTypeDef  USBD_Stop   (USBD_HandleTypeDef *pdev)
{
    /* Free Class Resources */
    pdev->pClass->DeInit(pdev, pdev->dev_config);

    /* Stop the low level driver  */
    USBD_LL_Stop(pdev);

    return USBD_OK;
}

/**
 * @brief  USBD_RunTestMode
 *         Launch test mode process
 * @param  pdev: device instance
 * @retval status
 */
USBD_StatusTypeDef  USBD_RunTestMode (USBD_HandleTypeDef  *pdev)
{
    return USBD_OK;
}


/**
 * @brief  USBD_SetClassConfig
 *        Configure device and start the interface
 * @param  pdev: device instance
 * @param  cfgidx: configuration index
 * @retval status
 */

USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef  *pdev, uint8_t cfgidx)
{
    USBD_StatusTypeDef   ret = USBD_FAIL;

    if(pdev->pClass != NULL)
    {
        /* Set configuration  and Start the Class*/
        if(pdev->pClass->Init(pdev, cfgidx) == 0)
        {
            ret = USBD_OK;
        }
    }
    return ret;
}

/**
 * @brief  USBD_ClrClassConfig
 *         Clear current configuration
 * @param  pdev: device instance
 * @param  cfgidx: configuration index
 * @retval status: USBD_StatusTypeDef
 */
USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef  *pdev, uint8_t cfgidx)
{
    /* Clear configuration  and Deinitialize the Class process*/
    pdev->pClass->DeInit(pdev, cfgidx);
    return USBD_OK;
}


/**
 * @brief  USBD_SetupStage
 *         Handle the setup stage
 * @param  pdev: device instance
 * @retval status
 */
USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup)
{

    USBD_ParseSetupRequest(&pdev->request, psetup);

    /*USBD_UsrLog("bmReq = %x bReq = %x wInd = %x wLen = %x wVal = %x\n",
            pdev->request.bmRequest, pdev->request.bRequest,
            pdev->request.wIndex, pdev->request.wLength, pdev->request.wValue);*/

    pdev->ep0_state = USBD_EP0_SETUP;
    pdev->ep0_data_len = pdev->request.wLength;

    switch (pdev->request.bmRequest & 0x1F)
    {
    case USB_REQ_RECIPIENT_DEVICE:
        USBD_StdDevReq (pdev, &pdev->request);
        break;

    case USB_REQ_RECIPIENT_INTERFACE:
        USBD_StdItfReq(pdev, &pdev->request);
        break;

    case USB_REQ_RECIPIENT_ENDPOINT:
        USBD_StdEPReq(pdev, &pdev->request);
        break;

    default:
        USBD_LL_StallEP(pdev , pdev->request.bmRequest & 0x80);
        break;
    }
    return USBD_OK;
}

/**
 * @brief  USBD_DataOutStage
 *         Handle data OUT stage
 * @param  pdev: device instance
 * @param  epnum: endpoint index
 * @retval status
 */
USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev , uint8_t epnum, uint8_t *pdata)
{
    USBD_EndpointTypeDef    *pep;

    if(epnum == 0)
    {
        pep = &pdev->ep_out[0];

        if ( pdev->ep0_state == USBD_EP0_DATA_OUT)
        {
            if(pep->rem_length > pep->maxpacket)
            {
                pep->rem_length -=  pep->maxpacket;

                USBD_CtlContinueRx (pdev,
                        pdata,
                        MIN(pep->rem_length ,pep->maxpacket));
            }
            else
            {
                if((pdev->pClass->EP0_RxReady != NULL)&&
                        (pdev->dev_state == USBD_STATE_CONFIGURED))
                {
                    pdev->pClass->EP0_RxReady(pdev);
                }
                USBD_CtlSendStatus(pdev);
            }
        }
    }
    else if((pdev->pClass->DataOut != NULL)&&
            (pdev->dev_state == USBD_STATE_CONFIGURED))
    {
        pdev->pClass->DataOut(pdev, epnum);
    }
    return USBD_OK;
}

/**
 * @brief  USBD_DataInStage
 *         Handle data in stage
 * @param  pdev: device instance
 * @param  epnum: endpoint index
 * @retval status
 */
USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev ,uint8_t epnum, uint8_t *pdata)
{
    USBD_EndpointTypeDef    *pep;

    if(epnum == 0)
    {
        pep = &pdev->ep_in[0];

        if ( pdev->ep0_state == USBD_EP0_DATA_IN)
        {
            if(pep->rem_length > pep->maxpacket)
            {
                pep->rem_length -=  pep->maxpacket;

                USBD_CtlContinueSendData (pdev,
                        pdata,
                        pep->rem_length);
            }
            else
            { /* last packet is MPS multiple, so send ZLP packet */
                if((pep->total_length % pep->maxpacket == 0) &&
                        (pep->total_length >= pep->maxpacket) &&
                        (pep->total_length < pdev->ep0_data_len ))
                {

                    USBD_CtlContinueSendData(pdev , NULL, 0);
                    pdev->ep0_data_len = 0;
                }
                else
                {
                    if((pdev->pClass->EP0_TxSent != NULL)&&
                            (pdev->dev_state == USBD_STATE_CONFIGURED))
                    {
                        pdev->pClass->EP0_TxSent(pdev);
                    }
                    USBD_CtlReceiveStatus(pdev);
                }
            }
        }

        if (pdev->dev_test_mode == 1)
        {
            USBD_RunTestMode(pdev);
            pdev->dev_test_mode = 0;
        }
    }
    else if((pdev->pClass->DataIn != NULL)&&
            (pdev->dev_state == USBD_STATE_CONFIGURED))
    {
        pdev->pClass->DataIn(pdev, epnum);
    }
    return USBD_OK;
}

/**
 * @brief  USBD_LL_Reset
 *         Handle Reset event
 * @param  pdev: device instance
 * @retval status
 */

USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef  *pdev)
{
    /* Open EP0 OUT */
    USBD_LL_OpenEP(pdev,
            0x00,
            USBD_EP_TYPE_CTRL,
            USB_MAX_EP0_SIZE);

    pdev->ep_out[0].maxpacket = USB_MAX_EP0_SIZE;

    /* Open EP0 IN */
    USBD_LL_OpenEP(pdev,
            0x80,
            USBD_EP_TYPE_CTRL,
            USB_MAX_EP0_SIZE);

    pdev->ep_in[0].maxpacket = USB_MAX_EP0_SIZE;
    /* Upon Reset call usr call back */
    pdev->dev_state = USBD_STATE_DEFAULT;

    if (pdev->pClassData)
        pdev->pClass->DeInit(pdev, pdev->dev_config);


    return USBD_OK;
}




/**
 * @brief  USBD_LL_Reset
 *         Handle Reset event
 * @param  pdev: device instance
 * @retval status
 */
USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef  *pdev, USBD_SpeedTypeDef speed)
{
    pdev->dev_speed = speed;
    return USBD_OK;
}

/**
 * @brief  USBD_Suspend
 *         Handle Suspend event
 * @param  pdev: device instance
 * @retval status
 */

USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef  *pdev)
{
    pdev->dev_old_state =  pdev->dev_state;
    pdev->dev_state  = USBD_STATE_SUSPENDED;
    return USBD_OK;
}

/**
 * @brief  USBD_Resume
 *         Handle Resume event
 * @param  pdev: device instance
 * @retval status
 */

USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef  *pdev)
{
    pdev->dev_state = pdev->dev_old_state;
    return USBD_OK;
}

/**
 * @brief  USBD_SOF
 *         Handle SOF event
 * @param  pdev: device instance
 * @retval status
 */

USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef  *pdev)
{
    if(pdev->dev_state == USBD_STATE_CONFIGURED)
    {
        if(pdev->pClass->SOF != NULL)
        {
            pdev->pClass->SOF(pdev);
        }
    }
    return USBD_OK;
}

/**
 * @brief  USBD_IsoINIncomplete
 *         Handle iso in incomplete event
 * @param  pdev: device instance
 * @retval status
 */
USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef  *pdev, uint8_t epnum)
{
    return USBD_OK;
}

/**
 * @brief  USBD_IsoOUTIncomplete
 *         Handle iso out incomplete event
 * @param  pdev: device instance
 * @retval status
 */
USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef  *pdev, uint8_t epnum)
{
    return USBD_OK;
}

/**
 * @brief  USBD_DevConnected
 *         Handle device connection event
 * @param  pdev: device instance
 * @retval status
 */
USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef  *pdev)
{
    return USBD_OK;
}

/**
 * @brief  USBD_DevDisconnected
 *         Handle device disconnection event
 * @param  pdev: device instance
 * @retval status
 */
USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef  *pdev)
{
    /* Free Class Resources */
    pdev->dev_state = USBD_STATE_DEFAULT;
    pdev->pClass->DeInit(pdev, pdev->dev_config);

    return USBD_OK;
}
/**
 * @}
 */


/**
 * @}
 */


/**
 * @}
 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

