/**
 ******************************************************************************
 * @file    usbd_core.c
 * @author  MCD Application Team
 * @version V1.1.0
 * @date    19-March-2012
 * @brief   This file provides all the USBD core functions.
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT 2012 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 ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#include "stdlib.h"
#include "global.h"

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

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

#include "usb/usb.h"

#include "usbd_core.h"
#include "usbd_req_msc.h"
#include "usbd_ioreq_msc.h"
#include "usb_dcd_int.h"
#include "usb_bsp.h"

#define DEBUG_USBD_SetupStage_msc 0
/** @addtogroup STM32_USB_OTG_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
 * @{
 */
static uint8_t USBD_SetupStage_msc(USB_OTG_CORE_HANDLE *pdev);
static uint8_t USBD_DataOutStage_msc(USB_OTG_CORE_HANDLE *pdev , uint8_t epnum);
static uint8_t USBD_DataInStage_msc(USB_OTG_CORE_HANDLE *pdev , uint8_t epnum);
static uint8_t USBD_SOF_msc(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_Reset_msc(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_Suspend_msc(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_Resume_msc(USB_OTG_CORE_HANDLE  *pdev);
#ifdef VBUS_SENSING_ENABLED
static uint8_t USBD_DevConnected_msc(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_DevDisconnected_msc(USB_OTG_CORE_HANDLE  *pdev);
#endif
static uint8_t USBD_IsoINIncomplete_msc(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_IsoOUTIncomplete_msc(USB_OTG_CORE_HANDLE  *pdev);
static uint8_t USBD_RunTestMode_msc (USB_OTG_CORE_HANDLE  *pdev) ;
/**
 * @}
 */

/** @defgroup USBD_CORE_Private_Variables
 * @{
 */

__IO USB_OTG_DCTL_TypeDef SET_TEST_MODE;

USBD_DCD_INT_cb_TypeDef USBD_DCD_INT_cb =
{
        USBD_DataOutStage_msc,
        USBD_DataInStage_msc,
        USBD_SetupStage_msc,
        USBD_SOF_msc,
        USBD_Reset_msc,
        USBD_Suspend_msc,
        USBD_Resume_msc,
        USBD_IsoINIncomplete_msc,
        USBD_IsoOUTIncomplete_msc,
#ifdef VBUS_SENSING_ENABLED
        USBD_DevConnected_msc,
        USBD_DevDisconnected_msc,
#endif
};

USBD_DCD_INT_cb_TypeDef  *USBD_DCD_INT_fops = &USBD_DCD_INT_cb;
/**
 * @}
 */

/** @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  class_cb: Class callback structure address
 * @param  usr_cb: User callback structure address
 * @retval None
 */
void USBD_Init_msc(USB_OTG_CORE_HANDLE *pdev,
        USB_OTG_CORE_ID_TypeDef coreID,
        USBD_DEVICE *pDevice,
        USBD_Class_cb_TypeDef *class_cb,
        USBD_Usr_cb_TypeDef *usr_cb)
{
    /* Hardware Init */
    USB_OTG_BSP_Init(pdev);

    //USBD_DeInit_msc(pdev);

    /*Register class and user callbacks */
    pdev->dev.class_cb = class_cb;
    pdev->dev.usr_cb = usr_cb;
    pdev->dev.usr_device = pDevice;

    /* set USB OTG core params */
    DCD_Init(pdev , coreID);

    /* Upon Init call usr callback */
    pdev->dev.usr_cb->Init();

    /* Enable Interrupts */
    USB_OTG_BSP_EnableInterrupt(pdev);
}

/**
 * @brief  USBD_DeInit
 *         Re-Initialize th device library
 * @param  pdev: device instance
 * @retval status: status
 */
USBD_Status USBD_DeInit_msc(USB_OTG_CORE_HANDLE *pdev)
{
    /* Software Init */
    //USB_OTG_DisableGlobalInt(pdev);
    //USB_OTG_StopDevice(pdev);
    //DCD_DevDisconnect(pdev);
    return USBD_OK;
}

/**
 * @brief  USBD_SetupStage
 *         Handle the setup stage
 * @param  pdev: device instance
 * @retval status
 */
static uint8_t USBD_SetupStage_msc(USB_OTG_CORE_HANDLE *pdev)
{
    USB_SETUP_REQ req;

    USBD_ParseSetupRequest_msc(pdev , &req);

    switch (req.bmRequest & 0x1F)
    {
    case USB_REQ_RECIPIENT_DEVICE:
#if DEBUG_USBD_SetupStage_msc > 0
        debug_printDirectStr("\tF\t");
#endif
        USBD_StdDevReq_msc (pdev, &req);
        break;

    case USB_REQ_RECIPIENT_INTERFACE:
#if DEBUG_USBD_SetupStage_msc > 0
        debug_printDirectStr("\tG\t");
#endif
        USBD_StdItfReq_msc(pdev, &req);
        break;

    case USB_REQ_RECIPIENT_ENDPOINT:
#if DEBUG_USBD_SetupStage_msc > 0
        debug_printDirectStr("\tH\t");
#endif
        USBD_StdEPReq_msc(pdev, &req);
        break;

    default:
#if DEBUG_USBD_SetupStage_msc > 0
        debug_printDirectStr("\tJ\t");
#endif
        DCD_EP_Stall(pdev , req.bmRequest & 0x80);
        break;
    }
    return USBD_OK;
}

/**
 * @brief  USBD_DataOutStage
 *         Handle data out stage
 * @param  pdev: device instance
 * @param  epnum: endpoint index
 * @retval status
 */
static uint8_t USBD_DataOutStage_msc(USB_OTG_CORE_HANDLE *pdev , uint8_t epnum)
{
    USB_OTG_EP *ep;

    if(epnum == 0)
    {
        ep = &pdev->dev.out_ep[0];
        if ( pdev->dev.device_state == USB_OTG_EP0_DATA_OUT)
        {
            if(ep->rem_data_len > ep->maxpacket)
            {
                ep->rem_data_len -=  ep->maxpacket;

                if(pdev->cfg.dma_enable == 1)
                {
                    /* in slave mode this, is handled by the RxSTSQLvl ISR */
                    ep->xfer_buff += ep->maxpacket;
                }
                USBD_CtlContinueRx_msc (pdev,
                        ep->xfer_buff,
                        MIN(ep->rem_data_len ,ep->maxpacket));
            }
            else
            {
                if((pdev->dev.class_cb->EP0_RxReady != NULL)&&
                        (pdev->dev.device_status == USB_OTG_CONFIGURED))
                {
                    pdev->dev.class_cb->EP0_RxReady(pdev);
                }
                USBD_CtlSendStatus_msc(pdev);
            }
        }
    }
    else if((pdev->dev.class_cb->DataOut != NULL)&&
            (pdev->dev.device_status == USB_OTG_CONFIGURED))
    {
        pdev->dev.class_cb->DataOut(pdev, epnum);
    }
    return USBD_OK;
}

/**
 * @brief  USBD_DataInStage
 *         Handle data in stage
 * @param  pdev: device instance
 * @param  epnum: endpoint index
 * @retval status
 */
static uint8_t USBD_DataInStage_msc(USB_OTG_CORE_HANDLE *pdev , uint8_t epnum)
{
    USB_OTG_EP *ep;

    if(epnum == 0)
    {
        ep = &pdev->dev.in_ep[0];
        if ( pdev->dev.device_state == USB_OTG_EP0_DATA_IN)
        {
            if(ep->rem_data_len > ep->maxpacket)
            {
                ep->rem_data_len -=  ep->maxpacket;
                if(pdev->cfg.dma_enable == 1)
                {
                    /* in slave mode this, is handled by the TxFifoEmpty ISR */
                    ep->xfer_buff += ep->maxpacket;
                }
                USBD_CtlContinueSendData_msc (pdev,
                        ep->xfer_buff,
                        ep->rem_data_len);
            }
            else
            { /* last packet is MPS multiple, so send ZLP packet */
                if((ep->total_data_len % ep->maxpacket == 0) &&
                        (ep->total_data_len >= ep->maxpacket) &&
                        (ep->total_data_len < ep->ctl_data_len ))
                {

                    USBD_CtlContinueSendData_msc(pdev , NULL, 0);
                    ep->ctl_data_len = 0;
                }
                else
                {
                    if((pdev->dev.class_cb->EP0_TxSent != NULL)&&
                            (pdev->dev.device_status == USB_OTG_CONFIGURED))
                    {
                        pdev->dev.class_cb->EP0_TxSent(pdev);
                    }
                    USBD_CtlReceiveStatus_msc(pdev);
                }
            }
        }
        if (pdev->dev.test_mode == 1)
        {
            USBD_RunTestMode_msc(pdev);
            pdev->dev.test_mode = 0;
        }
    }
    else if((pdev->dev.class_cb->DataIn != NULL)&&
            (pdev->dev.device_status == USB_OTG_CONFIGURED))
    {
        pdev->dev.class_cb->DataIn(pdev, epnum);
    }
    return USBD_OK;
}




/**
 * @brief  USBD_RunTestMode
 *         Launch test mode process
 * @param  pdev: device instance
 * @retval status
 */
static uint8_t  USBD_RunTestMode_msc (USB_OTG_CORE_HANDLE  *pdev)
{
    USB_OTG_WRITE_REG32(&pdev->regs.DREGS->DCTL, SET_TEST_MODE.d32);
    return USBD_OK;
}

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

static uint8_t USBD_Reset_msc(USB_OTG_CORE_HANDLE  *pdev)
{
    /* Open EP0 OUT */
    DCD_EP_Open(pdev,
            0x00,
            USB_OTG_MAX_EP0_SIZE,
            EP_TYPE_CTRL);

    /* Open EP0 IN */
    DCD_EP_Open(pdev,
            0x80,
            USB_OTG_MAX_EP0_SIZE,
            EP_TYPE_CTRL);

    /* Upon Reset call usr call back */
    pdev->dev.device_status = USB_OTG_DEFAULT;
    pdev->dev.usr_cb->DeviceReset(pdev->cfg.speed);

    return USBD_OK;
}

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

static uint8_t USBD_Resume_msc(USB_OTG_CORE_HANDLE  *pdev)
{
    /* Upon Resume call usr call back */
    pdev->dev.usr_cb->DeviceResumed();
    pdev->dev.device_status = pdev->dev.device_old_status;
    pdev->dev.device_status = USB_OTG_CONFIGURED;
    return USBD_OK;
}


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

static uint8_t USBD_Suspend_msc(USB_OTG_CORE_HANDLE  *pdev)
{
    pdev->dev.device_old_status = pdev->dev.device_status;
    pdev->dev.device_status  = USB_OTG_SUSPENDED;
    /* Upon Resume call usr call back */
    pdev->dev.usr_cb->DeviceSuspended();
    return USBD_OK;
}


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

static uint8_t USBD_SOF_msc(USB_OTG_CORE_HANDLE  *pdev)
{
    if(pdev->dev.class_cb->SOF)
    {
        pdev->dev.class_cb->SOF(pdev);
    }
    return USBD_OK;
}
/**
 * @brief  USBD_SetCfg
 *        Configure device and start the interface
 * @param  pdev: device instance
 * @param  cfgidx: configuration index
 * @retval status
 */

USBD_Status USBD_SetCfg_msc(USB_OTG_CORE_HANDLE  *pdev, uint8_t cfgidx)
{
    pdev->dev.class_cb->Init(pdev, cfgidx);

    /* Upon set config call usr call back */
    pdev->dev.usr_cb->DeviceConfigured();
    return USBD_OK;
}

/**
 * @brief  USBD_ClrCfg
 *         Clear current configuration
 * @param  pdev: device instance
 * @param  cfgidx: configuration index
 * @retval status: USBD_Status
 */
USBD_Status USBD_ClrCfg_msc(USB_OTG_CORE_HANDLE  *pdev, uint8_t cfgidx)
{
    pdev->dev.class_cb->DeInit(pdev, cfgidx);
    return USBD_OK;
}

/**
 * @brief  USBD_IsoINIncomplete
 *         Handle iso in incomplete event
 * @param  pdev: device instance
 * @retval status
 */
static uint8_t USBD_IsoINIncomplete_msc(USB_OTG_CORE_HANDLE  *pdev)
{
    pdev->dev.class_cb->IsoINIncomplete(pdev);
    return USBD_OK;
}

/**
 * @brief  USBD_IsoOUTIncomplete
 *         Handle iso out incomplete event
 * @param  pdev: device instance
 * @retval status
 */
static uint8_t USBD_IsoOUTIncomplete_msc(USB_OTG_CORE_HANDLE  *pdev)
{
    pdev->dev.class_cb->IsoOUTIncomplete(pdev);
    return USBD_OK;
}

#ifdef VBUS_SENSING_ENABLED
/**
 * @brief  USBD_DevConnected
 *         Handle device connection event
 * @param  pdev: device instance
 * @retval status
 */
static uint8_t USBD_DevConnected_msc(USB_OTG_CORE_HANDLE  *pdev)
{
    pdev->dev.usr_cb->DeviceConnected();
    pdev->dev.connection_status = 1;
    //usbState = USB_CONNECTION_OK;
    return USBD_OK;
}

/**
 * @brief  USBD_DevDisconnected
 *         Handle device disconnection event
 * @param  pdev: device instance
 * @retval status
 */
static uint8_t USBD_DevDisconnected_msc(USB_OTG_CORE_HANDLE  *pdev)
{
    pdev->dev.usr_cb->DeviceDisconnected();
    pdev->dev.class_cb->DeInit(pdev, 0);
    pdev->dev.connection_status = 0;
    usbState = USB_CONNECTION_DISCONNECT;
    return USBD_OK;
}
#endif
/**
 * @}
 */


/**
 * @}
 */


/**
 * @}
 */

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

