/***********************************************************************
* $Id:: mw_usbd_core.c 332 2012-08-09 19:00:53Z usb10131                      $
*
* Project: USB device ROM Stack
*
* Description:
*     USB Core Module.
*
***********************************************************************
*   Copyright(C) 2011, NXP Semiconductor
*   All rights reserved.
*
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* products. This software is supplied "AS IS" without any warranties.
* NXP Semiconductors assumes no responsibility or liability for the
* use of the software, conveys no license or title under any patent,
* copyright, or mask work right to the product. NXP Semiconductors
* reserves the right to make changes in the software without
* notification. NXP Semiconductors also make no representation or
* warranty that such application will be suitable for the specified
* use without further testing or modification.
**********************************************************************/

#include <string.h>
#include "mw_usbd.h"
#include "mw_usbd_hw.h"
#include "mw_usbd_core.h"
#include "mw_usbd_desc.h"


#ifndef FALSE
#define FALSE 0
#define TRUE !FALSE
#endif

/* forward function declarations */
ErrorCode_t USB_InvokeEp0Hdlrs (USB_CORE_CTRL_T* pCtrl, uint32_t event);

/*
*  Reset USB Core
*    Parameters:      None
*    Return Value:    None
*/

void mwUSB_ResetCore (USBD_HANDLE_T hUsb) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  /* set default as self powered*/
  pCtrl->device_status  = USB_GETSTATUS_SELF_POWERED;
  pCtrl->device_addr = 0;
  pCtrl->config_value = 0;
  /* enable EP0 by default */
  pCtrl->ep_mask  = 0x00010001;
  pCtrl->ep_halt  = 0x00000000;
  pCtrl->ep_stall = 0x00000000;
  /* inform all calss handler about reset event*/
  USB_InvokeEp0Hdlrs(pCtrl, USB_EVT_RESET);
}


/*
*  USB Request - Setup Stage
*    Parameters:      None (global pCtrl->SetupPacket)
*    Return Value:    None
*/

void mwUSB_SetupStage (USBD_HANDLE_T hUsb) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  hwUSB_ReadSetupPkt(pCtrl, 0x00, (uint32_t *)&pCtrl->SetupPacket);
}


/*
*  USB Request - Data In Stage
*    Parameters:      None (global EP0Data)
*    Return Value:    None
*/

void mwUSB_DataInStage(USBD_HANDLE_T hUsb)
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  uint32_t cnt;

  if (pCtrl->EP0Data.Count > USB_MAX_PACKET0) {
    cnt = USB_MAX_PACKET0;
  } else {
    cnt = pCtrl->EP0Data.Count;
  }
  cnt = hwUSB_WriteEP(pCtrl, 0x80, pCtrl->EP0Data.pData, cnt);
  pCtrl->EP0Data.pData += cnt;
  pCtrl->EP0Data.Count -= cnt;
}


/*
*  USB Request - Data Out Stage
*    Parameters:      None (global pCtrl->EP0Data)
*    Return Value:    None
*/

void mwUSB_DataOutStage(USBD_HANDLE_T hUsb) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  uint32_t cnt;

  cnt = hwUSB_ReadEP(pCtrl, 0x00, pCtrl->EP0Data.pData);
  pCtrl->EP0Data.pData += cnt;
  pCtrl->EP0Data.Count -= cnt;
}


/*
*  USB Request - Status In Stage
*    Parameters:      None
*    Return Value:    None
*/

void mwUSB_StatusInStage(USBD_HANDLE_T hUsb) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  hwUSB_WriteEP(pCtrl, 0x80, 0, 0);
}


/*
*  USB Request - Status Out Stage
*    Parameters:      None
*    Return Value:    None
*/

void mwUSB_StatusOutStage(USBD_HANDLE_T hUsb)
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  hwUSB_ReadEP(pCtrl, 0x00, pCtrl->EP0Buf);
}

/*
*  USB stall ep0 for invalid request
*    Parameters:      None
*    Return Value:    None
*/

void mwUSB_StallEp0(USBD_HANDLE_T hUsb)
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  hwUSB_SetStallEP(pCtrl, 0x80);
  pCtrl->EP0Data.Count = 0;
}

/*
*  Get Status USB Request
*    Parameters:      None (global pCtrl->SetupPacket)
*    Return Value:    TRUE - Success, FALSE - Error
*/

ErrorCode_t USB_ReqGetStatus (USBD_HANDLE_T hUsb) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  uint32_t n, m;
  ErrorCode_t ret = ERR_USBD_INVALID_REQ; 

  switch (pCtrl->SetupPacket.bmRequestType.BM.Recipient) {
    case REQUEST_TO_DEVICE:
      pCtrl->EP0Data.pData = (uint8_t *) & pCtrl->device_status;
      ret = LPC_OK;
      break;
    case REQUEST_TO_INTERFACE:
      if ((pCtrl->config_value != 0) && (pCtrl->SetupPacket.wIndex.WB.L < pCtrl->num_interfaces)) {
        *((uint16_t *)pCtrl->EP0Buf) = 0;
        pCtrl->EP0Data.pData = pCtrl->EP0Buf;
        ret = LPC_OK;
      } 
      break;
    case REQUEST_TO_ENDPOINT:
      n = pCtrl->SetupPacket.wIndex.WB.L & 0x8F;
      m = (n & 0x80) ? ((1 << 16) << (n & 0x0F)) : (1 << n);
      if (((pCtrl->config_value != 0) || ((n & 0x0F) == 0)) && (pCtrl->ep_mask & m)) {
        *((uint16_t *)pCtrl->EP0Buf) = (pCtrl->ep_halt & m) ? 1 : 0;
        pCtrl->EP0Data.pData = pCtrl->EP0Buf;
        ret = LPC_OK;
      } 
      break;
    default:
      break;
  }
  return (ret);
}


/*
*  Set/Clear Feature USB Request
*    Parameters:      sc:    0 - Clear, 1 - Set
*                            (global pCtrl->SetupPacket)
*    Return Value:    TRUE - Success, FALSE - Error
*/

ErrorCode_t USB_ReqSetClrFeature(USBD_HANDLE_T hUsb, uint32_t sc) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  uint32_t n, m;
  ErrorCode_t ret = ERR_USBD_INVALID_REQ; 

  switch (pCtrl->SetupPacket.bmRequestType.BM.Recipient) {
    case REQUEST_TO_DEVICE:
      if (pCtrl->SetupPacket.wValue.W == USB_FEATURE_REMOTE_WAKEUP) {
        if (pCtrl->USB_WakeUpCfg)
          pCtrl->USB_WakeUpCfg(pCtrl, sc);

        if (sc) {
          pCtrl->device_status |=  USB_GETSTATUS_REMOTE_WAKEUP;
        } else {
          pCtrl->device_status &= ~USB_GETSTATUS_REMOTE_WAKEUP;
        }
        ret = LPC_OK;
      } 
      else if (pCtrl->SetupPacket.wValue.W == USB_FEATURE_TEST_MODE) {
        ret = hwUSB_SetTestMode(pCtrl, pCtrl->SetupPacket.wIndex.WB.H);
      } 
      break;
    case REQUEST_TO_ENDPOINT:
      n = pCtrl->SetupPacket.wIndex.WB.L & 0x8F;
      m = (n & 0x80) ? ((1 << 16) << (n & 0x0F)) : (1 << n);
      if ((pCtrl->config_value != 0) && ((n & 0x0F) != 0) && (pCtrl->ep_mask & m)) {
        if (pCtrl->SetupPacket.wValue.W == USB_FEATURE_ENDPOINT_STALL) {
          if (sc) {
            hwUSB_SetStallEP(pCtrl, n);
            pCtrl->ep_halt |=  m;
          } 
          else if ((pCtrl->ep_stall & m) == 0) {
            hwUSB_ClrStallEP(pCtrl, n);
            pCtrl->ep_halt &= ~m;
          }
          ret = LPC_OK;
        } 
      } 
      break;
    //case REQUEST_TO_INTERFACE:
    default:
      break;
  }
  return (ret);
}


/*
*  Set Address USB Request
*    Parameters:      None (global pCtrl->SetupPacket)
*    Return Value:    TRUE - Success, FALSE - Error
*/

ErrorCode_t USB_ReqSetAddress (USB_CORE_CTRL_T* pCtrl) 
{
  if (pCtrl->SetupPacket.bmRequestType.BM.Recipient == REQUEST_TO_DEVICE) {

    pCtrl->device_addr = 0x80 | pCtrl->SetupPacket.wValue.WB.L;
    return (LPC_OK);
  }
  return (ERR_USBD_INVALID_REQ);
}

/*
*  Get Descriptor USB Request
*    Parameters:      None (global pCtrl->SetupPacket)
*    Return Value:    TRUE - Success, FALSE - Error
*/

ErrorCode_t USB_ReqGetDescriptor (USBD_HANDLE_T hUsb) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*) hUsb;
  uint8_t  *pD;
  uint32_t len, n;

  switch (pCtrl->SetupPacket.bmRequestType.BM.Recipient) {
    case REQUEST_TO_DEVICE:
      switch (pCtrl->SetupPacket.wValue.WB.H) {
        case USB_DEVICE_DESCRIPTOR_TYPE:
          pCtrl->EP0Data.pData = (uint8_t *)pCtrl->device_desc;
          len = USB_DEVICE_DESC_SIZE;
          break;
        case USB_CONFIGURATION_DESCRIPTOR_TYPE:
#ifndef FULL_SPEED_ONLY
          if (pCtrl->device_speed == USB_HIGH_SPEED)
            pD = pCtrl->high_speed_desc; 
          else
            pD = pCtrl->full_speed_desc; 
          
          ((USB_CONFIGURATION_DESCRIPTOR *)pD)->bDescriptorType = USB_CONFIGURATION_DESCRIPTOR_TYPE;
#else
            pD = pCtrl->high_speed_desc; 
#endif
          for (n = 0; n != pCtrl->SetupPacket.wValue.WB.L; n++) {
            if (((USB_CONFIGURATION_DESCRIPTOR *)pD)->bLength != 0) {
              pD += ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength;
            }
          }
          if (((USB_CONFIGURATION_DESCRIPTOR *)pD)->bLength == 0) {
            return (ERR_USBD_INVALID_REQ);
          }
          pCtrl->EP0Data.pData = pD;
          len = ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength;
          break;
        case USB_STRING_DESCRIPTOR_TYPE:
          pD = (uint8_t *)pCtrl->string_desc;
          for (n = 0; n != pCtrl->SetupPacket.wValue.WB.L; n++) {
            if (((USB_STRING_DESCRIPTOR *)pD)->bLength != 0) {
              pD += ((USB_STRING_DESCRIPTOR *)pD)->bLength;
            }
          }
          if (((USB_STRING_DESCRIPTOR *)pD)->bLength == 0) {
            return (ERR_USBD_INVALID_REQ);
          }
          pCtrl->EP0Data.pData = pD;
          len = ((USB_STRING_DESCRIPTOR *)pD)->bLength;
          break;
#ifndef FULL_SPEED_ONLY
        case USB_DEVICE_QUALIFIER_DESCRIPTOR_TYPE:
          /* USB Chapter 9. page 9.6.2 */
          if (pCtrl->device_qualifier == 0) /* for USB_FULL_SPEED_ONLY no device qualifier*/
            return (ERR_USBD_INVALID_REQ);

          pCtrl->EP0Data.pData = (uint8_t *)pCtrl->device_qualifier;
          len = USB_DEVICE_QUALI_SIZE;
          break;
        case USB_OTHER_SPEED_CONFIG_DESCRIPTOR_TYPE:
          if (pCtrl->device_qualifier == 0) /* for USB_FULL_SPEED_ONLY no device qualifier*/
            return (ERR_USBD_INVALID_REQ);

          /* select other speed configuration */
          if (pCtrl->device_speed == USB_HIGH_SPEED)
            pD = pCtrl->full_speed_desc; 
          else
            pD = pCtrl->high_speed_desc; 

          for (n = 0; n != pCtrl->SetupPacket.wValue.WB.L; n++) {
            if (((USB_OTHER_SPEED_CONFIGURATION *)pD)->bLength != 0) {
              pD += ((USB_OTHER_SPEED_CONFIGURATION *)pD)->wTotalLength;
            }
          }
          if (((USB_OTHER_SPEED_CONFIGURATION *)pD)->bLength == 0) {
            return (ERR_USBD_INVALID_REQ);
          }
          ((USB_CONFIGURATION_DESCRIPTOR *)pD)->bDescriptorType = USB_OTHER_SPEED_CONFIG_DESCRIPTOR_TYPE;
          pCtrl->EP0Data.pData = pD;
          len = ((USB_OTHER_SPEED_CONFIGURATION *)pD)->wTotalLength;
          break;
#endif
        default:
          return (ERR_USBD_INVALID_REQ);
      }
      break;
    case REQUEST_TO_INTERFACE:
      return (ERR_USBD_INVALID_REQ);
    default:
      return (ERR_USBD_INVALID_REQ);
  }

  if (pCtrl->EP0Data.Count > len) {
    pCtrl->EP0Data.Count = len;
  }

  return (LPC_OK);
}


/*
*  Get Configuration USB Request
*    Parameters:      None (global pCtrl->SetupPacket)
*    Return Value:    TRUE - Success, FALSE - Error
*/

ErrorCode_t USB_ReqGetConfiguration (USBD_HANDLE_T hUsb) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;

  if (pCtrl->SetupPacket.bmRequestType.BM.Recipient == REQUEST_TO_DEVICE) {
    pCtrl->EP0Data.pData = &pCtrl->config_value;
    return (LPC_OK);
  }
  return (ERR_USBD_INVALID_REQ);
}


/*
*  Set Configuration USB Request
*    Parameters:      None (global pCtrl->SetupPacket)
*    Return Value:    TRUE - Success, FALSE - Error
*/

ErrorCode_t USB_ReqSetConfiguration(USBD_HANDLE_T hUsb) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  USB_COMMON_DESCRIPTOR *pD;
  uint32_t alt = 0, n, m;
  uint32_t new_addr;

  switch (pCtrl->SetupPacket.bmRequestType.BM.Recipient) {
    case REQUEST_TO_DEVICE:

      if (pCtrl->SetupPacket.wValue.WB.L) {
        if (pCtrl->device_speed == USB_HIGH_SPEED)
          pD = (USB_COMMON_DESCRIPTOR *)pCtrl->high_speed_desc; 
        else
          pD = (USB_COMMON_DESCRIPTOR *)pCtrl->full_speed_desc; 

        while (pD->bLength) {
          switch (pD->bDescriptorType) {
          case USB_CONFIGURATION_DESCRIPTOR_TYPE:
            if (((USB_CONFIGURATION_DESCRIPTOR *)pD)->bConfigurationValue == pCtrl->SetupPacket.wValue.WB.L) {
              pCtrl->config_value = pCtrl->SetupPacket.wValue.WB.L;
              pCtrl->num_interfaces = ((USB_CONFIGURATION_DESCRIPTOR *)pD)->bNumInterfaces;
              for (n = 0; n < USB_MAX_IF_NUM; n++) {
                pCtrl->alt_setting[n] = 0;
              }
              for (n = 1; n < pCtrl->max_num_ep; n++) {
                if (pCtrl->ep_mask & (1 << n)) {
                  hwUSB_DisableEP(pCtrl, n);
                }
                if (pCtrl->ep_mask & ((1 << 16) << n)) {
                  hwUSB_DisableEP(pCtrl, n | 0x80);
                }
              }
              pCtrl->ep_mask = 0x00010001;
              pCtrl->ep_halt = 0x00000000;
              pCtrl->ep_stall= 0x00000000;
              hwUSB_Configure(pCtrl, TRUE);
              if (((USB_CONFIGURATION_DESCRIPTOR *)pD)->bmAttributes & USB_CONFIG_POWERED_MASK) {
                pCtrl->device_status |=  USB_GETSTATUS_SELF_POWERED;
              } else {
                pCtrl->device_status &= ~USB_GETSTATUS_SELF_POWERED;
              }
            } else {
              new_addr = (uint32_t)pD + ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength;
              pD = (USB_COMMON_DESCRIPTOR*)new_addr;
              continue;
            }
            break;
          case USB_INTERFACE_DESCRIPTOR_TYPE:
            alt = ((USB_INTERFACE_DESCRIPTOR *)pD)->bAlternateSetting;
            break;
          case USB_ENDPOINT_DESCRIPTOR_TYPE:
            if (alt == 0) {
              n = ((USB_ENDPOINT_DESCRIPTOR *)pD)->bEndpointAddress & 0x8F;
              m = (n & 0x80) ? ((1 << 16) << (n & 0x0F)) : (1 << n);
              pCtrl->ep_mask |= m;
              hwUSB_ConfigEP(pCtrl, (USB_ENDPOINT_DESCRIPTOR *)pD);
              hwUSB_EnableEP(pCtrl, n);
              hwUSB_ResetEP(pCtrl, n);
            }
            break;
          }
          new_addr = (uint32_t)pD + pD->bLength;
          pD = (USB_COMMON_DESCRIPTOR*)new_addr;
        }
      }
      else {
        pCtrl->config_value = 0;
        for (n = 1; n < pCtrl->max_num_ep; n++) {
          if (pCtrl->ep_mask & (1 << n)) {
            hwUSB_DisableEP(pCtrl, n);
          }
          if (pCtrl->ep_mask & ((1 << 16) << n)) {
            hwUSB_DisableEP(pCtrl, n | 0x80);
          }
        }
        pCtrl->ep_mask  = 0x00010001;
        pCtrl->ep_halt  = 0x00000000;
        pCtrl->ep_stall = 0x00000000;
        hwUSB_Configure(pCtrl, FALSE);
      }

      if (pCtrl->config_value != pCtrl->SetupPacket.wValue.WB.L) {
        return (ERR_USBD_INVALID_REQ);
      }
      break;
    default:
      return (ERR_USBD_INVALID_REQ);
  }
  return (LPC_OK);
}


/*
*  Get Interface USB Request
*    Parameters:      None (global pCtrl->SetupPacket)
*    Return Value:    TRUE - Success, FALSE - Error
*/

ErrorCode_t USB_ReqGetInterface (USBD_HANDLE_T hUsb) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;

  if (pCtrl->SetupPacket.bmRequestType.BM.Recipient == REQUEST_TO_INTERFACE) {
    if ((pCtrl->config_value != 0) && 
        (pCtrl->SetupPacket.wIndex.WB.L < pCtrl->num_interfaces)) {
      pCtrl->EP0Data.pData = pCtrl->alt_setting + pCtrl->SetupPacket.wIndex.WB.L;
      return (LPC_OK);
    }
  } 
  return (ERR_USBD_INVALID_REQ);
}


/*
*  Set Interface USB Request
*    Parameters:      None (global pCtrl->SetupPacket)
*    Return Value:    TRUE - Success, FALSE - Error
*/

ErrorCode_t USB_ReqSetInterface (USBD_HANDLE_T hUsb) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  USB_COMMON_DESCRIPTOR *pD;
  uint32_t ifn = 0, alt = 0, old = 0, msk = 0, n, m;
  uint32_t new_addr;
  ErrorCode_t ret = ERR_USBD_INVALID_REQ;

  switch (pCtrl->SetupPacket.bmRequestType.BM.Recipient) {
    case REQUEST_TO_INTERFACE:
      if (pCtrl->config_value == 0) return (ERR_USBD_INVALID_REQ);
      ret = ERR_USBD_INVALID_REQ;
      if (pCtrl->device_speed == USB_HIGH_SPEED)
        pD = (USB_COMMON_DESCRIPTOR *)pCtrl->high_speed_desc; 
      else
        pD = (USB_COMMON_DESCRIPTOR *)pCtrl->full_speed_desc; 

      while (pD->bLength) {
        switch (pD->bDescriptorType) {
          case USB_CONFIGURATION_DESCRIPTOR_TYPE:
            if (((USB_CONFIGURATION_DESCRIPTOR *)pD)->bConfigurationValue != pCtrl->config_value) {
              new_addr = (uint32_t)pD + ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength;
              pD = (USB_COMMON_DESCRIPTOR*)new_addr;
              continue;
            }
            break;
          case USB_INTERFACE_DESCRIPTOR_TYPE:
            ifn = ((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber;
            alt = ((USB_INTERFACE_DESCRIPTOR *)pD)->bAlternateSetting;
            msk = 0;
            if ((ifn == pCtrl->SetupPacket.wIndex.WB.L) && (alt == pCtrl->SetupPacket.wValue.WB.L)) {
              ret = LPC_OK;
              old = pCtrl->alt_setting[ifn];
              pCtrl->alt_setting[ifn] = (uint8_t)alt;
            }
            break;
          case USB_ENDPOINT_DESCRIPTOR_TYPE:
            if (ifn == pCtrl->SetupPacket.wIndex.WB.L) {
              n = ((USB_ENDPOINT_DESCRIPTOR *)pD)->bEndpointAddress & 0x8F;
              m = (n & 0x80) ? ((1 << 16) << (n & 0x0F)) : (1 << n);
              if (alt == pCtrl->SetupPacket.wValue.WB.L) {
                pCtrl->ep_mask |=  m;
                pCtrl->ep_halt &= ~m;
                hwUSB_ConfigEP(pCtrl, (USB_ENDPOINT_DESCRIPTOR *)pD);
                hwUSB_EnableEP(pCtrl, n);
                hwUSB_ResetEP(pCtrl, n);
                msk |= m;
              }
              else if ((alt == old) && ((msk & m) == 0)) {
                pCtrl->ep_mask &= ~m;
                pCtrl->ep_halt &= ~m;
                hwUSB_DisableEP(pCtrl, n);
              }
            }
            break;
        }
        new_addr = (uint32_t)pD + pD->bLength;
        pD = (USB_COMMON_DESCRIPTOR*)new_addr;
      }
      break;
    default:
      return (ERR_USBD_INVALID_REQ);
  }

  return (ret);
}

ErrorCode_t USB_InvokeEp0Hdlrs (USB_CORE_CTRL_T* pCtrl, uint32_t event) 
{
  uint32_t inf = 0;
  ErrorCode_t ret = ERR_USBD_UNHANDLED;

  for (inf = 0; inf < pCtrl->num_ep0_hdlrs; inf++) {
    /* check if a valid handler is installed */
    if(pCtrl->ep0_hdlr_cb[inf] != 0) {
      /*invoke the handlers */
      ret = pCtrl->ep0_hdlr_cb[inf](pCtrl, pCtrl->ep0_cb_data[inf], event);
      /* if un-handled continue to next handler */
      if (ret != ERR_USBD_UNHANDLED) {
        if (ret != LPC_OK) {
          /* STALL requested */
          //if (ret == ERR_USBD_STALL)
          mwUSB_StallEp0(pCtrl);
        }
        /* Event is handled so return the result */
        break;
      }
    }
  }
  return ret;
}

ErrorCode_t USB_EvtSetupHandler(USBD_HANDLE_T hUsb)
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  ErrorCode_t ret = ERR_USBD_UNHANDLED;

  mwUSB_SetupStage(pCtrl);
  hwUSB_DirCtrlEP(pCtrl, pCtrl->SetupPacket.bmRequestType.BM.Dir);
  pCtrl->EP0Data.Count = pCtrl->SetupPacket.wLength;     /* Number of bytes to transfer */

  /* Give class handlers first chance to handle the event.
  * If unhandled do the standard/default handling of the events.
  */
  ret = USB_InvokeEp0Hdlrs(pCtrl, USB_EVT_SETUP);
  /* One of the callabcks has handled the event. Just return. */ 
  if(ret != ERR_USBD_UNHANDLED) 
    return ret;

  switch (pCtrl->SetupPacket.bmRequestType.BM.Type) {

    case REQUEST_STANDARD:
      switch (pCtrl->SetupPacket.bRequest) {

        case USB_REQUEST_GET_STATUS:
          ret = pCtrl->USB_ReqGetStatus(pCtrl);
          if ( ret == LPC_OK) {
            mwUSB_DataInStage(pCtrl);
          }
          break;
  
        case USB_REQUEST_CLEAR_FEATURE:
          ret = pCtrl->USB_ReqSetClrFeature(pCtrl, 0); 
          if ( ret == LPC_OK) {
            mwUSB_StatusInStage(pCtrl);
            if (pCtrl->USB_Feature_Event)
              pCtrl->USB_Feature_Event(pCtrl);
          }
          break;

        case USB_REQUEST_SET_FEATURE:
          ret = pCtrl->USB_ReqSetClrFeature(pCtrl, 1); 
          if ( ret == LPC_OK) {
            mwUSB_StatusInStage(pCtrl);
            if (pCtrl->USB_Feature_Event)
              pCtrl->USB_Feature_Event(pCtrl);
          }
          break;

        case USB_REQUEST_SET_ADDRESS:
          ret = USB_ReqSetAddress(pCtrl);
          if ( ret == LPC_OK) {
            mwUSB_StatusInStage(pCtrl);
          }
          break;
  
        case USB_REQUEST_GET_DESCRIPTOR:
          ret = pCtrl->USB_ReqGetDescriptor(pCtrl);
          if (ret == LPC_OK) {
            mwUSB_DataInStage(pCtrl);
          }
          break;

        case USB_REQUEST_SET_DESCRIPTOR:
          hwUSB_SetStallEP(pCtrl, 0x00);            /* not supported */
          pCtrl->EP0Data.Count = 0;
          ret = LPC_OK;
          break;

        case USB_REQUEST_GET_CONFIGURATION:
          ret = pCtrl->USB_ReqGetConfiguration(pCtrl);
          if (ret == LPC_OK) {
            mwUSB_DataInStage(pCtrl);
          }
          break;
  
        case USB_REQUEST_SET_CONFIGURATION:
          ret = pCtrl->USB_ReqSetConfiguration(pCtrl);
          if (ret == LPC_OK) {
            mwUSB_StatusInStage(pCtrl);
            if (pCtrl->USB_Configure_Event)
              pCtrl->USB_Configure_Event(pCtrl);
          }
          break;

        case USB_REQUEST_GET_INTERFACE:
          ret = pCtrl->USB_ReqGetInterface(pCtrl);
          if (ret == LPC_OK) {
            mwUSB_DataInStage(pCtrl);
          }
          break;

        case USB_REQUEST_SET_INTERFACE:
          ret = pCtrl->USB_ReqSetInterface(pCtrl);
          if (ret == LPC_OK) {
            mwUSB_StatusInStage(pCtrl);
            if (pCtrl->USB_Interface_Event)
              pCtrl->USB_Interface_Event(pCtrl);
          }
          break;

        default:
            //mwUSB_StallEp0(pCtrl); 
            break;
        }
        break;  /* end case REQUEST_STANDARD */
  
    case REQUEST_CLASS:
      /* All CLASS request should have been handled by class handlers.
       * If we are here it is illegal so stall the EP.
       */
      //mwUSB_StallEp0(pCtrl); 
      break;
      /* end case REQUEST_CLASS */

    case REQUEST_VENDOR:
      if (pCtrl->USB_ReqVendor)  {
        ret = pCtrl->USB_ReqVendor(pCtrl, USB_EVT_SETUP);
        if (ret == LPC_OK) {
          if (pCtrl->SetupPacket.wLength) {
            if (pCtrl->SetupPacket.bmRequestType.BM.Dir == REQUEST_DEVICE_TO_HOST) {
              mwUSB_DataInStage(pCtrl);
            }
          } else {
            mwUSB_StatusInStage(pCtrl);
          }
        }
      }
      break;  /* end case REQUEST_VENDOR */ 

    default:
      //mwUSB_StallEp0(pCtrl); 
      break;
  }
  if (ret != LPC_OK) 
    mwUSB_StallEp0(pCtrl);

  return (LPC_OK);
}

ErrorCode_t USB_EvtOutHandler(USBD_HANDLE_T hUsb)
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  ErrorCode_t ret = LPC_OK;

  if (pCtrl->SetupPacket.bmRequestType.BM.Dir == REQUEST_HOST_TO_DEVICE) {
    if (pCtrl->EP0Data.Count) {                                             /* still data to receive ? */
      
      mwUSB_DataOutStage(pCtrl);                                            /* receive data */
      
      if (pCtrl->EP0Data.Count == 0) {                                      /* data complete ? */

        /* Give class handlers first chance to handle the event.
        * If unhandled do the standard/default handling of the events.
        */
        ret = USB_InvokeEp0Hdlrs(pCtrl, USB_EVT_OUT);
        if(ret != ERR_USBD_UNHANDLED) 
          return ret; /* One of the callabcks has handled the event. Just return. */

        /* except vendor callback everything else should have been 
        * handled in class handlers. If not stall the ep0_in to give negative handshake.
        */
        if ((pCtrl->SetupPacket.bmRequestType.BM.Type == REQUEST_VENDOR) &&
            (pCtrl->USB_ReqVendor != 0)) {
            if (pCtrl->USB_ReqVendor(pCtrl, USB_EVT_SETUP) == LPC_OK) {
              
              mwUSB_StatusInStage(pCtrl);
              return LPC_OK;
            } 
        }
        /* all other condition stall EP */ 
        mwUSB_StallEp0(pCtrl);
      }
    }
  } else {
    mwUSB_StatusOutStage(pCtrl);                                            /* receive Acknowledge */
  }

  return (LPC_OK);
}

/*
*  USB Endpoint 0 Event Callback
*    Parameter:       event
*/

ErrorCode_t USB_EndPoint0(USBD_HANDLE_T hUsb, void* data, uint32_t event) 
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;
  ErrorCode_t ret = ERR_USBD_UNHANDLED;

  switch (event) {
    case USB_EVT_SETUP:
      pCtrl->USB_EvtSetupHandler(pCtrl);
      break;  /* end case USB_EVT_SETUP */

    case USB_EVT_OUT_NAK:
      /* Give class handlers first chance to handle the event.
      * If unhandled do the standard/default handling of the events.
      */
      ret = USB_InvokeEp0Hdlrs(pCtrl, USB_EVT_OUT_NAK);
      /* One of the callabcks has handled the event. Just return. */ 
      if(ret == ERR_USBD_UNHANDLED)  {

        if (pCtrl->SetupPacket.bmRequestType.BM.Dir == 0)  {
          hwUSB_ReadReqEP(pCtrl, 0x00, pCtrl->EP0Data.pData, pCtrl->EP0Data.Count);
        } else {
          /* might be zero length pkt */
          hwUSB_ReadReqEP(pCtrl, 0x00, pCtrl->EP0Data.pData, 0);
        }
      }
      break;
    case USB_EVT_OUT:
      pCtrl->USB_EvtOutHandler(pCtrl);
      break;  /* end case USB_EVT_OUT */

    case USB_EVT_IN :
      if (pCtrl->SetupPacket.bmRequestType.BM.Dir == REQUEST_DEVICE_TO_HOST) {
        ret = USB_InvokeEp0Hdlrs(pCtrl, USB_EVT_IN);
        /* One of the callabcks has handled the event. Just return. */ 
        if(ret == ERR_USBD_UNHANDLED)  {
          mwUSB_DataInStage(pCtrl);                /* send data */
        }                                               
      } else {
        if (pCtrl->device_addr & 0x80) {
          pCtrl->device_addr &= 0x7F;
          hwUSB_SetAddress(pCtrl, pCtrl->device_addr);
        }
      }
      break;  /* end case USB_EVT_IN */

    case USB_EVT_OUT_STALL:
      hwUSB_ClrStallEP(pCtrl, 0x00);
      break;

    case USB_EVT_IN_STALL:
      hwUSB_ClrStallEP(pCtrl, 0x80);
      break;
  }
  return LPC_OK;
}


/* cache and mmu translation functions */
uint32_t USBD_virt_to_phys(void* vaddr)
{
  /* retun virtual address as physical address */
  return (uint32_t)vaddr;
}

void USBD_cache_flush(uint32_t* start_adr, uint32_t* end_adr)
{
  /* do nothing*/
}

void  mwUSB_InitCore(USB_CORE_CTRL_T* pCtrl, USB_CORE_DESCS_T* pdescr, USBD_API_INIT_PARAM_T* param)
{
  COMPILE_TIME_ASSERT((offsetof(USB_CORE_CTRL_T, SetupPacket) & 0x3) == 0);
  COMPILE_TIME_ASSERT((offsetof(USB_CORE_CTRL_T, EP0Buf) & 0x3) == 0);

  /* init USB controller struct */
  memset((void*)pCtrl, 0, sizeof(USB_CORE_CTRL_T));
  pCtrl->max_num_ep = param->max_num_ep;
  /* assign default implementation to virtual functions*/
  mwUSB_RegisterEpHandler (pCtrl, 0, USB_EndPoint0, pCtrl);
  mwUSB_RegisterEpHandler (pCtrl, 1, USB_EndPoint0, pCtrl);
  pCtrl->USB_EvtSetupHandler = USB_EvtSetupHandler;
  pCtrl->USB_EvtOutHandler = USB_EvtOutHandler;

  pCtrl->USB_ReqGetStatus = USB_ReqGetStatus;
  pCtrl->USB_ReqSetClrFeature = USB_ReqSetClrFeature;
  pCtrl->USB_ReqGetDescriptor = USB_ReqGetDescriptor;
  pCtrl->USB_ReqGetConfiguration = USB_ReqGetConfiguration;
  pCtrl->USB_ReqSetConfiguration = USB_ReqSetConfiguration;
  pCtrl->USB_ReqGetInterface = USB_ReqGetInterface;
  pCtrl->USB_ReqSetInterface = USB_ReqSetInterface;
  
  /* cache and mmu translation functions */
  pCtrl->virt_to_phys = (param->virt_to_phys)?param->virt_to_phys:USBD_virt_to_phys;
  pCtrl->cache_flush = (param->cache_flush)?param->cache_flush:USBD_cache_flush;

  /* copy other callback */
  pCtrl->USB_WakeUpCfg     = param->USB_WakeUpCfg;    
  pCtrl->USB_Power_Event   = param->USB_Power_Event;  
  pCtrl->USB_Reset_Event   = param->USB_Reset_Event;  
  pCtrl->USB_Suspend_Event = param->USB_Suspend_Event;
  pCtrl->USB_Resume_Event  = param->USB_Resume_Event; 
  pCtrl->USB_SOF_Event     = param->USB_SOF_Event;    
  pCtrl->USB_Error_Event   = param->USB_Error_Event;  

  pCtrl->USB_Configure_Event = param->USB_Configure_Event;  
  pCtrl->USB_Interface_Event = param->USB_Interface_Event;
  pCtrl->USB_Feature_Event   = param->USB_Feature_Event;

  /* parse descriptors */
  pCtrl->device_desc = pdescr->device_desc;
  pCtrl->string_desc = pdescr->string_desc;
  pCtrl->full_speed_desc = pdescr->full_speed_desc;
  pCtrl->high_speed_desc = pdescr->high_speed_desc;
  pCtrl->device_qualifier = pdescr->device_qualifier;

}

ErrorCode_t mwUSB_RegisterClassHandler(USBD_HANDLE_T hUsb, USB_EP_HANDLER_T pfn, void* data)
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;

  /* check if index is within limits. */
  if (pCtrl->num_ep0_hdlrs >= USB_MAX_IF_NUM)
    return ERR_USBD_TOO_MANY_CLASS_HDLR;

  pCtrl->ep0_hdlr_cb[pCtrl->num_ep0_hdlrs] = pfn;
  pCtrl->ep0_cb_data[pCtrl->num_ep0_hdlrs] = data;
  /* increment handler index */
  pCtrl->num_ep0_hdlrs++;

  return LPC_OK;
}

ErrorCode_t mwUSB_RegisterEpHandler(USBD_HANDLE_T hUsb, uint32_t ep_indx, USB_EP_HANDLER_T pfn, void* data)
{
  USB_CORE_CTRL_T* pCtrl = (USB_CORE_CTRL_T*)hUsb;

  /* check if index is within limits. */
  if (ep_indx > (2 * pCtrl->max_num_ep))
    return ERR_API_INVALID_PARAM2;

  pCtrl->ep_event_hdlr[ep_indx] = pfn;
  pCtrl->ep_hdlr_data[ep_indx] = data;

  return LPC_OK;
}




