/**
  ******************************************************************************
  * @file    usbd_core.c
  * @author  MCD Application Team
  * @brief   This file provides all the USBD core functions.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2015 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "usbd_core.h"

#ifdef USE_USBD_COMPOSITE
#include "usbd_composite_builder.h"
#endif /* USE_USBD_COMPOSITE */

/** @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
  *         Initializes the device stack and load the class driver
  * @param  pdev: device instance
  * @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)
{
  USBD_StatusTypeDef ret;

  /* Check whether the USB Host handle is valid */
  if (pdev == NULL)
  {
#if (USBD_DEBUG_LEVEL > 1U)
    USBD_ErrLog("Invalid Device handle");
#endif /* (USBD_DEBUG_LEVEL > 1U) */
    return USBD_FAIL;
  }

#ifdef USE_USBD_COMPOSITE
  /* Parse the table of classes in use */
  for (uint32_t i = 0; i < USBD_MAX_SUPPORTED_CLASS; i++)
  {
    /* Unlink previous class*/
    pdev->pClass[i] = NULL;
    pdev->pUserData[i] = NULL;

    /* Set class as inactive */
    pdev->tclasslist[i].Active = 0;
    pdev->NumClasses = 0;
    pdev->classId = 0;
  }
#else
  /* Unlink previous class*/
  pdev->pClass[0] = NULL;
  pdev->pUserData[0] = NULL;
#endif /* USE_USBD_COMPOSITE */

  pdev->pConfDesc = 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 */
  ret = USBD_LL_Init(pdev);

  return ret;
}

/**
  * @brief  USBD_DeInit
  *         Re-Initialize the device library
  * @param  pdev: device instance
  * @retval status: status
  */
USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev)
{
  USBD_StatusTypeDef ret;

  /* Disconnect the USB Device */
  (void)USBD_LL_Stop(pdev);

  /* Set Default State */
  pdev->dev_state = USBD_STATE_DEFAULT;

#ifdef USE_USBD_COMPOSITE
  /* Parse the table of classes in use */
  for (uint32_t i = 0; i < USBD_MAX_SUPPORTED_CLASS; i++)
  {
    /* Check if current class is in use */
    if ((pdev->tclasslist[i].Active) == 1U)
    {
      if (pdev->pClass[i] != NULL)
      {
        pdev->classId = i;
        /* Free Class Resources */
        pdev->pClass[i]->DeInit(pdev, (uint8_t)pdev->dev_config);
      }
    }
  }
#else
  /* Free Class Resources */
  if (pdev->pClass[0] != NULL)
  {
    pdev->pClass[0]->DeInit(pdev, (uint8_t)pdev->dev_config);
  }

  pdev->pUserData[0] = NULL;

#endif /* USE_USBD_COMPOSITE */

  /* Free Device descriptors resources */
  pdev->pDesc = NULL;
  pdev->pConfDesc = NULL;

  /* DeInitialize low level driver */
  ret = USBD_LL_DeInit(pdev);

  return ret;
}

/**
  * @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)
{
  uint16_t len = 0U;

  if (pclass == NULL)
  {
#if (USBD_DEBUG_LEVEL > 1U)
    USBD_ErrLog("Invalid Class handle");
#endif /* (USBD_DEBUG_LEVEL > 1U) */
    return USBD_FAIL;
  }

  /* link the class to the USB Device handle */
  pdev->pClass[0] = pclass;

  /* Get Device Configuration Descriptor */
#ifdef USE_USB_HS
  if (pdev->pClass[pdev->classId]->GetHSConfigDescriptor != NULL)
  {
    pdev->pConfDesc = (void *)pdev->pClass[pdev->classId]->GetHSConfigDescriptor(&len);
  }
#else /* Default USE_USB_FS */
  if (pdev->pClass[pdev->classId]->GetFSConfigDescriptor != NULL)
  {
    pdev->pConfDesc = (void *)pdev->pClass[pdev->classId]->GetFSConfigDescriptor(&len);
  }
#endif /* USE_USB_FS */

  /* Increment the NumClasses */
  pdev->NumClasses ++;

  return USBD_OK;
}

#ifdef USE_USBD_COMPOSITE
/**
  * @brief  USBD_RegisterClassComposite
  *         Link class driver to Device Core.
  * @param  pdev : Device Handle
  * @param  pclass: Class handle
  * @param  classtype: Class type
  * @param  EpAddr: Endpoint Address handle
  * @retval USBD Status
  */
USBD_StatusTypeDef  USBD_RegisterClassComposite(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass,
                                                USBD_CompositeClassTypeDef classtype, uint8_t *EpAddr)
{
  USBD_StatusTypeDef   ret = USBD_OK;
  uint16_t len = 0U;

  if ((pdev->classId < USBD_MAX_SUPPORTED_CLASS) && (pdev->NumClasses < USBD_MAX_SUPPORTED_CLASS))
  {
    if ((uint32_t)pclass != 0U)
    {
      /* Link the class to the USB Device handle */
      pdev->pClass[pdev->classId] = pclass;
      ret = USBD_OK;

      pdev->tclasslist[pdev->classId].EpAdd = EpAddr;

      /* Call the composite class builder */
      (void)USBD_CMPSIT_AddClass(pdev, pclass, classtype, 0);

      /* Increment the ClassId for the next occurrence */
      pdev->classId ++;
      pdev->NumClasses ++;
    }
    else
    {
#if (USBD_DEBUG_LEVEL > 1U)
      USBD_ErrLog("Invalid Class handle");
#endif /* (USBD_DEBUG_LEVEL > 1U) */
      ret = USBD_FAIL;
    }
  }

  if (ret == USBD_OK)
  {
    /* Get Device Configuration Descriptor */
#ifdef USE_USB_HS
    pdev->pConfDesc = USBD_CMPSIT.GetHSConfigDescriptor(&len);
#else /* Default USE_USB_FS */
    pdev->pConfDesc = USBD_CMPSIT.GetFSConfigDescriptor(&len);
#endif /* USE_USB_FS */
  }

  return ret;
}

/**
  * @brief  USBD_UnRegisterClassComposite
  *         UnLink all composite class drivers from Device Core.
  * @param  pDevice : Device Handle
  * @retval USBD Status
  */
USBD_StatusTypeDef  USBD_UnRegisterClassComposite(USBD_HandleTypeDef *pdev)
{
  USBD_StatusTypeDef   ret = USBD_FAIL;
  uint8_t idx1;
  uint8_t idx2;

  /* Unroll all activated classes */
  for (idx1 = 0; idx1 < pdev->NumClasses; idx1++)
  {
    /* Check if the class correspond to the requested type and if it is active */
    if (pdev->tclasslist[idx1].Active == 1U)
    {
      /* Set the new class ID */
      pdev->classId = idx1;

      /* Free resources used by the selected class */
      if (pdev->pClass[pdev->classId] != NULL)
      {
        /* Free Class Resources */
        if (pdev->pClass[pdev->classId]->DeInit(pdev, (uint8_t)pdev->dev_config) != 0U)
        {
#if (USBD_DEBUG_LEVEL > 1U)
          USBD_ErrLog("Class DeInit didn't succeed!, can't unregister selected class");
#endif /* (USBD_DEBUG_LEVEL > 1U) */

          ret = USBD_FAIL;
        }
      }

      /* Free the class pointer */
      pdev->pClass[pdev->classId] = NULL;

      /* Free the class location in classes table and reset its parameters to zero */
      pdev->tclasslist[pdev->classId].ClassType = CLASS_TYPE_NONE;
      pdev->tclasslist[pdev->classId].ClassId = 0U;
      pdev->tclasslist[pdev->classId].Active = 0U;
      pdev->tclasslist[pdev->classId].NumEps = 0U;
      pdev->tclasslist[pdev->classId].NumIf = 0U;
      pdev->tclasslist[pdev->classId].CurrPcktSze = 0U;

      for (idx2 = 0U; idx2 < USBD_MAX_CLASS_ENDPOINTS; idx2++)
      {
        pdev->tclasslist[pdev->classId].Eps[idx2].add = 0U;
        pdev->tclasslist[pdev->classId].Eps[idx2].type = 0U;
        pdev->tclasslist[pdev->classId].Eps[idx2].size = 0U;
        pdev->tclasslist[pdev->classId].Eps[idx2].is_used = 0U;
      }

      for (idx2 = 0U; idx2 < USBD_MAX_CLASS_INTERFACES; idx2++)
      {
        pdev->tclasslist[pdev->classId].Ifs[idx2] = 0U;
      }
    }
  }

  /* Reset the configuration descriptor */
  (void)USBD_CMPST_ClearConfDesc(pdev);

  /* Reset the class ID and number of classes */
  pdev->classId = 0U;
  pdev->NumClasses = 0U;

  return ret;
}


#endif /* USE_USBD_COMPOSITE */

/**
  * @brief  USBD_Start
  *         Start the USB Device Core.
  * @param  pdev: Device Handle
  * @retval USBD Status
  */
USBD_StatusTypeDef USBD_Start(USBD_HandleTypeDef *pdev)
{
#ifdef USE_USBD_COMPOSITE
  pdev->classId = 0U;
#endif /* USE_USBD_COMPOSITE */

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

/**
  * @brief  USBD_Stop
  *         Stop the USB Device Core.
  * @param  pdev: Device Handle
  * @retval USBD Status
  */
USBD_StatusTypeDef USBD_Stop(USBD_HandleTypeDef *pdev)
{
  /* Disconnect USB Device */
  (void)USBD_LL_Stop(pdev);

  /* Free Class Resources */
#ifdef USE_USBD_COMPOSITE
  /* Parse the table of classes in use */
  for (uint32_t i = 0U; i < USBD_MAX_SUPPORTED_CLASS; i++)
  {
    /* Check if current class is in use */
    if ((pdev->tclasslist[i].Active) == 1U)
    {
      if (pdev->pClass[i] != NULL)
      {
        pdev->classId = i;
        /* Free Class Resources */
        (void)pdev->pClass[i]->DeInit(pdev, (uint8_t)pdev->dev_config);
      }
    }
  }

  /* Reset the class ID */
  pdev->classId = 0U;
#else
  if (pdev->pClass[0] != NULL)
  {
    (void)pdev->pClass[0]->DeInit(pdev, (uint8_t)pdev->dev_config);
  }
#endif /* USE_USBD_COMPOSITE */

  return USBD_OK;
}

/**
  * @brief  USBD_RunTestMode
  *         Launch test mode process
  * @param  pdev: device instance
  * @retval status
  */
USBD_StatusTypeDef USBD_RunTestMode(USBD_HandleTypeDef *pdev)
{
#ifdef USBD_HS_TESTMODE_ENABLE
  USBD_StatusTypeDef ret;

  /* Run USB HS test mode */
  ret = USBD_LL_SetTestMode(pdev, pdev->dev_test_mode);

  return ret;
#else
  /* Prevent unused argument compilation warning */
  UNUSED(pdev);

  return USBD_OK;
#endif /* USBD_HS_TESTMODE_ENABLE */
}

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

#ifdef USE_USBD_COMPOSITE
  /* Parse the table of classes in use */
  for (uint32_t i = 0U; i < USBD_MAX_SUPPORTED_CLASS; i++)
  {
    /* Check if current class is in use */
    if ((pdev->tclasslist[i].Active) == 1U)
    {
      if (pdev->pClass[i] != NULL)
      {
        pdev->classId = i;
        /* Set configuration  and Start the Class*/
        if (pdev->pClass[i]->Init(pdev, cfgidx) != 0U)
        {
          ret = USBD_FAIL;
        }
      }
    }
  }
#else
  if (pdev->pClass[0] != NULL)
  {
    /* Set configuration and Start the Class */
    ret = (USBD_StatusTypeDef)pdev->pClass[0]->Init(pdev, cfgidx);
  }
#endif /* USE_USBD_COMPOSITE */

  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)
{
  USBD_StatusTypeDef ret = USBD_OK;

#ifdef USE_USBD_COMPOSITE
  /* Parse the table of classes in use */
  for (uint32_t i = 0U; i < USBD_MAX_SUPPORTED_CLASS; i++)
  {
    /* Check if current class is in use */
    if ((pdev->tclasslist[i].Active) == 1U)
    {
      if (pdev->pClass[i] != NULL)
      {
        pdev->classId = i;
        /* Clear configuration  and De-initialize the Class process */
        if (pdev->pClass[i]->DeInit(pdev, cfgidx) != 0U)
        {
          ret = USBD_FAIL;
        }
      }
    }
  }
#else
  /* Clear configuration  and De-initialize the Class process */
  if (pdev->pClass[0]->DeInit(pdev, cfgidx) != 0U)
  {
    ret = USBD_FAIL;
  }
#endif /* USE_USBD_COMPOSITE */

  return ret;
}


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

  USBD_ParseSetupRequest(&pdev->request, psetup);

  pdev->ep0_state = USBD_EP0_SETUP;

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

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

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

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

    default:
      ret = USBD_LL_StallEP(pdev, (pdev->request.bmRequest & 0x80U));
      break;
  }

  return ret;
}

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

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

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

        (void)USBD_CtlContinueRx(pdev, pdata, MIN(pep->rem_length, pep->maxpacket));
      }
      else
      {
        /* Find the class ID relative to the current request */
        switch (pdev->request.bmRequest & 0x1FU)
        {
          case USB_REQ_RECIPIENT_DEVICE:
            /* Device requests must be managed by the first instantiated class
               (or duplicated by all classes for simplicity) */
            idx = 0U;
            break;

          case USB_REQ_RECIPIENT_INTERFACE:
            idx = USBD_CoreFindIF(pdev, LOBYTE(pdev->request.wIndex));
            break;

          case USB_REQ_RECIPIENT_ENDPOINT:
            idx = USBD_CoreFindEP(pdev, LOBYTE(pdev->request.wIndex));
            break;

          default:
            /* Back to the first class in case of doubt */
            idx = 0U;
            break;
        }

        if (idx < USBD_MAX_SUPPORTED_CLASS)
        {
          /* Setup the class ID and route the request to the relative class function */
          if (pdev->dev_state == USBD_STATE_CONFIGURED)
          {
            if (pdev->pClass[idx]->EP0_RxReady != NULL)
            {
              pdev->classId = idx;
              pdev->pClass[idx]->EP0_RxReady(pdev);
            }
          }
        }

        (void)USBD_CtlSendStatus(pdev);
      }
    }
    else
    {
#if 0
      if (pdev->ep0_state == USBD_EP0_STATUS_OUT)
      {
        /*
          * STATUS PHASE completed, update ep0_state to idle
          */
        pdev->ep0_state = USBD_EP0_IDLE;
        (void)USBD_LL_StallEP(pdev, 0U);
      }
#endif
    }
  }
  else
  {
    /* Get the class index relative to this interface */
    idx = USBD_CoreFindEP(pdev, (epnum & 0x7FU));

    if (((uint16_t)idx != 0xFFU) && (idx < USBD_MAX_SUPPORTED_CLASS))
    {
      /* Call the class data out function to manage the request */
      if (pdev->dev_state == USBD_STATE_CONFIGURED)
      {
        if (pdev->pClass[idx]->DataOut != NULL)
        {
          pdev->classId = idx;
          ret = (USBD_StatusTypeDef)pdev->pClass[idx]->DataOut(pdev, epnum);
        }
      }
      if (ret != USBD_OK)
      {
        return ret;
      }
    }
  }

  return USBD_OK;
}

/**
  * @brief  USBD_LL_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;
  USBD_StatusTypeDef ret;
  uint8_t idx;

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

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

        (void)USBD_CtlContinueSendData(pdev, pdata, pep->rem_length);

        /* Prepare endpoint for premature end of transfer */
        (void)USBD_LL_PrepareReceive(pdev, 0U, NULL, 0U);
      }
      else
      {
        /* last packet is MPS multiple, so send ZLP packet */
        if ((pep->maxpacket == pep->rem_length) &&
            (pep->total_length >= pep->maxpacket) &&
            (pep->total_length < pdev->ep0_data_len))
        {
          (void)USBD_CtlContinueSendData(pdev, NULL, 0U);
          pdev->ep0_data_len = 0U;

          /* Prepare endpoint for premature end of transfer */
          (void)USBD_LL_PrepareReceive(pdev, 0U, NULL, 0U);
        }
        else
        {
          if (pdev->dev_state == USBD_STATE_CONFIGURED)
          {
            if (pdev->pClass[0]->EP0_TxSent != NULL)
            {
              pdev->classId = 0U;
              pdev->pClass[0]->EP0_TxSent(pdev);
            }
          }
          (void)USBD_LL_StallEP(pdev, 0x80U);
          (void)USBD_CtlReceiveStatus(pdev);
        }
      }
    }
    else
    {
#if 0
      if ((pdev->ep0_state == USBD_EP0_STATUS_IN) ||
          (pdev->ep0_state == USBD_EP0_IDLE))
      {
        (void)USBD_LL_StallEP(pdev, 0x80U);
      }
#endif
    }

    if (pdev->dev_test_mode != 0U)
    {
      (void)USBD_RunTestMode(pdev);
      pdev->dev_test_mode = 0U;
    }
  }
  else
  {
    /* Get the class index relative to this interface */
    idx = USBD_CoreFindEP(pdev, ((uint8_t)epnum | 0x80U));

    if (((uint16_t)idx != 0xFFU) && (idx < USBD_MAX_SUPPORTED_CLASS))
    {
      /* Call the class data out function to manage the request */
      if (pdev->dev_state == USBD_STATE_CONFIGURED)
      {
        if (pdev->pClass[idx]->DataIn != NULL)
        {
          pdev->classId = idx;
          ret = (USBD_StatusTypeDef)pdev->pClass[idx]->DataIn(pdev, epnum);

          if (ret != USBD_OK)
          {
            return ret;
          }
        }
      }
    }
  }

  return USBD_OK;
}

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

USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev)
{
  USBD_StatusTypeDef ret = USBD_OK;

  /* Upon Reset call user call back */
  pdev->dev_state = USBD_STATE_DEFAULT;
  pdev->ep0_state = USBD_EP0_IDLE;
  pdev->dev_config = 0U;
  pdev->dev_remote_wakeup = 0U;
  pdev->dev_test_mode = 0U;

#ifdef USE_USBD_COMPOSITE
  /* Parse the table of classes in use */
  for (uint32_t i = 0U; i < USBD_MAX_SUPPORTED_CLASS; i++)
  {
    /* Check if current class is in use */
    if ((pdev->tclasslist[i].Active) == 1U)
    {
      if (pdev->pClass[i] != NULL)
      {
        pdev->classId = i;
        /* Clear configuration  and De-initialize the Class process*/

        if (pdev->pClass[i]->DeInit != NULL)
        {
          if (pdev->pClass[i]->DeInit(pdev, (uint8_t)pdev->dev_config) != USBD_OK)
          {
            ret = USBD_FAIL;
          }
        }
      }
    }
  }
#else

  if (pdev->pClass[0] != NULL)
  {
    if (pdev->pClass[0]->DeInit != NULL)
    {
      if (pdev->pClass[0]->DeInit(pdev, (uint8_t)pdev->dev_config) != USBD_OK)
      {
        ret = USBD_FAIL;
      }
    }
  }
#endif /* USE_USBD_COMPOSITE */

  /* Open EP0 OUT */
  (void)USBD_LL_OpenEP(pdev, 0x00U, USBD_EP_TYPE_CTRL, USB_MAX_EP0_SIZE);
  pdev->ep_out[0x00U & 0xFU].is_used = 1U;

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

  /* Open EP0 IN */
  (void)USBD_LL_OpenEP(pdev, 0x80U, USBD_EP_TYPE_CTRL, USB_MAX_EP0_SIZE);
  pdev->ep_in[0x80U & 0xFU].is_used = 1U;

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

  return ret;
}

/**
  * @brief  USBD_LL_SetSpeed
  *         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_LL_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_LL_Resume
  *         Handle Resume event
  * @param  pdev: device instance
  * @retval status
  */

USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev)
{
  if (pdev->dev_state == USBD_STATE_SUSPENDED)
  {
    pdev->dev_state = pdev->dev_old_state;
  }

  return USBD_OK;
}

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

USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev)
{
  /* The SOF event can be distributed for all classes that support it */
  if (pdev->dev_state == USBD_STATE_CONFIGURED)
  {
#ifdef USE_USBD_COMPOSITE
    /* Parse the table of classes in use */
    for (uint32_t i = 0; i < USBD_MAX_SUPPORTED_CLASS; i++)
    {
      /* Check if current class is in use */
      if ((pdev->tclasslist[i].Active) == 1U)
      {
        if (pdev->pClass[i] != NULL)
        {
          if (pdev->pClass[i]->SOF != NULL)
          {
            pdev->classId = i;
            (void)pdev->pClass[i]->SOF(pdev);
          }
        }
      }
    }
#else
    if (pdev->pClass[0] != NULL)
    {
      if (pdev->pClass[0]->SOF != NULL)
      {
        (void)pdev->pClass[0]->SOF(pdev);
      }
    }
#endif /* USE_USBD_COMPOSITE */
  }

  return USBD_OK;
}

/**
  * @brief  USBD_LL_IsoINIncomplete
  *         Handle iso in incomplete event
  * @param  pdev: device instance
  * @retval status
  */
USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev,
                                           uint8_t epnum)
{
  if (pdev->pClass[pdev->classId] == NULL)
  {
    return USBD_FAIL;
  }

  if (pdev->dev_state == USBD_STATE_CONFIGURED)
  {
    if (pdev->pClass[pdev->classId]->IsoINIncomplete != NULL)
    {
      (void)pdev->pClass[pdev->classId]->IsoINIncomplete(pdev, epnum);
    }
  }

  return USBD_OK;
}

/**
  * @brief  USBD_LL_IsoOUTIncomplete
  *         Handle iso out incomplete event
  * @param  pdev: device instance
  * @retval status
  */
USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev,
                                            uint8_t epnum)
{
  if (pdev->pClass[pdev->classId] == NULL)
  {
    return USBD_FAIL;
  }

  if (pdev->dev_state == USBD_STATE_CONFIGURED)
  {
    if (pdev->pClass[pdev->classId]->IsoOUTIncomplete != NULL)
    {
      (void)pdev->pClass[pdev->classId]->IsoOUTIncomplete(pdev, epnum);
    }
  }

  return USBD_OK;
}

/**
  * @brief  USBD_LL_DevConnected
  *         Handle device connection event
  * @param  pdev: device instance
  * @retval status
  */
USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev)
{
  /* Prevent unused argument compilation warning */
  UNUSED(pdev);

  return USBD_OK;
}

/**
  * @brief  USBD_LL_DevDisconnected
  *         Handle device disconnection event
  * @param  pdev: device instance
  * @retval status
  */
USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev)
{
  USBD_StatusTypeDef   ret = USBD_OK;

  /* Free Class Resources */
  pdev->dev_state = USBD_STATE_DEFAULT;

#ifdef USE_USBD_COMPOSITE
  /* Parse the table of classes in use */
  for (uint32_t i = 0; i < USBD_MAX_SUPPORTED_CLASS; i++)
  {
    /* Check if current class is in use */
    if ((pdev->tclasslist[i].Active) == 1U)
    {
      if (pdev->pClass[i] != NULL)
      {
        pdev->classId = i;
        /* Clear configuration  and De-initialize the Class process*/
        if (pdev->pClass[i]->DeInit(pdev, (uint8_t)pdev->dev_config) != 0U)
        {
          ret = USBD_FAIL;
        }
      }
    }
  }
#else
  if (pdev->pClass[0] != NULL)
  {
    if (pdev->pClass[0]->DeInit(pdev, (uint8_t)pdev->dev_config) != 0U)
    {
      ret = USBD_FAIL;
    }
  }
#endif /* USE_USBD_COMPOSITE */

  return ret;
}

/**
  * @brief  USBD_CoreFindIF
  *         return the class index relative to the selected interface
  * @param  pdev: device instance
  * @param  index : selected interface number
  * @retval index of the class using the selected interface number. OxFF if no class found.
  */
uint8_t USBD_CoreFindIF(USBD_HandleTypeDef *pdev, uint8_t index)
{
#ifdef USE_USBD_COMPOSITE
  /* Parse the table of classes in use */
  for (uint32_t i = 0U; i < USBD_MAX_SUPPORTED_CLASS; i++)
  {
    /* Check if current class is in use */
    if ((pdev->tclasslist[i].Active) == 1U)
    {
      /* Parse all interfaces listed in the current class */
      for (uint32_t j = 0U; j < pdev->tclasslist[i].NumIf; j++)
      {
        /* Check if requested Interface matches the current class interface */
        if (pdev->tclasslist[i].Ifs[j] == index)
        {
          if (pdev->pClass[i]->Setup != NULL)
          {
            return (uint8_t)i;
          }
        }
      }
    }
  }

  return 0xFFU;
#else
  UNUSED(pdev);
  UNUSED(index);

  return 0x00U;
#endif /* USE_USBD_COMPOSITE */
}

/**
  * @brief  USBD_CoreFindEP
  *         return the class index relative to the selected endpoint
  * @param  pdev: device instance
  * @param  index : selected endpoint number
  * @retval index of the class using the selected endpoint number. 0xFF if no class found.
  */
uint8_t USBD_CoreFindEP(USBD_HandleTypeDef *pdev, uint8_t index)
{
#ifdef USE_USBD_COMPOSITE
  /* Parse the table of classes in use */
  for (uint32_t i = 0U; i < USBD_MAX_SUPPORTED_CLASS; i++)
  {
    /* Check if current class is in use */
    if ((pdev->tclasslist[i].Active) == 1U)
    {
      /* Parse all endpoints listed in the current class */
      for (uint32_t j = 0U; j < pdev->tclasslist[i].NumEps; j++)
      {
        /* Check if requested endpoint matches the current class endpoint */
        if (pdev->tclasslist[i].Eps[j].add == index)
        {
          if (pdev->pClass[i]->Setup != NULL)
          {
            return (uint8_t)i;
          }
        }
      }
    }
  }

  return 0xFFU;
#else
  UNUSED(pdev);
  UNUSED(index);

  return 0x00U;
#endif /* USE_USBD_COMPOSITE */
}

#ifdef USE_USBD_COMPOSITE
/**
  * @brief  USBD_CoreGetEPAdd
  *         Get the endpoint address relative to a selected class
  * @param  pdev: device instance
  * @param  ep_dir: USBD_EP_IN or USBD_EP_OUT
  * @param  ep_type: USBD_EP_TYPE_CTRL, USBD_EP_TYPE_ISOC, USBD_EP_TYPE_BULK or USBD_EP_TYPE_INTR
  * @retval Address of the selected endpoint or 0xFFU if no endpoint found.
  */
uint8_t USBD_CoreGetEPAdd(USBD_HandleTypeDef *pdev, uint8_t ep_dir, uint8_t ep_type)
{
  uint8_t idx;

  /* Find the EP address in the selected class table */
  for (idx = 0; idx < pdev->tclasslist[pdev->classId].NumEps; idx++)
  {
    if (((pdev->tclasslist[pdev->classId].Eps[idx].add & USBD_EP_IN) == ep_dir) && \
        (pdev->tclasslist[pdev->classId].Eps[idx].type == ep_type) && \
        (pdev->tclasslist[pdev->classId].Eps[idx].is_used != 0U))
    {
      return (pdev->tclasslist[pdev->classId].Eps[idx].add);
    }
  }

  /* If reaching this point, then no endpoint was found */
  return 0xFFU;
}
#endif /* USE_USBD_COMPOSITE */

/**
  * @brief  USBD_GetEpDesc
  *         This function return the Endpoint descriptor
  * @param  pdev: device instance
  * @param  pConfDesc:  pointer to Bos descriptor
  * @param  EpAddr:  endpoint address
  * @retval pointer to video endpoint descriptor
  */
void *USBD_GetEpDesc(uint8_t *pConfDesc, uint8_t EpAddr)
{
  USBD_DescHeaderTypeDef *pdesc = (USBD_DescHeaderTypeDef *)(void *)pConfDesc;
  USBD_ConfigDescTypeDef *desc = (USBD_ConfigDescTypeDef *)(void *)pConfDesc;
  USBD_EpDescTypeDef *pEpDesc = NULL;
  uint16_t ptr;

  if (desc->wTotalLength > desc->bLength)
  {
    ptr = desc->bLength;

    while (ptr < desc->wTotalLength)
    {
      pdesc = USBD_GetNextDesc((uint8_t *)pdesc, &ptr);

      if (pdesc->bDescriptorType == USB_DESC_TYPE_ENDPOINT)
      {
        pEpDesc = (USBD_EpDescTypeDef *)(void *)pdesc;

        if (pEpDesc->bEndpointAddress == EpAddr)
        {
          break;
        }
        else
        {
          pEpDesc = NULL;
        }
      }
    }
  }

  return (void *)pEpDesc;
}

/**
  * @brief  USBD_GetNextDesc
  *         This function return the next descriptor header
  * @param  buf: Buffer where the descriptor is available
  * @param  ptr: data pointer inside the descriptor
  * @retval next header
  */
USBD_DescHeaderTypeDef *USBD_GetNextDesc(uint8_t *pbuf, uint16_t *ptr)
{
  USBD_DescHeaderTypeDef *pnext = (USBD_DescHeaderTypeDef *)(void *)pbuf;

  *ptr += pnext->bLength;
  pnext = (USBD_DescHeaderTypeDef *)(void *)(pbuf + pnext->bLength);

  return (pnext);
}

/**
  * @}
  */


/**
  * @}
  */


/**
  * @}
  */

