USB device stack
Dependents: USBMSD_step1 USBMSD_step1_5 picossd_step1_2cs
Diff: targets/TARGET_Silicon_Labs/src/em_usbdint.c
- Revision:
- 71:53949e6131f6
diff -r 2c525a50f1b6 -r 53949e6131f6 targets/TARGET_Silicon_Labs/src/em_usbdint.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/TARGET_Silicon_Labs/src/em_usbdint.c Thu Jul 27 12:14:04 2017 +0100 @@ -0,0 +1,946 @@ +/**************************************************************************//** + * @file em_usbdint.c + * @brief USB protocol stack library, USB device peripheral interrupt handlers. + * @version 3.20.14 + ****************************************************************************** + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * 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 "em_device.h" +#if defined( USB_PRESENT ) && ( USB_COUNT == 1 ) +#include "em_usb.h" +#if defined( USB_DEVICE ) + +#include "em_cmu.h" +#include "em_usbtypes.h" +#include "em_usbhal.h" +#include "em_usbd.h" + +#ifdef __MBED__ +extern void usbhal_allow_em2(bool em2_allow); +#endif + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +#define HANDLE_INT( x ) if ( status & x ) { Handle_##x(); status &= ~x; } + +static void Handle_USB_GINTSTS_ENUMDONE ( void ); +static void Handle_USB_GINTSTS_IEPINT ( void ); +static void Handle_USB_GINTSTS_OEPINT ( void ); +static void Handle_USB_GINTSTS_RESETDET ( void ); +static void Handle_USB_GINTSTS_SOF ( void ); +static void Handle_USB_GINTSTS_USBRST ( void ); +static void Handle_USB_GINTSTS_USBSUSP ( void ); +static void Handle_USB_GINTSTS_WKUPINT ( void ); +#if defined( USB_DOEP0INT_STUPPKTRCVD ) +static void HandleOutEpIntr( uint32_t status, USBD_Ep_TypeDef *ep ); +#else +static void ProcessSetup ( void ); +static void ProcessOepData ( USBD_Ep_TypeDef *ep ); +#endif + +#if ( USB_PWRSAVE_MODE ) +/* Variables and prototypes for USB powerdown (suspend) functionality. */ +static bool UsbPowerDown( void ); +static bool UsbPowerUp( void ); + +volatile bool USBD_poweredDown = false; + +/* Storage for backing up USB core registers. */ +static uint32_t x_USB_GINTMSK; +#if defined(_USB_GOTGCTL_MASK) +static uint32_t x_USB_GOTGCTL; +#endif +static uint32_t x_USB_GAHBCFG; +static uint32_t x_USB_GUSBCFG; +static uint32_t x_USB_GRXFSIZ; +static uint32_t x_USB_GNPTXFSIZ; +static uint32_t x_USB_DCFG; +static uint32_t x_USB_DCTL; +static uint32_t x_USB_DAINTMSK; +static uint32_t x_USB_DIEPMSK; +static uint32_t x_USB_DOEPMSK; +static uint32_t x_USB_PCGCCTL; + +#if ( NUM_EP_USED > 0 ) +static uint32_t x_USB_EP_CTL[ NUM_EP_USED ]; +static uint32_t x_USB_EP_TSIZ[ NUM_EP_USED ]; +static uint32_t x_USB_EP_DMAADDR[ NUM_EP_USED ]; +#endif + +#if ( NUM_EP_USED > MAX_NUM_TX_FIFOS ) +#define FIFO_CNT MAX_NUM_TX_FIFOS +#else +#define FIFO_CNT NUM_EP_USED +#endif + +#if ( FIFO_CNT > 0 ) +static uint32_t x_USB_DIEPTXFS[ FIFO_CNT ]; +#endif + +#if ( USB_PWRSAVE_MODE ) +static uint32_t cmuStatus = 0; +#endif + +#endif /* if ( USB_PWRSAVE_MODE ) */ + +/* + * USB_IRQHandler() is the first level handler for the USB peripheral interrupt. + */ +void USB_IRQHandler( void ) +{ + uint32_t status; + bool servedVbusInterrupt = false; + + INT_Disable(); + +#if ( USB_PWRSAVE_MODE ) + if ( USBD_poweredDown ) + { + /* Switch USBC clock from 32kHz to a 48MHz clock to be able to */ + /* read USB peripheral registers. */ + /* If we woke up from EM2, HFCLK is now HFRCO. */ + + /* Restore clock oscillators.*/ +#if defined( CMU_OSCENCMD_USHFRCOEN ) + if ( ( CMU->STATUS & CMU_STATUS_USHFRCOENS ) == 0 )/*Wakeup from EM2 ?*/ + { + CMU->OSCENCMD = ( cmuStatus + & ( CMU_STATUS_AUXHFRCOENS | CMU_STATUS_HFXOENS ) ) + | CMU_OSCENCMD_USHFRCOEN; + } +#else + if ( ( CMU->STATUS & CMU_STATUS_HFXOENS ) == 0 ) /* Wakeup from EM2 ? */ + { + CMU->OSCENCMD = cmuStatus + & ( CMU_STATUS_AUXHFRCOENS | CMU_STATUS_HFXOENS ); + } +#endif + + /* Select correct USBC clock.*/ +#if defined( CMU_OSCENCMD_USHFRCOEN ) + CMU->CMD = CMU_CMD_USBCCLKSEL_USHFRCO; + while ( ( CMU->STATUS & CMU_STATUS_USBCUSHFRCOSEL ) == 0 ){} +#else + CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV; + while ( ( CMU->STATUS & CMU_STATUS_USBCHFCLKSEL ) == 0 ){} +#endif + } +#endif /* if ( USB_PWRSAVE_MODE ) */ + + if ( USB->IF && ( USB->CTRL & USB_CTRL_VREGOSEN ) ) + { + if ( USB->IF & USB_IF_VREGOSH ) + { + USB->IFC = USB_IFC_VREGOSH; + + if ( USB->STATUS & USB_STATUS_VREGOS ) + { + servedVbusInterrupt = true; + DEBUG_USB_INT_LO_PUTS( "\nVboN" ); + +#if ( USB_PWRSAVE_MODE ) + if ( UsbPowerUp() ) + { + USBDHAL_EnableUsbResetAndSuspendInt(); + } + USBD_SetUsbState( USBD_STATE_POWERED ); +#endif + } + } + + if ( USB->IF & USB_IF_VREGOSL ) + { + USB->IFC = USB_IFC_VREGOSL; + + if ( ( USB->STATUS & USB_STATUS_VREGOS ) == 0 ) + { + servedVbusInterrupt = true; + DEBUG_USB_INT_LO_PUTS( "\nVboF" ); + +#if ( USB_PWRSAVE_MODE ) +#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) + if ( !USBD_poweredDown ) + { + USB->GINTMSK = 0; + USB->GINTSTS = 0xFFFFFFFF; + } + + UsbPowerDown(); +#endif + USBD_SetUsbState( USBD_STATE_NONE ); +#endif + } + } + } + + status = USBHAL_GetCoreInts(); + if ( status == 0 ) + { + INT_Enable(); + if ( !servedVbusInterrupt ) + { + DEBUG_USB_INT_LO_PUTS( "\nSinT" ); + } + return; + } + + HANDLE_INT( USB_GINTSTS_RESETDET ) + HANDLE_INT( USB_GINTSTS_WKUPINT ) + HANDLE_INT( USB_GINTSTS_USBSUSP ) + HANDLE_INT( USB_GINTSTS_SOF ) + HANDLE_INT( USB_GINTSTS_ENUMDONE ) + HANDLE_INT( USB_GINTSTS_USBRST ) + HANDLE_INT( USB_GINTSTS_IEPINT ) + HANDLE_INT( USB_GINTSTS_OEPINT ) + + INT_Enable(); + + if ( status != 0 ) + { + DEBUG_USB_INT_LO_PUTS( "\nUinT" ); + } +} + +/* + * Handle port enumeration interrupt. This has nothing to do with normal + * device enumeration. + */ +static void Handle_USB_GINTSTS_ENUMDONE( void ) +{ +#if ( USB_PWRSAVE_MODE ) + UsbPowerUp(); +#endif + + USBDHAL_Ep0Activate( dev->ep0MpsCode ); + dev->ep[ 0 ].state = D_EP_IDLE; + USBDHAL_EnableInts( dev ); + DEBUG_USB_INT_LO_PUTS( "EnumD" ); +} + +/* + * Handle IN endpoint transfer interrupt. + */ +static void Handle_USB_GINTSTS_IEPINT( void ) +{ + int epnum; + uint16_t epint; + uint16_t epmask; + uint32_t status; + USBD_Ep_TypeDef *ep; + + DEBUG_USB_INT_HI_PUTCHAR( 'i' ); + + epint = USBDHAL_GetAllInEpInts(); + for ( epnum = 0, epmask = 1; + epnum <= MAX_NUM_IN_EPS; + epnum++, epmask <<= 1 ) + { + if ( epint & epmask ) + { + ep = USBD_GetEpFromAddr( USB_SETUP_DIR_MASK | epnum ); + status = USBDHAL_GetInEpInts( ep ); + + if ( status & USB_DIEP_INT_XFERCOMPL ) + { + USB_DINEPS[ epnum ].INT = USB_DIEP_INT_XFERCOMPL; + + DEBUG_USB_INT_HI_PUTCHAR( 'c' ); + + if ( epnum == 0 ) + { + if ( ep->remaining > ep->packetSize ) + { + ep->remaining -= ep->packetSize; + ep->xferred += ep->packetSize; + } + else + { + ep->xferred += ep->remaining; + ep->remaining = 0; + } + USBDEP_Ep0Handler( dev ); + } + else + { + ep->xferred = ep->remaining - + ( ( USB_DINEPS[ epnum ].TSIZ & + _USB_DIEP_TSIZ_XFERSIZE_MASK ) >> + _USB_DIEP_TSIZ_XFERSIZE_SHIFT ); + ep->remaining -= ep->xferred; + + USBDEP_EpHandler( ep->addr ); +#if defined( USB_DOEP0INT_STUPPKTRCVD ) + if ( USB_DINEPS[ ep->num ].INT & USB_DIEP_INT_NAKINTRPT ) + { + USB_DINEPS[ ep->num ].INT = USB_DIEP_INT_NAKINTRPT; + } +#endif + } + } + } + } +} + +/* + * Handle OUT endpoint transfer interrupt. + */ +static void Handle_USB_GINTSTS_OEPINT( void ) +{ + int epnum; + uint16_t epint; + uint16_t epmask; + uint32_t status; + USBD_Ep_TypeDef *ep; + + DEBUG_USB_INT_HI_PUTCHAR( 'o' ); + + epint = USBDHAL_GetAllOutEpInts(); + for ( epnum = 0, epmask = 1; + epnum <= MAX_NUM_OUT_EPS; + epnum++, epmask <<= 1 ) + { + if ( epint & epmask ) + { + ep = USBD_GetEpFromAddr( epnum ); + status = USBDHAL_GetOutEpInts( ep ); + +#if defined( USB_DOEP0INT_STUPPKTRCVD ) + HandleOutEpIntr( status, ep ); +#else + if ( status & USB_DOEP_INT_XFERCOMPL ) + { + USB_DOUTEPS[ epnum ].INT = USB_DOEP_INT_XFERCOMPL; + DEBUG_USB_INT_HI_PUTCHAR( 'c' ); + ProcessOepData( ep ); + } + + /* Setup Phase Done */ + if ( status & USB_DOEP0INT_SETUP ) + { + ProcessSetup(); + } +#endif + } + } +} + +#if !defined( USB_DOEP0INT_STUPPKTRCVD ) +static void ProcessOepData( USBD_Ep_TypeDef *ep ) +{ + if ( ep->num == 0 ) + { + +#ifdef __MBED__ + int xfer_size = ep->packetSize - (( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_XFERSIZE_MASK ) + >> _USB_DOEP0TSIZ_XFERSIZE_SHIFT); + int setup_pkt_received = USBDHAL_GetOutEpInts( ep ) & USB_DOEP0INT_SETUP; + + if ( (!setup_pkt_received && xfer_size == 0) || + (setup_pkt_received && xfer_size == 8) ) + { + /* Higher levels need to see the correct transfer amount for ZLPs */ + ep->remaining = 0; + ep->xferred = 0; + } + else + { + /* FIXME - does not work if actual read size > 56 */ + if ( setup_pkt_received ) xfer_size -= 8; + + ep->xferred = xfer_size; + ep->remaining -= xfer_size; + } +#else + if ( ep->remaining > ep->packetSize ) + { + ep->remaining -= ep->packetSize; + ep->xferred += ep->packetSize; + } + else + { + ep->xferred += ep->remaining; + ep->remaining = 0; + } +#endif + + USBDEP_Ep0Handler( dev ); + } + else + { + ep->xferred = ep->hwXferSize - + ( ( USB_DOUTEPS[ ep->num ].TSIZ & _USB_DOEP_TSIZ_XFERSIZE_MASK )>> + _USB_DOEP_TSIZ_XFERSIZE_SHIFT ); + ep->remaining -= ep->xferred; + USBDEP_EpHandler( ep->addr ); + } +} +#endif + +#if !defined( USB_DOEP0INT_STUPPKTRCVD ) +static void ProcessSetup( void ) +{ + DEBUG_USB_INT_LO_PUTS( "\nS" ); + + if ( USB->DOEP0INT & USB_DOEP0INT_BACK2BACKSETUP ) + { /* Back to back setup packets received */ + USB->DOEP0INT = USB_DOEP0INT_BACK2BACKSETUP; + DEBUG_USB_INT_LO_PUTS( "B2B" ); + + dev->setup = (USB_Setup_TypeDef*)( USB->DOEP0DMAADDR - USB_SETUP_PKT_SIZE ); + } + else + { + /* Read SETUP packet counter from hw. */ + int supCnt = ( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_SUPCNT_MASK ) + >> _USB_DOEP0TSIZ_SUPCNT_SHIFT; + + if ( supCnt == 3 ) + supCnt = 2; + + dev->setup = &dev->setupPkt[ 2 - supCnt ]; + } + USB->DOEP0TSIZ |= 3 << _USB_DOEP0TSIZ_SUPCNT_SHIFT; + USB->DOEP0DMAADDR = (uint32_t)dev->setupPkt; + USB->DOEP0INT = USB_DOEP0INT_SETUP; + + USBDEP_Ep0Handler( dev ); /* Call the SETUP handler for EP0 */ +} +#endif + +/* + * Handle USB reset detected interrupt in suspend mode. + */ +static void Handle_USB_GINTSTS_RESETDET ( void ) +{ +#if ( USB_PWRSAVE_MODE ) + if ( ! USBD_poweredDown ) + { + USB->GINTSTS = USB_GINTSTS_RESETDET; + } + + if ( UsbPowerUp() ) + { + USB->GINTSTS = USB_GINTSTS_RESETDET; + } + +#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) + /* Power down immediately if VBUS is off. */ + if ( ! ( USB->STATUS & USB_STATUS_VREGOS ) ) + { + UsbPowerDown(); + } +#endif + +#else + USB->GINTSTS = USB_GINTSTS_RESETDET; +#endif /* if ( USB_PWRSAVE_MODE ) */ + + if ( USB->STATUS & USB_STATUS_VREGOS ) + { + USBD_SetUsbState( USBD_STATE_DEFAULT ); + } + else + { + USBD_SetUsbState( USBD_STATE_NONE ); + } + DEBUG_USB_INT_LO_PUTS( "RsuP\n" ); +} + +/* + * Handle Start Of Frame (SOF) interrupt. + */ +static void Handle_USB_GINTSTS_SOF( void ) +{ + USB->GINTSTS = USB_GINTSTS_SOF; + + if ( dev->callbacks->sofInt ) + { + dev->callbacks->sofInt( + ( USB->DSTS & _USB_DSTS_SOFFN_MASK ) >> _USB_DSTS_SOFFN_SHIFT ); + } +} + +/* + * Handle USB port reset interrupt. + */ +static void Handle_USB_GINTSTS_USBRST( void ) +{ + int i; + + DEBUG_USB_INT_LO_PUTS( "ReseT" ); + + /* Clear Remote Wakeup Signalling */ + USB->DCTL &= ~( DCTL_WO_BITMASK | USB_DCTL_RMTWKUPSIG ); + USBHAL_FlushTxFifo( 0 ); + + /* Clear pending interrupts */ + for ( i = 0; i <= MAX_NUM_IN_EPS; i++ ) + { + USB_DINEPS[ i ].INT = 0xFFFFFFFF; + } + + for ( i = 0; i <= MAX_NUM_OUT_EPS; i++ ) + { + USB_DOUTEPS[ i ].INT = 0xFFFFFFFF; + } + + USB->DAINTMSK = USB_DAINTMSK_INEPMSK0 | USB_DAINTMSK_OUTEPMSK0; +#if defined( USB_DOEPMSK_STSPHSERCVDMSK ) + USB->DOEPMSK = USB_DOEPMSK_SETUPMSK | USB_DOEPMSK_XFERCOMPLMSK + | USB_DOEPMSK_STSPHSERCVDMSK; +#else + USB->DOEPMSK = USB_DOEPMSK_SETUPMSK | USB_DOEPMSK_XFERCOMPLMSK; +#endif + USB->DIEPMSK = USB_DIEPMSK_XFERCOMPLMSK; + + /* Reset Device Address */ + USB->DCFG &= ~_USB_DCFG_DEVADDR_MASK; + + /* Setup EP0 to receive SETUP packets */ + USBDHAL_StartEp0Setup( dev ); + USBDHAL_EnableInts( dev ); + + if ( dev->callbacks->usbReset ) + { + dev->callbacks->usbReset(); + } + + USBD_SetUsbState( USBD_STATE_DEFAULT ); + USBDHAL_AbortAllTransfers( USB_STATUS_DEVICE_RESET ); +} + +/* + * Handle USB port suspend interrupt. + */ +static void Handle_USB_GINTSTS_USBSUSP( void ) +{ + USBD_State_TypeDef state; + + USB->GINTSTS = USB_GINTSTS_USBSUSP; + USBDHAL_AbortAllTransfers( USB_STATUS_DEVICE_SUSPENDED ); + DEBUG_USB_INT_LO_PUTS( "\nSusP" ); + + if ( USBD_GetUsbState() == USBD_STATE_NONE ) + { + USBD_SetUsbState( USBD_STATE_POWERED ); + } + + state = USBD_GetUsbState(); + if ( ( state == USBD_STATE_POWERED ) || + ( state == USBD_STATE_DEFAULT ) || + ( state == USBD_STATE_ADDRESSED ) || + ( state == USBD_STATE_CONFIGURED ) ) + { +#if ( USB_PWRSAVE_MODE ) + UsbPowerDown(); +#endif + USBD_SetUsbState( USBD_STATE_SUSPENDED ); + } +} + +/* + * Handle USB port wakeup interrupt. + */ +static void Handle_USB_GINTSTS_WKUPINT( void ) +{ +#if ( USB_PWRSAVE_MODE ) + if ( ! USBD_poweredDown ) + { + USB->GINTSTS = USB_GINTSTS_WKUPINT; + } + + if ( UsbPowerUp() ) + { + USB->GINTSTS = USB_GINTSTS_WKUPINT; + USBDHAL_StartEp0Setup( dev ); + USBDHAL_Ep0Activate( dev->ep0MpsCode ); + } +#else + USB->GINTSTS = USB_GINTSTS_WKUPINT; +#endif + + USBD_SetUsbState( dev->savedState ); + DEBUG_USB_INT_LO_PUTS( "WkuP\n" ); +} + +#if ( USB_PWRSAVE_MODE ) +/* + * Backup essential USB core registers, and set the core in partial powerdown + * mode. Optionally prepare entry into EM2. + */ +static bool UsbPowerDown( void ) +{ +#if ( NUM_EP_USED > 0 ) || ( FIFO_CNT > 0 ) + int i; +#endif +#if ( NUM_EP_USED > 0 ) + int epNum; + USBD_Ep_TypeDef *ep; +#endif + + if ( !USBD_poweredDown ) + { + USBD_poweredDown = true; + DEBUG_USB_INT_LO_PUTCHAR( '\\' ); + + /* Backup USB core registers. */ + x_USB_GINTMSK = USB->GINTMSK; +#if defined(_USB_GOTGCTL_MASK) + x_USB_GOTGCTL = USB->GOTGCTL; +#endif + x_USB_GAHBCFG = USB->GAHBCFG; + x_USB_GUSBCFG = USB->GUSBCFG; + x_USB_GRXFSIZ = USB->GRXFSIZ; + x_USB_GNPTXFSIZ = USB->GNPTXFSIZ; + x_USB_DCFG = USB->DCFG; + x_USB_DCTL = USB->DCTL; + x_USB_DAINTMSK = USB->DAINTMSK; + x_USB_DIEPMSK = USB->DIEPMSK; + x_USB_DOEPMSK = USB->DOEPMSK; + x_USB_PCGCCTL = USB->PCGCCTL; + +#if ( NUM_EP_USED > 0 ) + for ( i = 0; i < NUM_EP_USED; i++ ) + { + ep = &dev->ep[ i+1 ]; + epNum = ep->num; + if ( ep->in ) + { + x_USB_EP_CTL[ i ] = USB_DINEPS[ epNum ].CTL; + x_USB_EP_TSIZ[ i ] = USB_DINEPS[ epNum ].TSIZ; + x_USB_EP_DMAADDR[ i ] = USB_DINEPS[ epNum ].DMAADDR; + } + else + { + x_USB_EP_CTL[ i ] = USB_DOUTEPS[ epNum ].CTL; + x_USB_EP_TSIZ[ i ] = USB_DOUTEPS[ epNum ].TSIZ; + x_USB_EP_DMAADDR[ i ] = USB_DOUTEPS[ epNum ].DMAADDR; + } + } +#endif + +#if ( FIFO_CNT > 0 ) + for ( i = 0; i < FIFO_CNT; i++ ) + { + x_USB_DIEPTXFS[ i ] = USB_DIEPTXFS[ i ]; + } +#endif + + /* Prepare for wakeup on resume and reset. */ + USB->DCFG = (USB->DCFG & ~_USB_DCFG_RESVALID_MASK) | + (4 << _USB_DCFG_RESVALID_SHIFT); + USB->DCFG |= USB_DCFG_ENA32KHZSUSP; + USB->GINTMSK = USB_GINTMSK_RESETDETMSK | USB_GINTMSK_WKUPINTMSK; + + /* Enter partial powerdown mode. */ + USB->PCGCCTL |= USB_PCGCCTL_PWRCLMP; + USB->PCGCCTL |= USB_PCGCCTL_RSTPDWNMODULE; + USB->PCGCCTL |= USB_PCGCCTL_STOPPCLK; + + /* Record current clock settings. */ + cmuStatus = CMU->STATUS; + +#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ENTEREM2 ) +#ifndef __MBED__ + /* Enter EM2 on interrupt exit. */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk; +#else + usbhal_allow_em2(true); +#endif +#endif + + /* Switch USBC clock to 32 kHz. */ +#if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) + CMU->CMD = CMU_CMD_USBCCLKSEL_LFXO; + while ( ( CMU->STATUS & CMU_STATUS_USBCLFXOSEL ) == 0 ){} +#else + CMU->CMD = CMU_CMD_USBCCLKSEL_LFRCO; + while ( ( CMU->STATUS & CMU_STATUS_USBCLFRCOSEL ) == 0 ){} +#endif + + return true; + } + return false; +} +#endif /* if ( USB_PWRSAVE_MODE ) */ + +#if ( USB_PWRSAVE_MODE ) +/* + * Exit USB core partial powerdown mode, restore essential USB core registers. + * Will prevent re-entry back to EM2. + * Returns true if a powerup sequence was performed. + */ +static bool UsbPowerUp( void ) +{ +#if ( NUM_EP_USED > 0 ) || ( FIFO_CNT > 0 ) + int i; +#endif +#if ( NUM_EP_USED > 0 ) + int epNum; + uint32_t tmp; + USBD_Ep_TypeDef *ep; +#endif + + if ( USBD_poweredDown ) + { + USBD_poweredDown = false; + DEBUG_USB_INT_LO_PUTCHAR( '/' ); + +#if !defined( USB_CORECLK_HFRCO ) || !defined( CMU_OSCENCMD_USHFRCOEN ) + /* Switch HFCLK from HFRCO to HFXO. */ + CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO ); +#endif + + /* Turn off HFRCO when not needed. */ + if ( ( cmuStatus & CMU_STATUS_HFRCOENS ) == 0 ) + { + CMU->OSCENCMD = CMU_OSCENCMD_HFRCODIS; + } + + /* Exit partial powerdown mode. */ + USB->PCGCCTL &= ~USB_PCGCCTL_STOPPCLK; + USB->PCGCCTL &= ~(USB_PCGCCTL_PWRCLMP | USB_PCGCCTL_RSTPDWNMODULE); + + if (( USB->GINTSTS & ( USB_GINTSTS_WKUPINT | USB_GINTSTS_RESETDET ) ) == 0) + { + USB->DCTL = x_USB_DCTL | USB_DCTL_RMTWKUPSIG; + USB->DCTL = x_USB_DCTL; + } + + /* Restore USB core registers. */ + USB->GUSBCFG = x_USB_GUSBCFG; + USB->DCFG = x_USB_DCFG; + +#if ( FIFO_CNT > 0 ) + for ( i = 0; i < FIFO_CNT; i++ ) + { + USB_DIEPTXFS[ i ] = x_USB_DIEPTXFS[ i ]; + } +#endif + +#if ( NUM_EP_USED > 0 ) + for ( i = 0; i < NUM_EP_USED; i++ ) + { + ep = &dev->ep[ i+1 ]; + epNum = ep->num; + + tmp = x_USB_EP_CTL[ i ] & + ~( USB_DIEP_CTL_CNAK | USB_DIEP_CTL_SNAK | + USB_DIEP_CTL_SETD0PIDEF | USB_DIEP_CTL_SETD1PIDOF ); + + if ( x_USB_EP_CTL[ i ] & USB_DIEP_CTL_DPIDEOF ) + tmp |= USB_DIEP_CTL_SETD1PIDOF; + else + tmp |= USB_DIEP_CTL_SETD0PIDEF; + + if ( x_USB_EP_CTL[ i ] & USB_DIEP_CTL_NAKSTS ) + tmp |= USB_DIEP_CTL_SNAK; + else + tmp |= USB_DIEP_CTL_CNAK; + + if ( ep->in ) + { + USB_DINEPS[ epNum ].CTL = tmp; + USB_DINEPS[ epNum ].TSIZ = x_USB_EP_TSIZ[ i ]; + USB_DINEPS[ epNum ].DMAADDR = x_USB_EP_DMAADDR[ i ]; + } + else + { + USB_DOUTEPS[ epNum ].CTL = tmp; + USB_DOUTEPS[ epNum ].TSIZ = x_USB_EP_TSIZ[ i ]; + USB_DOUTEPS[ epNum ].DMAADDR = x_USB_EP_DMAADDR[ i ]; + } + } +#endif + + USB->PCGCCTL = x_USB_PCGCCTL; + USB->DOEPMSK = x_USB_DOEPMSK; + USB->DIEPMSK = x_USB_DIEPMSK; + USB->DAINTMSK = x_USB_DAINTMSK; + USB->DCTL = x_USB_DCTL; + USB->GNPTXFSIZ = x_USB_GNPTXFSIZ; + USB->GRXFSIZ = x_USB_GRXFSIZ; + USB->GAHBCFG = x_USB_GAHBCFG; +#if defined(_USB_GOTGCTL_MASK) + USB->GOTGCTL = x_USB_GOTGCTL; +#endif + USB->GINTMSK = x_USB_GINTMSK; + + USB->DCTL |= USB_DCTL_PWRONPRGDONE; + +#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ENTEREM2 ) +#ifndef __MBED__ + /* Do not reenter EM2 on interrupt exit. */ + SCB->SCR &= ~(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk); +#else + usbhal_allow_em2(false); +#endif +#endif + + return true; + } + return false; +} +#endif /* if ( USB_PWRSAVE_MODE ) */ + +#if defined( USB_DOEP0INT_STUPPKTRCVD ) +static void HandleOutEpIntr( uint32_t status, USBD_Ep_TypeDef *ep ) +{ + uint32_t doeptsiz; + + if ( ep->num == 0 ) + { + if ( status & USB_DOEP0INT_XFERCOMPL ) + { + USB->DOEP0INT = USB_DOEP0INT_XFERCOMPL; + doeptsiz = USB->DOEP0TSIZ; + + if ( ep->state == D_EP_IDLE ) + { + if ( status & USB_DOEP0INT_STUPPKTRCVD ) + { + USB->DOEP0INT = USB_DOEP0INT_STUPPKTRCVD; + } + status = USBDHAL_GetOutEpInts( ep ); + doeptsiz = USB->DOEP0TSIZ; + + if ( status & USB_DOEP0INT_SETUP ) + { +retry: + /* Already started data stage, clear setup */ + USB->DOEP0INT = USB_DOEP0INT_SETUP; + status &= ~USB_DOEP0INT_SETUP; + { + int supCnt = ( doeptsiz & _USB_DOEP0TSIZ_SUPCNT_MASK ) + >> _USB_DOEP0TSIZ_SUPCNT_SHIFT; + + if ( supCnt == 3 ) + supCnt = 2; + + dev->setup = &dev->setupPkt[ 2 - supCnt ]; + } + DEBUG_USB_INT_LO_PUTS( "\nS" ); + USBDEP_Ep0Handler( dev ); + + /* Prepare for more setup packets */ + if ( ep->state == D_EP0_IN_STATUS || ep->state == D_EP_TRANSMITTING ) + { + USBDHAL_StartEp0Setup( dev ); + } + } + else /* xfercompl && idle && !setup */ + { + status = USBDHAL_GetOutEpInts( ep ); + if ( status & USB_DOEP0INT_SETUP ) + goto retry; + USBDHAL_StartEp0Setup( dev ); + } + } + else /* ep0state != EP0_IDLE */ + { +#ifdef __MBED__ + if ( ep->state == D_EP_RECEIVING ) + { + int xfer_size = ep->packetSize - (( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_XFERSIZE_MASK ) + >> _USB_DOEP0TSIZ_XFERSIZE_SHIFT); + int setup_pkt_received = status & USB_DOEP0INT_SETUP; + + if ( (!setup_pkt_received && xfer_size == 0) || + (setup_pkt_received && xfer_size == 8) ) + { + /* Higher levels need to see the correct transfer amount for ZLPs */ + ep->remaining = 0; + ep->xferred = 0; + } + else + { + /* FIXME - does not work if actual read size > 56 */ + if ( setup_pkt_received ) xfer_size -= 8; + + ep->xferred = xfer_size; + ep->remaining -= xfer_size; + } + + USBDEP_Ep0Handler( dev ); + } +#else + if ( ep->state == D_EP_RECEIVING ) + { + if ( ep->remaining > ep->packetSize ) + { + ep->remaining -= ep->packetSize; + ep->xferred += ep->packetSize; + } + else + { + ep->xferred += ep->remaining; + ep->remaining = 0; + } + USBDEP_Ep0Handler( dev ); + } + else if ( ep->state == D_EP0_OUT_STATUS ) + { + USBDEP_Ep0Handler( dev ); + } +#endif + } + } /* if ( status & USB_DOEP0INT_XFERCOMPL ) */ + + if ( status & USB_DOEP0INT_STSPHSERCVD ) + { + USB->DOEP0INT = USB_DOEP0INT_STSPHSERCVD; + } + + if ( status & USB_DOEP0INT_SETUP ) + { + USB->DOEP0INT = USB_DOEP0INT_SETUP; + { + int supCnt = ( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_SUPCNT_MASK ) + >> _USB_DOEP0TSIZ_SUPCNT_SHIFT; + + if ( supCnt == 3 ) + supCnt = 2; + + dev->setup = &dev->setupPkt[ 2 - supCnt ]; + } + DEBUG_USB_INT_LO_PUTS( "\nS" ); + USBDEP_Ep0Handler( dev ); + } + } + else /* epnum != 0 */ + { + if ( status & USB_DOEP_INT_XFERCOMPL ) + { + USB_DOUTEPS[ ep->num ].INT = USB_DOEP_INT_XFERCOMPL; + + ep->xferred = ep->hwXferSize - + ( ( USB_DOUTEPS[ ep->num ].TSIZ & _USB_DOEP_TSIZ_XFERSIZE_MASK )>> + _USB_DOEP_TSIZ_XFERSIZE_SHIFT ); + ep->remaining -= ep->xferred; + + USBDEP_EpHandler( ep->addr ); + } + } +} +#endif + +/** @endcond */ + +#endif /* defined( USB_DEVICE ) */ +#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */