text: /***************************************************************************//**
  * \file cy_usbfs_dev_drv_io_dma.c
  * \version 2.10
  *
  * Provides data transfer API implementation of the USBFS driver.
  *
  ********************************************************************************
  * \copyright
  * Copyright 2018-2019 Cypress Semiconductor Corporation
  * SPDX-License-Identifier: Apache-2.0
  *
  * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
  *
  * 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.
  *******************************************************************************/
  
  #include <string.h>
  #include "cy_usbfs_dev_drv_pvt.h"
  
  #ifdef CY_IP_MXUSBFS
  
  #if defined(__cplusplus)
  extern "C" {
  #endif
  
  /*******************************************************************************
  *                        Internal Constants
  *******************************************************************************/
  
  /* Invalid channel */
  #define DMA_INVALID_CHANNEL     ((uint32_t) (-1))
  
  /* Arbiter interrupt sources for OUT and IN endpoints when mode is
  * CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO.
  */
  #define IN_ENDPOINT_ARB_INTR_SOURCES    (USBFS_USBDEV_ARB_EP_IN_BUF_FULL_Msk | \
                                           USBFS_USBDEV_ARB_EP_BUF_OVER_Msk    | \
                                           USBFS_USBDEV_ARB_EP_BUF_UNDER_Msk   | \
                                           USBFS_USBDEV_ARB_EP_ERR_Msk)
  
  #define OUT_ENDPOINT_ARB_INTR_SOURCES   (USBFS_USBDEV_ARB_EP_DMA_TERMIN_Msk | \
                                           USBFS_USBDEV_ARB_EP_BUF_OVER_Msk   | \
                                           USBFS_USBDEV_ARB_EP_BUF_UNDER_Msk  | \
                                           USBFS_USBDEV_ARB_EP_ERR_Msk)
  
  /* DMA configuration defines*/
  #define DMA_XLOOP_INCREMENT    (1)
  #define DMA_YLOOP_INCREMENT    (32)
  #define DMA_NO_INCREMENT       (0)
  
  /* Timeout for dynamic reconfiguration */
  #define DYN_RECONFIG_ONE_TICK   (1U)        /* 1 tick = 1 us */
  #define DYN_RECONFIG_TIMEOUT    (25000UL)   /* (25000 * tick)us = 25 ms ( TDRQCMPLTND / 2 ) */
  
  /* Timeout for DMA read operation */
  #define DMA_READ_REQUEST_ONE_TICK   (1U)      /* 1 tick = 1 us */
  #define DMA_READ_REQUEST_TIMEOUT    (25000UL) /* (25000 * tick)us = 25 ms ( TDRQCMPLTND / 2 ) */
  
  #define DMA_WRITE_REQUEST_ONE_TICK   (1U)      /* 1 tick = 1 us */
  #define DMA_WRITE_REQUEST_TIMEOUT    (25000UL) /* (25000 * tick)us = 25 ms ( TDRQCMPLTND / 2 ) */
  
  
  /*******************************************************************************
  *                        Internal Functions Prototypes
  *******************************************************************************/
  
  static void DmaEndpointInit1D(cy_stc_dma_descriptor_t *descr, 
                                bool inDirection, 
                                cy_en_dma_data_size_t dataSize, 
                                volatile uint32_t const *dataReg);
  
  static void DmaEndpointInit2D(cy_stc_dma_descriptor_t *descr, 
                                bool inDirection, 
                                int32_t numElements);
  
  static void DmaEndpointSetLength(bool inDirection, 
                                   uint32_t size, 
                                   cy_stc_usbfs_dev_drv_endpoint_data_t *endpoint);
  
  
  /*******************************************************************************
  *                            Internal Constants
  *******************************************************************************/
  
  /* Used for getting data in X loops */
  #define DMA_DESCR_1D_CFG \
  {                        \
      /* .retrigger      = */ CY_DMA_RETRIG_IM,        \
      /* .interruptType  = */ CY_DMA_DESCR,            \
      /* .triggerOutType = */ CY_DMA_DESCR,            \
      /* .channelState   = */ CY_DMA_CHANNEL_DISABLED, \
      /* .triggerInType  = */ CY_DMA_DESCR,            \
      /* .dataSize        = */ CY_DMA_BYTE,               \
      /* .srcTransferSize = */ CY_DMA_TRANSFER_SIZE_WORD, \
      /* .dstTransferSize = */ CY_DMA_TRANSFER_SIZE_WORD, \
      /* .descriptorType  = */ CY_DMA_1D_TRANSFER,        \
      /* .srcAddress     = */ NULL, \
      /* .dstAddress     = */ NULL, \
      /* .srcXincrement  = */ 0,    \
      /* .dstXincrement  = */ 0,    \
      /* .xCount         = */ 1UL,  \
      /* .srcYincrement  = */ 0,    \
      /* .dstYincrement  = */ 0,    \
      /* .yCount         = */ 1UL,  \
      /* .nextDescriptor = */ NULL, \
  }
  
  /* Used for getting data in Y loops */
  #define DMA_DESCR_2D_CFG \
  {                        \
      /* .retrigger      = */ CY_DMA_RETRIG_IM,       \
      /* .interruptType  = */ CY_DMA_X_LOOP,          \
      /* .triggerOutType = */ CY_DMA_X_LOOP,          \
      /* .channelState   = */ CY_DMA_CHANNEL_ENABLED, \
      /* .triggerInType  = */ CY_DMA_X_LOOP,          \
      /* .dataSize        = */ CY_DMA_BYTE,               \
      /* .srcTransferSize = */ CY_DMA_TRANSFER_SIZE_WORD, \
      /* .dstTransferSize = */ CY_DMA_TRANSFER_SIZE_WORD, \
      /* .descriptorType  = */ CY_DMA_2D_TRANSFER,        \
      /* .srcAddress     = */ NULL, \
      /* .dstAddress     = */ NULL, \
      /* .srcXincrement  = */ 0,    \
      /* .dstXincrement  = */ 0,    \
      /* .xCount         = */ 1UL,  \
      /* .srcYincrement  = */ 0,    \
      /* .dstYincrement  = */ 0,    \
      /* .yCount         = */ 1UL,  \
      /* .nextDescriptor = */ NULL, \
  }
  
  
  /*******************************************************************************
  * Function Name: DmaInit
  ****************************************************************************//**
  *
  * Initializes all DMA channels used by the USBFS Device.
  *
  * \param config
  * The pointer to the driver configuration structure \ref cy_stc_usbfs_dev_drv_config_t.
  *
  * \param context
  * The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
  * allocated by the user. The structure is used during the USBFS Device 
  * operation for internal configuration and data retention. The user must not 
  * modify anything in this structure.
  *
  * \return
  * Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
  *
  *******************************************************************************/
  cy_en_usbfs_dev_drv_status_t DmaInit(cy_stc_usbfs_dev_drv_config_t const *config, cy_stc_usbfs_dev_drv_context_t *context)
  {
      cy_en_usbfs_dev_drv_status_t retStatus = CY_USBFS_DEV_DRV_DMA_CFG_FAILED;
  
      uint32_t endpoint;
  
      /* Configure DMA descriptors and channels for data endpoints */
      for (endpoint = 0UL; endpoint < CY_USBFS_DEV_DRV_NUM_EPS_MAX; ++endpoint)
      {
          /* DMA configuration status for endpoint */
          retStatus = CY_USBFS_DEV_DRV_DMA_CFG_FAILED;
  
          /* Get pointer to endpoint data */
          cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];
  
          if (config->dmaConfig[endpoint] != NULL)
          {
              cy_en_usbfs_dev_drv_status_t locStatus = CY_USBFS_DEV_DRV_DMA_CFG_FAILED;
  
              cy_stc_dma_channel_config_t chConfig;
              
              /* Descriptors configurations */
              const cy_stc_dma_descriptor_config_t DmaDescr1DCfg = DMA_DESCR_1D_CFG;
              const cy_stc_dma_descriptor_config_t DmaDescr2DCfg = DMA_DESCR_2D_CFG;
  
              /* Store DMA configuration required for operation */
              endpointData->base   = config->dmaConfig[endpoint]->base;
              endpointData->chNum  = config->dmaConfig[endpoint]->chNum;
              endpointData->descr0 = config->dmaConfig[endpoint]->descr0;
              endpointData->descr1 = config->dmaConfig[endpoint]->descr1;
              endpointData->outTrigMux = config->dmaConfig[endpoint]->outTrigMux;
              endpointData->copyData   = NULL;
  
              if (CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA == context->mode)
              {
                  if (NULL != endpointData->descr0)
                  {
                      /* Initialize DMA descriptor 0 for 1D operation. 
                      * Discard return because descriptor configuration (defined in driver) is valid.
                      */
                      (void) Cy_DMA_Descriptor_Init(endpointData->descr0, &DmaDescr1DCfg);
  
                      locStatus = CY_USBFS_DEV_DRV_SUCCESS;
                  }
              }
              else
              {
                  if ((NULL != endpointData->descr0) && (NULL != endpointData->descr1))
                  {
                      /* Initialize DMA descriptor 0 for 2D operation. 
                      * Discard return because descriptor configuration (defined in driver) is valid.
                      */
                      (void) Cy_DMA_Descriptor_Init(endpointData->descr0, &DmaDescr2DCfg);
                      
  
                      /* Initialize DMA descriptor 0 for 1D operation. 
                      * Discard return because descriptor configuration (defined in driver) is valid.
                      */
                      (void)Cy_DMA_Descriptor_Init(endpointData->descr1, &DmaDescr1DCfg);
  
                      locStatus = CY_USBFS_DEV_DRV_SUCCESS;
                  }
              }
  
              /* Set DMA channel configuration */
              chConfig.enable      = false;
              chConfig.bufferable  = false;
              chConfig.descriptor  = config->dmaConfig[endpoint]->descr0;
              chConfig.preemptable = config->dmaConfig[endpoint]->preemptable;
              chConfig.priority    = config->dmaConfig[endpoint]->priority;
  
              /* Initialize DMA channel */
              if ((CY_DMA_SUCCESS != Cy_DMA_Channel_Init(endpointData->base, endpointData->chNum, &chConfig)) ||
                  (CY_USBFS_DEV_DRV_SUCCESS != locStatus))
              {
                  break;
              }
  
              /* Enable DMA block */
              Cy_DMA_Enable(endpointData->base);
          }
          else
          {
              endpointData->chNum = DMA_INVALID_CHANNEL;
          }
  
          /* Configuration complete successfully */
          retStatus = CY_USBFS_DEV_DRV_SUCCESS;
      }
  
      /* Disable all DMAs if any configuration failed */
      if (CY_USBFS_DEV_DRV_SUCCESS != retStatus)
      {
          DmaDisable(context);
      }
  
      return retStatus;
  }
  
  
  /*******************************************************************************
  * Function Name: DmaDisable
  ****************************************************************************//**
  *
  * Disables all DMA channels used by the USBFS Device. 
  * The channel state is not verified before being disabled.
  *
  * \param context
  * The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
  * allocated by the user. The structure is used during the USBFS Device 
  * operation for internal configuration and data retention. The user must not 
  * modify anything in this structure.
  *
  *******************************************************************************/
  void DmaDisable(cy_stc_usbfs_dev_drv_context_t *context)
  {
      uint32_t endpoint;
  
      /* Disable DMA channels for data endpoints */
      for (endpoint = 0UL; endpoint < CY_USBFS_DEV_DRV_NUM_EPS_MAX; ++endpoint)
      {
          /* Get pointer to endpoint data */
          cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];
  
          if (endpointData->chNum != DMA_INVALID_CHANNEL)
          {
              Cy_DMA_Channel_Disable(endpointData->base, endpointData->chNum);
          }
      }
  }
  
  
  /*******************************************************************************
  * Function Name: DmaEndpointInit1D
  ****************************************************************************//**
  *
  * Configures DMA 1D descriptor used in DMA modes.
  *
  * \param descr
  * The pointer to the DMA descriptor.
  *
  * \param dataSize
  * The DMA transfer data size \ref cy_en_dma_data_size_t.
  *
  * \param dataReg
  * The pointer to the data endpoint data register.
  *
  *******************************************************************************/
  static void DmaEndpointInit1D(cy_stc_dma_descriptor_t *descr, bool inDirection, 
                                cy_en_dma_data_size_t dataSize, volatile uint32_t const *dataReg)
  {
      Cy_DMA_Descriptor_SetDataSize(descr, dataSize);
  
      if (inDirection)
      {
          Cy_DMA_Descriptor_SetSrcTransferSize(descr, CY_DMA_TRANSFER_SIZE_DATA);
          Cy_DMA_Descriptor_SetDstTransferSize(descr, CY_DMA_TRANSFER_SIZE_WORD);
  
          Cy_DMA_Descriptor_SetXloopSrcIncrement(descr, DMA_XLOOP_INCREMENT);
          Cy_DMA_Descriptor_SetXloopDstIncrement(descr, DMA_NO_INCREMENT);
  
          Cy_DMA_Descriptor_SetDstAddress(descr, (void const *) dataReg);
      }
      else
      {
          Cy_DMA_Descriptor_SetSrcTransferSize(descr, CY_DMA_TRANSFER_SIZE_WORD);
          Cy_DMA_Descriptor_SetDstTransferSize(descr, CY_DMA_TRANSFER_SIZE_DATA);
  
          Cy_DMA_Descriptor_SetXloopSrcIncrement(descr, DMA_NO_INCREMENT);
          Cy_DMA_Descriptor_SetXloopDstIncrement(descr, DMA_XLOOP_INCREMENT);
  
          Cy_DMA_Descriptor_SetSrcAddress(descr, (void const *) dataReg);
      }
  
      /* Link descriptor to itself */
      Cy_DMA_Descriptor_SetNextDescriptor(descr, descr);    
  }
  
  
  /*******************************************************************************
  * Function Name: DmaEndpointInit2D
  ****************************************************************************//**
  *
  * Configures DMA 2D descriptor used in the DMA Automatic mode.
  *
  * \param descr
  * The pointer to the DMA descriptor.
  *
  * \param inDirection
  * Endpoint direction associated with DMA descriptor.
  *
  * \param numElements
  * Number of elements to transfer.
  *
  *******************************************************************************/
  static void DmaEndpointInit2D(cy_stc_dma_descriptor_t *descr, bool inDirection, 
                                int32_t numElements)
  {
      /* Descriptor 0 (2D): it transfers number of data elements (X loop count) 
      * and increments source/destination (depends on direction) by this amount 
      * (Y loop increment).
      */
      Cy_DMA_Descriptor_SetXloopDataCount(descr, (uint32_t) numElements);
  
      if (inDirection)
      {
          Cy_DMA_Descriptor_SetYloopSrcIncrement(descr, numElements);
          Cy_DMA_Descriptor_SetYloopDstIncrement(descr, DMA_NO_INCREMENT);
      }
      else
      {
          Cy_DMA_Descriptor_SetYloopSrcIncrement(descr, DMA_NO_INCREMENT);
          Cy_DMA_Descriptor_SetYloopDstIncrement(descr, numElements);
      }
  }
  
  
  /*******************************************************************************
  * Function Name: DmaEndpointSetLength
  ****************************************************************************//**
  *
  * Completes DMA initialization for the OUT data endpoint.
  * Applicable only when mode is \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO.
  *
  * \param endpoint
  * The pointer to the structure that stores endpoint information.
  *
  *******************************************************************************/
  static void DmaEndpointSetLength(bool inDirection, uint32_t size, 
                                   cy_stc_usbfs_dev_drv_endpoint_data_t *endpoint)
  {
      uint8_t *buf = endpoint->buffer;
      
      /*
      * Descriptor 0: get number of Y loops. It transfers data in multiples of 32 bytes.
      * Descriptor 1: get number of X loops. It transfers data what was left 1-31 bytes.
      */
      uint32_t numYloops = size / (uint32_t) DMA_YLOOP_INCREMENT;
      uint32_t numXloops = size % (uint32_t) DMA_YLOOP_INCREMENT;
  
      /* Configure */
      if (inDirection)
      {
          endpoint->startBuf = (uint16_t) size;  /* Store DMA transfer size */
          Cy_DMA_Descriptor_SetSrcAddress(endpoint->descr0, buf);
          Cy_DMA_Descriptor_SetSrcAddress(endpoint->descr1, &buf[size - numXloops]);
      }
      else
      {
          endpoint->startBuf = (numYloops > 0U) ? 1U : 0U; /* Store 1st DMA descriptor to execute */
          Cy_DMA_Descriptor_SetDstAddress(endpoint->descr0, buf);
          Cy_DMA_Descriptor_SetDstAddress(endpoint->descr1, &buf[size - numXloops]);
      }
  
      /* Configure loop length */
      if (numYloops > 0UL)
      {
          Cy_DMA_Descriptor_SetYloopDataCount(endpoint->descr0, numYloops);
      }
  
      if (numXloops > 0UL)
      {        
          Cy_DMA_Descriptor_SetXloopDataCount(endpoint->descr1, numXloops);
      }
  
      /* Chain descriptors to operate */
      if (numYloops == 0UL)
      {
          /* (Size < 32): only Descriptor 1 transfers data */
          Cy_DMA_Descriptor_SetNextDescriptor(endpoint->descr1, endpoint->descr1);
      }
      else if (numXloops == 0UL)
      {
          /* Size multiple of 32: only Descriptor 0 transfers data */
          Cy_DMA_Descriptor_SetNextDescriptor(endpoint->descr0, endpoint->descr0);
      }
      else
      {
          /* (Size > 32): both Descriptor 0 and 1 transfer data */
          Cy_DMA_Descriptor_SetNextDescriptor(endpoint->descr0, endpoint->descr1);
          Cy_DMA_Descriptor_SetNextDescriptor(endpoint->descr1, endpoint->descr0);
      }
      
      /* Keep channel enabled after execution of Descriptor 0 to execute Descriptor 1 */
      Cy_DMA_Descriptor_SetChannelState(endpoint->descr0,
                                          ((numYloops > 0UL) && (numXloops > 0UL)) ?
                                              CY_DMA_CHANNEL_ENABLED : CY_DMA_CHANNEL_DISABLED);
      
      /* Start execution from Descriptor 0 (length >= 32) or Descriptor 1 (length < 32) */
      Cy_DMA_Channel_SetDescriptor(endpoint->base, endpoint->chNum,
                                      ((numYloops > 0UL) ? endpoint->descr0 : endpoint->descr1));
      
      /* Configuration complete: enable channel */
      Cy_DMA_Channel_Enable(endpoint->base, endpoint->chNum);
  }
  
        
  /*******************************************************************************
  * Function Name: DmaOutEndpointRestore
  ****************************************************************************//**
  *   
  * Restores the DMA channel after transfer is completed for the the OUT data endpoint.
  * Applicable only when mode is \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO.
  *
  * \param endpoint
  * The pointer to the structure that stores endpoint information.
  *
  *******************************************************************************/
  void DmaOutEndpointRestore(cy_stc_usbfs_dev_drv_endpoint_data_t *endpoint)
  {
      /* Number of clocks for DMA completion pulse */
      const uint32_t CY_USBFS_DEV_DRV_TRIG_CYCLES = 2UL;
  
      /* Define which descriptor is 1st in the chain */
      bool setDescr0 = (0U != endpoint->startBuf);
  
      /* Channel disable aborts on-going transfer */
      Cy_DMA_Channel_Disable(endpoint->base, endpoint->chNum);
  
      /* Send pulse to UBSFS IP to indicate end of DMA transfer */
      (void) Cy_TrigMux_SwTrigger(endpoint->outTrigMux, CY_USBFS_DEV_DRV_TRIG_CYCLES);
  
      /* Set 1st DMA descriptor for the following transfer */
      Cy_DMA_Channel_SetDescriptor(endpoint->base, endpoint->chNum,
                                                  (setDescr0 ? endpoint->descr0 : endpoint->descr1));
  
      Cy_DMA_Channel_Enable(endpoint->base, endpoint->chNum);
  }
  
  
  /*******************************************************************************
  * Function Name: DmaEndpointInit
  ****************************************************************************//**
  *
  * Initializes DMA descriptor for a certain data endpoint.
  *
  * \param base
  * The pointer to the USBFS instance.
  *
  * \param endpointAddr
  * The data endpoint address (7 bit - direction, 3-0 bits - endpoint number).
  *
  * \param context
  * The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
  * allocated by the user. The structure is used during the USBFS Device 
  * operation for internal configuration and data retention. The user must not 
  * modify anything in this structure.
  *
  * \return
  * Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
  *
  *******************************************************************************/
  cy_en_usbfs_dev_drv_status_t DmaEndpointInit(USBFS_Type *base, 
                                               cy_en_usbfs_dev_drv_ep_management_mode_t mode, 
                                               bool useReg16, 
                                               cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData)
  {
      cy_en_dma_data_size_t regSize;
      int32_t numElements;
      uint32_t volatile *dataReg;
  
      /* Get direction and endpoint number */
      bool inDirection  = IS_EP_DIR_IN(endpointData->address);
      uint32_t endpoint = EPADDR2PHY(endpointData->address);
  
      if (DMA_INVALID_CHANNEL == endpointData->chNum)
      {
          return CY_USBFS_DEV_DRV_DMA_CFG_FAILED;
      }
  
      /* Set configuration variables depending on access type */
      if (useReg16)
      {
          dataReg     = Cy_USBFS_Dev_Drv_GetDataReg16Addr(base, endpoint);
          regSize     = CY_DMA_HALFWORD;
          numElements = (DMA_YLOOP_INCREMENT / 2);
      }
      else
      {
          dataReg     = Cy_USBFS_Dev_Drv_GetDataRegAddr(base, endpoint);
          regSize     = CY_DMA_BYTE;
          numElements = DMA_YLOOP_INCREMENT;
      }
  
      /* Disable channel before configuration */
      Cy_DMA_Channel_Disable(endpointData->base, endpointData->chNum);
  
      /* Configure Descriptor 0 for 1D operation */
      DmaEndpointInit1D(endpointData->descr0, inDirection, regSize, dataReg);
  
      if (CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO == mode)
      {
          /* Configure Descriptor 0 for 2D operation */
          DmaEndpointInit2D(endpointData->descr0, inDirection, numElements);
  
          /* Configure Descriptor 1 for 1D operation */
          DmaEndpointInit1D(endpointData->descr1, inDirection, regSize, dataReg);
  
          /* Configure descriptors to access buffer */
          DmaEndpointSetLength(inDirection, (uint32_t) endpointData->bufferSize, endpointData);
      }
  
      return CY_USBFS_DEV_DRV_SUCCESS;
  }
  
  
  /*******************************************************************************
  * Function Name: DynamicEndpointReConfiguration
  ****************************************************************************//**
  *
  * Changes endpoint direction (IN -> OUT or OUT -> IN).
  * This function is also used to flush IN or OUT endpoint buffer.
  * Applicable only when mode is \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO.
  *
  * \param base
  * The pointer to the USBFS instance.
  *
  * \param inDirection
  * True - endpoint direction is IN; False - endpoint direction is OUT.
  *
  * \param endpoint
  * The data endpoint number.
  * 
  * \return 
  * Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
  *
  *******************************************************************************/
  cy_en_usbfs_dev_drv_status_t DynamicEndpointReConfiguration(USBFS_Type *base,
                                                              bool        inDirection,
                                                              uint32_t    endpoint)
  {
      cy_en_usbfs_dev_drv_status_t retStatus = CY_USBFS_DEV_DRV_EP_DYN_RECONFIG_TIMEOUT;
  
      uint32_t timeout = DYN_RECONFIG_TIMEOUT;
  
      /* Only enabled endpoint can be re-configured */
      if (0U == (USBFS_DEV_EP_ACTIVE(base) & EP2MASK(endpont)))
      {
          return CY_USBFS_DEV_DRV_BUF_ALLOC_FAILED;
      }
  
      /* Request reconfiguration for endpoint */
      USBFS_DEV_DYN_RECONFIG(base) = (USBFS_USBDEV_DYN_RECONFIG_EN_Msk |
                                      _VAL2FLD(USBFS_USBDEV_DYN_RECONFIG_EPNO, endpoint));
  
      /* Cypress ID#295549: execute dummy read */
      (void) USBFS_DEV_DYN_RECONFIG(base);
  
      /* Wait for dynamic re-configuration completion */
      while ((0U == (USBFS_DEV_DYN_RECONFIG(base) & USBFS_USBDEV_DYN_RECONFIG_RDY_STS_Msk)) &&
             (timeout > 0U))
      {
          Cy_SysLib_DelayUs(DYN_RECONFIG_ONE_TICK);
          --timeout;
      }
  
      /* Verify operation result */
      if (timeout > 0U)
      {
          Cy_USBFS_Dev_Drv_SetEpType(base, inDirection, endpoint);
          retStatus = CY_USBFS_DEV_DRV_SUCCESS;
      }
  
      /* Complete endpoint reconfiguration: clear request */
      USBFS_DEV_DYN_RECONFIG(base) &= ~USBFS_USBDEV_DYN_RECONFIG_EN_Msk;
      (void) USBFS_DEV_DYN_RECONFIG(base);
  
      /* Clear register for next re-configuration */
      USBFS_DEV_DYN_RECONFIG(base) = 0U;
      
      return retStatus;
  }
  
  
  /*******************************************************************************
  * Function Name: AddEndpointRamBuffer
  ****************************************************************************//**
  *
  * Implements \ref Cy_USBFS_Dev_Drv_AddEndpoint for 
  * \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO mode.
  *
  * \param base
  * The pointer to the USBFS instance.
  *
  * \param config
  * The pointer to data endpoint configuration \ref cy_stc_usb_dev_ep_config_t.
  *
  * \param context
  * The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
  * allocated by the user. The structure is used during the USBFS Device 
  * operation for internal configuration and data retention. The user must not 
  * modify anything in this structure.
  *
  * \return
  * Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
  *
  *******************************************************************************/
  cy_en_usbfs_dev_drv_status_t AddEndpointRamBuffer(USBFS_Type *base,
                                                    cy_stc_usb_dev_ep_config_t const *config,
                                                    cy_stc_usbfs_dev_drv_context_t   *context)
  {
      cy_en_usbfs_dev_drv_status_t retStatus = CY_USBFS_DEV_DRV_BAD_PARAM;
  
      uint32_t endpoint = EPADDR2PHY(config->endpointAddr);
  
      /* Get pointer to endpoint data */
      cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];
  
      /* Get buffer for endpoint using buffer allocated by the user */
      if (config->allocBuffer)
      {
          uint32_t startBufIdx;
  
          /* Configure active endpoint */
          context->activeEpMask    |= (uint8_t) EP2MASK(endpont);
          USBFS_DEV_EP_ACTIVE(base) = context->activeEpMask;
          
          /* Allocate buffer for endpoint */
          retStatus = GetEndpointBuffer((uint32_t) config->bufferSize, &startBufIdx, context);
          if (CY_USBFS_DEV_DRV_SUCCESS != retStatus)
          {
              return retStatus;
          }
  
          /* Store pointer to buffer for this endpoint */
          endpointData->buffer = &context->epSharedBuf[startBufIdx];
      }
  
      /* Enable endpoint for operation */
      if (config->enableEndpoint)
      {
          bool inDirection = IS_EP_DIR_IN(config->endpointAddr);
          
          /* Clear variables related to endpoint */
          endpointData->state   = CY_USB_DEV_EP_IDLE;
          endpointData->address = config->endpointAddr;
          endpointData->toggle  = 0U;
          endpointData->bufferSize = config->maxPacketSize;
          endpointData->sieMode    = GetEndpointActiveMode(inDirection, config->attributes);
          endpointData->isPending  = false;
  
          /* Set arbiter configuration (clears DMA requests) */
          Cy_USBFS_Dev_Drv_SetArbEpConfig(base, endpoint, (USBFS_USBDEV_ARB_EP1_CFG_CRC_BYPASS_Msk |
                                                           USBFS_USBDEV_ARB_EP1_CFG_RESET_PTR_Msk));
  
          /* Performs dynamic reconfiguration to make sure that the DMA has completed the data transfer. 
          * Also it flushes endpoint pre-fetch buffer (useful for IN endpoints).
          */
          retStatus = DynamicEndpointReConfiguration(base, inDirection, endpoint);
          if (CY_USBFS_DEV_DRV_SUCCESS != retStatus)
          {
              return retStatus;
          }
      
          /* Configure DMA for endpoint */
          retStatus = DmaEndpointInit(base, context->mode, context->useReg16, endpointData);
          if (CY_USBFS_DEV_DRV_SUCCESS != retStatus)
          {
              return retStatus;
          }
          
          /* Enable Arbiter interrupt sources for endpoint */
          Cy_USBFS_Dev_Drv_SetArbEpInterruptMask(base, endpoint,(inDirection ?
                                                               IN_ENDPOINT_ARB_INTR_SOURCES :
                                                               OUT_ENDPOINT_ARB_INTR_SOURCES));
  
          /* Enable SIE and arbiter interrupt for endpoint */
          Cy_USBFS_Dev_Drv_EnableSieEpInterrupt(base, endpoint);
          Cy_USBFS_Dev_Drv_EnableArbEpInterrupt(base, endpoint);
  
          /* Set SIE mode to respond to host */
          Cy_USBFS_Dev_Drv_SetSieEpMode(base, endpoint, GetEndpointInactiveMode((uint32_t) endpointData->sieMode));
      }
  
      return retStatus;
  }
  
  
  /*******************************************************************************
  * Function Name: RestoreEndpointRamBuffer
  ****************************************************************************//**
  *
  * Restores endpoint active configuration for 
  * \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO mode.
  *
  * \param base
  * The pointer to the USBFS instance.
  *
  * \param endpointData
  * The pointer to the endpoint data structure.
  *
  * \param context
  * The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
  * allocated by the user. The structure is used during the USBFS Device 
  * operation for internal configuration and data retention. The user must not 
  * modify anything in this structure.
  *
  *******************************************************************************/
  void RestoreEndpointRamBuffer(USBFS_Type *base, 
                                cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData)
  {
      bool inDirection  = IS_EP_DIR_IN(endpointData->address);
      uint32_t endpoint = EPADDR2PHY(endpointData->address);
  
      /* Clear state */
      endpointData->state = CY_USB_DEV_EP_IDLE;
  
      /* Configure active endpoint */
      USBFS_DEV_EP_ACTIVE(base) |= EP2MASK(endpont);
  
      /* Configure endpoint type: OUT - 1, IN - 0 */
      Cy_USBFS_Dev_Drv_SetEpType(base, inDirection, endpoint);
  
      /* Enable Arbiter interrupt sources for endpoint */
      Cy_USBFS_Dev_Drv_SetArbEpInterruptMask(base, endpoint, (inDirection ?
                                                              IN_ENDPOINT_ARB_INTR_SOURCES :
                                                              OUT_ENDPOINT_ARB_INTR_SOURCES));
      
      /* Enable SIE and arbiter interrupt for endpoint */
      Cy_USBFS_Dev_Drv_EnableSieEpInterrupt(base, endpoint);
      Cy_USBFS_Dev_Drv_EnableArbEpInterrupt(base, endpoint);
  
      if (false == inDirection)
      {
          /* OUT Endpoint: enable DMA channel endpoint ready for operation. 
          * IN Endpoint: keep disabled, it is enabled in LoadInEndpointDmaAuto.
          */
          Cy_DMA_Channel_Enable(endpointData->base, endpointData->chNum);
      }
  
      /* Sets an arbiter configuration */
      Cy_USBFS_Dev_Drv_SetArbEpConfig(base, endpoint, (USBFS_USBDEV_ARB_EP1_CFG_CRC_BYPASS_Msk |
                                                       USBFS_USBDEV_ARB_EP1_CFG_RESET_PTR_Msk));
  
      /* Set SIE mode to respond to host */
      Cy_USBFS_Dev_Drv_SetSieEpMode(base, endpoint, GetEndpointInactiveMode((uint32_t) endpointData->sieMode));
  }
  
  
  /*******************************************************************************
  * Function Name: LoadInEndpointDma
  ****************************************************************************//**
  *
  * Implements \ref Cy_USBFS_Dev_Drv_LoadInEndpoint for 
  * \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA mode.
  *
  * \param base
  * The pointer to the USBFS instance.
  *
  * \param endpoint
  * The IN data endpoint number.
  *
  * \param buffer
  * The pointer to the buffer containing data bytes to load.
  *
  * \param size
  * The number of bytes to load into the endpoint.
  * This value must be less than or equal to the endpoint maximum packet size.
  *
  * \param context
  * The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
  * allocated by the user. The structure is used during the USBFS Device 
  * operation for internal configuration and data retention. The user must not 
  * modify anything in this structure.
  *
  * \return
  * Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
  *
  *******************************************************************************/
  cy_en_usbfs_dev_drv_status_t LoadInEndpointDma(USBFS_Type    *base,
                                                 uint32_t      endpoint,
                                                 uint8_t const *buffer,
                                                 uint32_t      size,
                                                 cy_stc_usbfs_dev_drv_context_t *context)
  {
      cy_en_usbfs_dev_drv_status_t retStatus = CY_USBFS_DEV_DRV_EP_DMA_WRITE_TIMEOUT;
  
      /* Get pointer to endpoint data */
      cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];
  
      /* Request to load more bytes than endpoint buffer */
      if (size > endpointData->bufferSize)
      {
          return CY_USBFS_DEV_DRV_BAD_PARAM;
      }
  
      /* Clears the abort mask for the endpoint (there is no transfer during abort) */
      context->epAbortMask &= (uint8_t) ~EP2MASK(endpoint);
  
      /* Set count and data toggle */
      Cy_USBFS_Dev_Drv_SetSieEpCount(base, endpoint, size, (uint32_t) endpointData->toggle);
  
      if (0U == size)
      {
          /* Endpoint pending: Waits for the host read data after exiting this function */
          endpointData->state = CY_USB_DEV_EP_PENDING;
  
          /* Arm endpoint: Host is allowed to read data */
          Cy_USBFS_Dev_Drv_SetSieEpMode(base, endpoint, (uint32_t) endpointData->sieMode);
  
          retStatus = CY_USBFS_DEV_DRV_SUCCESS;
      }
      else
      {
          /* Channel is disabled after initialization or descriptor completion  */
      
          uint32_t timeout = DMA_WRITE_REQUEST_TIMEOUT;
  
          /* Get number of data elements to transfer */
          size = context->useReg16 ? GET_SIZE16(size) : size;
  
          /* 1D descriptor: configure source address and length */
          Cy_DMA_Descriptor_SetSrcAddress    (endpointData->descr0, (const void*) buffer);
          Cy_DMA_Descriptor_SetXloopDataCount(endpointData->descr0, size);
  
          /* Enable DMA channel: configuration complete */
          Cy_DMA_Channel_Enable(endpointData->base, endpointData->chNum);
  
          /* Generate DMA request: the endpoint will be armed when the DMA is 
          * finished in the Arbiter interrupt 
          */
          Cy_USBFS_Dev_Drv_TriggerArbCfgEpDmaReq(base, endpoint);
  
          /* Waits until DMA completes the write operation. The current endpoint state is 
          * idle or completed and DMA completion interrupt changes state to pending 
          * (endpoint waits for the host read data).
          */
          while ((CY_USB_DEV_EP_PENDING != endpointData->state) && 
                 (timeout > 0U))
          {
              Cy_SysLib_DelayUs(DMA_WRITE_REQUEST_ONE_TICK);
              --timeout;
          }
  
          /* Check timeout */
          if (timeout > 0U)
          {
              retStatus = CY_USBFS_DEV_DRV_SUCCESS;
          }
      }
  
      return retStatus;
  }
  
  
  /*******************************************************************************
  * Function Name: ReadOutEndpointDma
  ****************************************************************************//**
  *
  * Implements \ref Cy_USBFS_Dev_Drv_ReadOutEndpoint for 
  * \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA mode.
  *
  * \param base
  * The pointer to the USBFS instance.
  *
  * \param endpoint
  * The OUT data endpoint number.
  *
  * \param buffer
  * Pointer to buffer that stores data that was read.
  *
  * \param size
  * The number of bytes to read from endpoint.
  * This value must be less than or equal to the endpoint maximum packet size.
  *
  * \param actSize
  * The number of bytes that were actually read.
  *
  * \param context
  * The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
  * allocated by the user. The structure is used during the USBFS Device 
  * operation for internal configuration and data retention. The user must not 
  * modify anything in this structure.
  *
  * \return
  * Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
  *
  *******************************************************************************/
  cy_en_usbfs_dev_drv_status_t ReadOutEndpointDma(USBFS_Type *base,
                                                  uint32_t   endpoint,
                                                  uint8_t    *buffer,
                                                  uint32_t   size,
                                                  uint32_t   *actSize,
                                                  cy_stc_usbfs_dev_drv_context_t *context)
  {
      cy_en_usbfs_dev_drv_status_t retStatus = CY_USBFS_DEV_DRV_EP_DMA_READ_TIMEOUT;
  
      /* Get pointer to endpoint data */
      cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];
  
      uint32_t timeout = DMA_READ_REQUEST_TIMEOUT;
  
      /* Get number of received bytes */
      uint32_t numToCopy = Cy_USBFS_Dev_Drv_GetSieEpCount(base, endpoint);
      uint32_t actCopied = numToCopy;
  
      /* Initialize actual number of copied bytes */
      *actSize = 0U;
  
      /* Endpoint received more bytes than provided buffer */
      if (numToCopy > size)
      {
          return CY_USBFS_DEV_DRV_BAD_PARAM;
      }
  
      /* Nothing to copy; return success */
      if (0U == numToCopy)
      {
          return CY_USBFS_DEV_DRV_SUCCESS;
      }
      
      /* Channel is disabled after initialization or descriptor completion  */
  
      /* Get number of data elements to transfer */
      numToCopy = context->useReg16 ? GET_SIZE16(numToCopy) : numToCopy;
  
      /* 1D descriptor: configure destination address and length */
      Cy_DMA_Descriptor_SetDstAddress    (endpointData->descr0, buffer);
      Cy_DMA_Descriptor_SetXloopDataCount(endpointData->descr0, numToCopy);
  
      /* Enable DMA channel: configuration complete */
      Cy_DMA_Channel_Enable(endpointData->base, endpointData->chNum);
  
      /* The current endpoint state is completed, changes the state to pending to
      * track DMA read completion.
      */
      endpointData->state = CY_USB_DEV_EP_PENDING;
  
      /* Generate DMA request to read data from hardware buffer */
      Cy_USBFS_Dev_Drv_TriggerArbCfgEpDmaReq(base, endpoint);
  
      /* Waits until DMA completes the read operation */
      while ((CY_USB_DEV_EP_COMPLETED != endpointData->state) &&
             (timeout > 0U))
      {
          Cy_SysLib_DelayUs(DMA_READ_REQUEST_ONE_TICK);
          --timeout;
      }
  
      /* Check timeout */
      if (timeout > 0U)
      {
          /* Update number of copied bytes */
          *actSize = actCopied;
          retStatus = CY_USBFS_DEV_DRV_SUCCESS;
      }
  
      return retStatus;
  }
  
  
  /*******************************************************************************
  * Function Name: LoadInEndpointDmaAuto
  ****************************************************************************//**
  *
  * Implements \ref Cy_USBFS_Dev_Drv_LoadInEndpoint for 
  * \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO mode.
  *
  * \param base
  * The pointer to the USBFS instance.
  *
  * \param endpoint
  * The IN data endpoint number.
  *
  * \param buffer
  * The pointer to the buffer containing data bytes to load.
  *
  * \param size
  * The number of bytes to load into endpoint.
  * This value must be less than or equal to the endpoint maximum packet size.
  *
  * \param context
  * The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
  * allocated by the user. The structure is used during the USBFS Device 
  * operation for internal configuration and data retention. The user must not 
  * modify anything in this structure.
  *
  * \return
  * Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
  *
  *******************************************************************************/
  cy_en_usbfs_dev_drv_status_t LoadInEndpointDmaAuto(USBFS_Type    *base,
                                                     uint32_t      endpoint,
                                                     uint8_t const *buffer,
                                                     uint32_t      size,
                                                     cy_stc_usbfs_dev_drv_context_t *context)
  {
      /* Get pointer to endpoint data */
      cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];
  
      /* Request to load more bytes than endpoint buffer */
      if (size > endpointData->bufferSize)
      {
          return CY_USBFS_DEV_DRV_BAD_PARAM;
      }
  
      /* Clears the abort mask for the endpoint (there is no transfer during abort) */
      context->epAbortMask &= (uint8_t) ~EP2MASK(endpoint);
  
      /* Endpoint pending: Waits for the host read data after exiting this function */
      endpointData->state = CY_USB_DEV_EP_PENDING;
      
      /* Set count and data toggle */
      Cy_USBFS_Dev_Drv_SetSieEpCount(base, endpoint, size, (uint32_t) endpointData->toggle);
  
      if (0U == size)
      {
          /* Arm endpoint: Host is allowed to read data */
          Cy_USBFS_Dev_Drv_SetSieEpMode(base, endpoint, (uint32_t) endpointData->sieMode);
      }
      else
      {
          /* Copy data from user buffer to internal endpoint buffer */
          if (NULL != endpointData->copyData)
          {
              endpointData->copyData(endpointData->buffer, buffer, size);
          }
          else
          {
              (void) memcpy(endpointData->buffer, buffer, size);
          }
  
          /* Configure transfer length */
          if (size != endpointData->startBuf)
          {
              /* Update transfer length, endpoint startBuf and enables DMA */
              DmaEndpointSetLength(true, size, &context->epPool[endpoint]);
          }
          else
          {
              /* Reset DMA channel indexes, they keep value after Resume or Abort */
              Cy_DMA_Channel_SetDescriptor(endpointData->base, endpointData->chNum, 
                                              (endpointData->startBuf >= (uint32_t) DMA_YLOOP_INCREMENT) ? 
                                                  endpointData->descr0 : endpointData->descr1);
  
              /* Enable channel: configuration complete */
              Cy_DMA_Channel_Enable(endpointData->base, endpointData->chNum);
          }
  
          /* Generate DMA request to pre-load data into endpoint buffer */
          Cy_USBFS_Dev_Drv_SetArbCfgEpInReady(base, endpoint);
  
          /* IN endpoint will be armed in the Arbiter interrupt (source: IN_BUF_FULL)
          * after DMA pre-load data buffer.
          */
      }
  
      return CY_USBFS_DEV_DRV_SUCCESS;
  }
  
  
  /*******************************************************************************
  * Function Name: ReadOutEndpointDmaAuto
  ****************************************************************************//**
  *
  * Implements \ref Cy_USBFS_Dev_Drv_ReadOutEndpoint for 
  * \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO mode.
  *
  * \param base
  * The pointer to the USBFS instance.
  *
  * \param endpoint
  * The OUT data endpoint number.
  *
  * \param buffer
  * Pointer to buffer that stores data that was read.
  *
  * \param size
  * The number of bytes to read from the endpoint.
  * This value must be less than or equal to the endpoint maximum packet size.
  *
  * \param actSize
  * The number of bytes which were actually read.
  *
  * \param context
  * The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
  * allocated by the user. The structure is used during the USBFS Device 
  * operation for internal configuration and data retention. The user must not 
  * modify anything in this structure.
  *
  * \return
  * Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
  *
  *******************************************************************************/
  cy_en_usbfs_dev_drv_status_t ReadOutEndpointDmaAuto(USBFS_Type *base,
                                                      uint32_t   endpoint,
                                                      uint8_t    *buffer,
                                                      uint32_t   size,
                                                      uint32_t   *actSize,
                                                      cy_stc_usbfs_dev_drv_context_t *context)
  {
      /* Get pointer to endpoint data */
      cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];
      
      /* Get number of received bytes */
      uint32_t numToCopy = Cy_USBFS_Dev_Drv_GetSieEpCount(base, endpoint);
      
      /* Initialize actual number of copied bytes */
      *actSize = 0U;
      
      /* Endpoint received more bytes than provided buffer */
      if (numToCopy > size)
      {
          return CY_USBFS_DEV_DRV_BAD_PARAM;
      }
  
      /* Nothing to copy (zero length packet) return success */
      if (0U == numToCopy)
      {
          return CY_USBFS_DEV_DRV_SUCCESS;
      }
  
      /* Copy data from user buffer to internal endpoint buffer */
      if (NULL != endpointData->copyData)
      {
          (void) endpointData->copyData(buffer, endpointData->buffer, numToCopy);
      }
      else
      {
          (void) memcpy(buffer, endpointData->buffer, numToCopy);
      }
      
      /* Update number of copied bytes */
      *actSize = numToCopy;    
      
      return CY_USBFS_DEV_DRV_SUCCESS;
  }
  
  
  #if defined(__cplusplus)
  }
  #endif
  
  #endif /* CY_IP_MXUSBFS */
  
  
  /* [] END OF FILE */
  