int USBD_SetAddress(uint8_t addr) { int retVal = USB_STATUS_REQ_ERR; if ( dev->state == USBD_STATE_DEFAULT ) { if ( addr != 0 ) { USBD_SetUsbState( USBD_STATE_ADDRESSED ); } USBDHAL_SetAddr( addr ); retVal = USB_STATUS_OK; } else if ( dev->state == USBD_STATE_ADDRESSED ) { if ( addr == 0 ) { USBD_SetUsbState( USBD_STATE_DEFAULT ); } USBDHAL_SetAddr( addr ); retVal = USB_STATUS_OK; } return retVal; }
/***************************************************************************//** * @brief * Stop USB device stack operation. * * @details * The data-line pullup resistor is turned off, USB interrupts are disabled, * and finally the USB pins are disabled. ******************************************************************************/ void USBD_Stop( void ) { USBD_Disconnect(); NVIC_DisableIRQ( USB_IRQn ); USBHAL_DisableGlobalInt(); USB->IEN = _USB_IEN_RESETVALUE; USB->ROUTE = _USB_ROUTE_RESETVALUE; USBD_SetUsbState( USBD_STATE_NONE ); }
/***************************************************************************//** * @brief * Stop USB device stack operation. * * @details * The data-line pullup resistor is turned off, USB interrupts are disabled, * and finally the USB pins are disabled. ******************************************************************************/ void USBD_Stop( void ) { USBD_Disconnect(); NVIC_DisableIRQ( USB_IRQn ); USBHAL_DisableGlobalInt(); USBHAL_DisableUsbInt(); USBHAL_DisablePhyPins(); USBD_SetUsbState( USBD_STATE_NONE ); /* Turn off USB clocks. */ CMU->HFCORECLKEN0 &= ~(CMU_HFCORECLKEN0_USB | CMU_HFCORECLKEN0_USBC); }
/***************************************************************************//** * @brief Handle USB port suspend interrupt * @details After receiving a USB reset, set the device state and * call @ref USBD_Suspend() if configured to do so in * @ref SLAB_USB_PWRSAVE_MODE ******************************************************************************/ static void handleUsbSuspendInt(void) { if (myUsbDevice.state >= USBD_STATE_POWERED) { USBD_SetUsbState(USBD_STATE_SUSPENDED); #if (SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONSUSPEND) USBD_Suspend(); #endif } }
/* * 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" ); } }
/***************************************************************************//** * @brief * Initializes USB device hardware and internal protocol stack data structures, * then connects the data-line (D+ or D-) pullup resistor to signal host that * enumeration can begin. * * @note * You may later use @ref USBD_Disconnect() and @ref USBD_Connect() to force * reenumeration. * * @param[in] p * Pointer to device initialization struct. See @ref USBD_Init_TypeDef. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_Init( const USBD_Init_TypeDef *p ) { USBD_Ep_TypeDef *ep; CORE_DECLARE_IRQ_STATE; #if !defined( USB_CORECLK_HFRCO ) || !defined( CMU_OSCENCMD_USHFRCOEN ) /* Devices supporting crystal-less USB can use HFRCO or HFXO as core clock. */ /* All other devices must use HFXO as core clock. */ if ( CMU_ClockSelectGet( cmuClock_HF ) != cmuSelect_HFXO ) { CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO ); } #endif #if !defined( CMU_OSCENCMD_USHFRCOEN ) #if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) CMU_OscillatorEnable(cmuOsc_LFXO, true, false); #else CMU_OscillatorEnable(cmuOsc_LFRCO, true, false); #endif #else CMU_ClockEnable(cmuClock_CORELE, true); /* LFC clock is needed to detect USB suspend when LEMIDLE is activated. */ #if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) CMU_ClockSelectSet(cmuClock_LFC, cmuSelect_LFXO); #else CMU_ClockSelectSet(cmuClock_LFC, cmuSelect_LFRCO); #endif CMU_ClockEnable(cmuClock_USBLE, true); #endif USBTIMER_Init(); memset( dev, 0, sizeof( USBD_Device_TypeDef ) ); dev->setup = dev->setupPkt; dev->state = USBD_STATE_LASTMARKER; dev->savedState = USBD_STATE_NONE; dev->lastState = USBD_STATE_NONE; dev->callbacks = p->callbacks; dev->remoteWakeupEnabled = false; /* Initialize EP0 */ ep = &dev->ep[ 0 ]; ep->in = false; ep->buf = NULL; ep->num = 0; ep->mask = 1; ep->addr = 0; ep->type = USB_EPTYPE_CTRL; ep->txFifoNum = 0; /* FIXME! */ ep->packetSize = 64; dev->ep0MpsCode = _USB_DOEP0CTL_MPS_64B; ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; ep->fifoSize = ep->packetSize / 4; totalTxFifoSize = ep->fifoSize * p->bufferingMultiplier[ 0 ]; totalRxFifoSize = (ep->fifoSize + 1) * p->bufferingMultiplier[ 0 ]; /* Rx-FIFO size: SETUP packets : 4*n + 6 n=#CTRL EP's * GOTNAK : 1 * Status info : 2*n n=#OUT EP's (EP0 included) in HW */ totalRxFifoSize += 10 + 1 + ( 2 * (MAX_NUM_OUT_EPS + 1) ); CORE_ENTER_CRITICAL(); /* Enable USB clock */ CMU->HFCORECLKEN0 |= CMU_HFCORECLKEN0_USB | CMU_HFCORECLKEN0_USBC; #if defined( CMU_OSCENCMD_USHFRCOEN ) CMU->USHFRCOCONF = CMU_USHFRCOCONF_BAND_48MHZ; CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_USHFRCO ); /* Enable USHFRCO Clock Recovery mode. */ CMU->USBCRCTRL |= CMU_USBCRCTRL_EN; /* Turn on Low Energy Mode (LEM) features. */ USB->CTRL = USB_CTRL_LEMOSCCTRL_GATE | USB_CTRL_LEMIDLEEN | USB_CTRL_LEMPHYCTRL; #else CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_HFCLK ); #endif USBHAL_DisableGlobalInt(); if ( USBDHAL_CoreInit( totalRxFifoSize, totalTxFifoSize ) == USB_STATUS_OK ) { USBDHAL_EnableUsbResetAndSuspendInt(); USBHAL_EnableGlobalInt(); NVIC_ClearPendingIRQ( USB_IRQn ); NVIC_EnableIRQ( USB_IRQn ); } else { CORE_EXIT_CRITICAL(); DEBUG_USB_API_PUTS( "\nUSBD_Init(), FIFO setup error" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } #if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) if ( USBHAL_VbusIsOn() ) { USBD_SetUsbState( USBD_STATE_POWERED ); } else #endif { USBD_SetUsbState( USBD_STATE_NONE ); } CORE_EXIT_CRITICAL(); return USB_STATUS_OK; }
/* * 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 HFCLK to be able to read USB */ /* peripheral registers. */ /* If we woke up from EM2, HFCLK is now HFRCO. */ CMU_OscillatorEnable( cmuOsc_HFXO, true, false); /* Prepare HFXO. */ CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV; while ( !( CMU->STATUS & CMU_STATUS_USBCHFCLKSEL ) ) { } } #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 & USB_PWRSAVE_MODE_ONVBUSOFF ) if ( UsbPowerUp() ) #endif { USBDHAL_EnableUsbResetInt(); } USBD_SetUsbState( USBD_STATE_POWERED ); } } if ( USB->IF & USB_IF_VREGOSL ) { USB->IFC = USB_IFC_VREGOSL; if ( !( USB->STATUS & USB_STATUS_VREGOS ) ) { servedVbusInterrupt = true; DEBUG_USB_INT_LO_PUTS( "\nVboF" ); USB->GINTMSK = 0; USB->GINTSTS = 0xFFFFFFFF; #if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) UsbPowerDown(); #endif USBD_SetUsbState( USBD_STATE_NONE ); } } } 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" ); } }
/***************************************************************************//** * @brief Handles USB port resume interrupt * @details Restore the device state to its previous value. ******************************************************************************/ static void handleUsbResumeInt(void) { USBD_SetUsbState(myUsbDevice.savedState); }
/***************************************************************************//** * @brief Handles USB port reset interrupt * @details After receiving a USB reset, halt all endpoints except for * Endpoint 0, set the device state, and configure USB hardware. ******************************************************************************/ static void handleUsbResetInt(void) { // Setup EP0 to receive SETUP packets myUsbDevice.ep0.state = D_EP_IDLE; // Halt all other endpoints #if SLAB_USB_EP1IN_USED myUsbDevice.ep1in.state = D_EP_HALT; #endif #if SLAB_USB_EP2IN_USED myUsbDevice.ep2in.state = D_EP_HALT; #endif #if SLAB_USB_EP3IN_USED myUsbDevice.ep3in.state = D_EP_HALT; #endif #if SLAB_USB_EP1OUT_USED myUsbDevice.ep1out.state = D_EP_HALT; #endif #if SLAB_USB_EP2OUT_USED myUsbDevice.ep2out.state = D_EP_HALT; #endif #if SLAB_USB_EP3OUT_USED myUsbDevice.ep3out.state = D_EP_HALT; #endif // After a USB reset, some USB hardware configurations will be reset and must // be reconfigured. // Re-enable clock recovery #if SLAB_USB_CLOCK_RECOVERY_ENABLED #if SLAB_USB_FULL_SPEED USB_EnableFullSpeedClockRecovery(); #else USB_EnableLowSpeedClockRecovery(); #endif #endif // Re-enable USB interrupts USB_EnableSuspendDetection(); USB_EnableDeviceInts(); // If VBUS is preset, put the device in the Default state. // Otherwise, put it in the Attached state. //#if (!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) // If VBUS is preset, put the device in the Default state. // Otherwise, put it in the Attached state. // If the device is bus-powered, always put it in the Default state #if (!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF) && !SLAB_USB_BUS_POWERED) if (USB_IsVbusOn()) { USBD_SetUsbState(USBD_STATE_DEFAULT); } else { USBD_SetUsbState(USBD_STATE_ATTACHED); } #else USBD_SetUsbState(USBD_STATE_DEFAULT); #endif // (!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) #if SLAB_USB_RESET_CB // Make the USB Reset Callback USBD_ResetCb(); #endif }
void USBD_Stop(void) { USB_DisableInts(); USBD_Disconnect(); USBD_SetUsbState(USBD_STATE_NONE); }
/***************************************************************************//** * @brief * Initializes USB device hardware and internal protocol stack data structures, * then connects the data-line (D+ or D-) pullup resistor to signal host that * enumeration can begin. * * @note * You may later use @ref USBD_Disconnect() and @ref USBD_Connect() to force * reenumeration. * * @param[in] p * Pointer to device initialization struct. See @ref USBD_Init_TypeDef. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_Init( const USBD_Init_TypeDef *p ) { int numEps; USBD_Ep_TypeDef *ep; uint8_t txFifoNum; uint8_t *conf, *confEnd; USB_EndpointDescriptor_TypeDef *epd; uint32_t totalRxFifoSize, totalTxFifoSize, numInEps, numOutEps; CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO ); #if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) CMU_OscillatorEnable( cmuOsc_LFXO, true, false ); #else CMU_OscillatorEnable( cmuOsc_LFRCO, true, false ); #endif USBTIMER_Init(); memset( dev, 0, sizeof( USBD_Device_TypeDef ) ); dev->setup = dev->setupPkt; dev->deviceDescriptor = p->deviceDescriptor; dev->configDescriptor = (USB_ConfigurationDescriptor_TypeDef*) p->configDescriptor; dev->stringDescriptors = p->stringDescriptors; dev->numberOfStrings = p->numberOfStrings; dev->state = USBD_STATE_LASTMARKER; dev->savedState = USBD_STATE_NONE; dev->lastState = USBD_STATE_NONE; dev->callbacks = p->callbacks; dev->remoteWakeupEnabled = false; /* Initialize EP0 */ ep = &dev->ep[ 0 ]; ep->in = false; ep->buf = NULL; ep->num = 0; ep->mask = 1; ep->addr = 0; ep->type = USB_EPTYPE_CTRL; ep->txFifoNum = 0; ep->packetSize = USB_EP0_SIZE; ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; ep->fifoSize = USB_EP0_SIZE / 4; totalTxFifoSize = ep->fifoSize * p->bufferingMultiplier[ 0 ]; totalRxFifoSize = (ep->fifoSize + 1) * p->bufferingMultiplier[ 0 ]; #if defined( DEBUG_USB_API ) /* Do a sanity check on the configuration descriptor */ { int i; /* Check if bLength's adds up exactly to wTotalLength */ i = 0; conf = (uint8_t*)dev->configDescriptor; confEnd = conf + dev->configDescriptor->wTotalLength; while ( conf < confEnd ) { if ( *conf == 0 ) break; i += *conf; conf += *conf; } if ( ( conf != confEnd ) || ( i != dev->configDescriptor->wTotalLength ) ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal configuration descriptor" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } #endif /* defined( DEBUG_USB_API ) */ /* Parse configuration decriptor */ numEps = 0; numInEps = 0; numOutEps = 0; conf = (uint8_t*)dev->configDescriptor; confEnd = conf + dev->configDescriptor->wTotalLength; txFifoNum = 1; while ( conf < confEnd ) { if ( *conf == 0 ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal configuration descriptor" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( *(conf + 1) == USB_ENDPOINT_DESCRIPTOR ) { numEps++; epd = (USB_EndpointDescriptor_TypeDef*)conf; ep = &dev->ep[ numEps ]; ep->in = ( epd->bEndpointAddress & USB_SETUP_DIR_MASK ) != 0; ep->buf = NULL; ep->addr = epd->bEndpointAddress; ep->num = ep->addr & USB_EPNUM_MASK; ep->mask = 1 << ep->num; ep->type = epd->bmAttributes & CONFIG_DESC_BM_TRANSFERTYPE; ep->packetSize = epd->wMaxPacketSize; ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; if ( ep->in ) { numInEps++; ep->txFifoNum = txFifoNum++; ep->fifoSize = (ep->packetSize/4) * p->bufferingMultiplier[ numEps ]; dev->inEpAddr2EpIndex[ ep->num ] = numEps; totalTxFifoSize += ep->fifoSize; if ( ep->num > MAX_NUM_IN_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal IN EP address" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } else { numOutEps++; ep->fifoSize = (ep->packetSize/4 + 1) * p->bufferingMultiplier[ numEps ]; dev->outEpAddr2EpIndex[ ep->num ] = numEps; totalRxFifoSize += ep->fifoSize; if ( ep->num > MAX_NUM_OUT_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal OUT EP address" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } } conf += *conf; } /* Rx-FIFO size: SETUP packets : 4*n + 6 n=#CTRL EP's * GOTNAK : 1 * Status info : 2*n n=#OUT EP's (EP0 included) in HW */ totalRxFifoSize += 10 + 1 + ( 2 * (MAX_NUM_OUT_EPS + 1) ); if ( numEps != NUM_EP_USED ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( numInEps > MAX_NUM_IN_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal IN EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( numOutEps > MAX_NUM_OUT_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal OUT EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } INT_Disable(); /* Enable USB clock */ CMU->HFCORECLKEN0 |= CMU_HFCORECLKEN0_USB | CMU_HFCORECLKEN0_USBC; CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_HFCLK ); USBHAL_DisableGlobalInt(); if ( USBDHAL_CoreInit( totalRxFifoSize, totalTxFifoSize ) == USB_STATUS_OK ) { USBDHAL_EnableUsbResetInt(); USBHAL_EnableGlobalInt(); NVIC_ClearPendingIRQ( USB_IRQn ); NVIC_EnableIRQ( USB_IRQn ); } else { INT_Enable(); DEBUG_USB_API_PUTS( "\nUSBD_Init(), FIFO setup error" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } #if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) if ( USBHAL_VbusIsOn() ) { USBD_SetUsbState( USBD_STATE_POWERED ); } else #endif { USBD_SetUsbState( USBD_STATE_NONE ); } INT_Enable(); return USB_STATUS_OK; }
/***************************************************************************//** * @brief * Initializes USB device hardware and internal protocol stack data structures, * then connects the data-line (D+ or D-) pullup resistor to signal host that * enumeration can begin. * * @note * You may later use @ref USBD_Disconnect() and @ref USBD_Connect() to force * reenumeration. * * @param[in] p * Pointer to device initialization struct. See @ref USBD_Init_TypeDef. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_Init( const USBD_Init_TypeDef *p ) { int numEps; USBD_Ep_TypeDef *ep; uint8_t txFifoNum; uint8_t *conf, *confEnd; USB_EndpointDescriptor_TypeDef *epd; USB_InterfaceDescriptor_TypeDef *id; uint32_t totalRxFifoSize, totalTxFifoSize, numInEps, numOutEps; #if !defined( USB_CORECLK_HFRCO ) || !defined( CMU_OSCENCMD_USHFRCOEN ) /* Devices supporting crystal-less USB can use HFRCO or HFXO as core clock. */ /* All other devices must use HFXO as core clock. */ if ( CMU_ClockSelectGet( cmuClock_HF ) != cmuSelect_HFXO ) { CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO ); } #endif #if !defined( CMU_OSCENCMD_USHFRCOEN ) #if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) CMU_OscillatorEnable(cmuOsc_LFXO, true, false); #else CMU_OscillatorEnable(cmuOsc_LFRCO, true, false); #endif #else CMU_ClockEnable(cmuClock_CORELE, true); /* LFC clock is needed to detect USB suspend when LEMIDLE is activated. */ #if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) CMU_ClockSelectSet(cmuClock_LFC, cmuSelect_LFXO); #else CMU_ClockSelectSet(cmuClock_LFC, cmuSelect_LFRCO); #endif CMU_ClockEnable(cmuClock_USBLE, true); #endif USBTIMER_Init(); memset( dev, 0, sizeof( USBD_Device_TypeDef ) ); dev->setup = dev->setupPkt; dev->deviceDescriptor = p->deviceDescriptor; dev->configDescriptor = (USB_ConfigurationDescriptor_TypeDef*) p->configDescriptor; dev->stringDescriptors = p->stringDescriptors; dev->numberOfStrings = p->numberOfStrings; dev->state = USBD_STATE_LASTMARKER; dev->savedState = USBD_STATE_NONE; dev->lastState = USBD_STATE_NONE; dev->callbacks = p->callbacks; dev->remoteWakeupEnabled = false; /* Initialize EP0 */ ep = &dev->ep[ 0 ]; ep->in = false; ep->buf = NULL; ep->num = 0; ep->mask = 1; ep->addr = 0; ep->type = USB_EPTYPE_CTRL; ep->txFifoNum = 0; ep->packetSize = p->deviceDescriptor->bMaxPacketSize0; if ( ep->packetSize == 32 ) { dev->ep0MpsCode = _USB_DOEP0CTL_MPS_32B; } else if ( ep->packetSize == 64 ) { dev->ep0MpsCode = _USB_DOEP0CTL_MPS_64B; } else { return USB_STATUS_ILLEGAL; } ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; ep->fifoSize = ep->packetSize / 4; totalTxFifoSize = ep->fifoSize * p->bufferingMultiplier[ 0 ]; totalRxFifoSize = (ep->fifoSize + 1) * p->bufferingMultiplier[ 0 ]; #if defined( DEBUG_USB_API ) /* Do a sanity check on the configuration descriptor */ { int i; /* Check if bLength's adds up exactly to wTotalLength */ i = 0; conf = (uint8_t*)dev->configDescriptor; confEnd = conf + dev->configDescriptor->wTotalLength; while ( conf < confEnd ) { if ( *conf == 0 ) break; i += *conf; conf += *conf; } if ( ( conf != confEnd ) || ( i != dev->configDescriptor->wTotalLength ) ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal configuration descriptor" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } #endif /* defined( DEBUG_USB_API ) */ /* Parse configuration decriptor */ numEps = 0; numInEps = 0; numOutEps = 0; conf = (uint8_t*)dev->configDescriptor; confEnd = conf + dev->configDescriptor->wTotalLength; txFifoNum = 1; while ( conf < confEnd ) { if ( *conf == 0 ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal configuration descriptor" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( *(conf + 1) == USB_ENDPOINT_DESCRIPTOR ) { numEps++; epd = (USB_EndpointDescriptor_TypeDef*)conf; #if defined( __GNUC__ ) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" #endif ep = &dev->ep[ numEps ]; #if defined( __GNUC__ ) #pragma GCC diagnostic pop #endif ep->in = ( epd->bEndpointAddress & USB_SETUP_DIR_MASK ) != 0; ep->buf = NULL; ep->addr = epd->bEndpointAddress; ep->num = ep->addr & USB_EPNUM_MASK; ep->mask = 1 << ep->num; ep->type = epd->bmAttributes & CONFIG_DESC_BM_TRANSFERTYPE; ep->packetSize = epd->wMaxPacketSize; ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; if ( p->bufferingMultiplier[ numEps ] == 0 ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal EP fifo buffer configuration" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( ep->in ) { numInEps++; ep->txFifoNum = txFifoNum++; ep->fifoSize = ( ( ep->packetSize + 3 ) / 4 ) * p->bufferingMultiplier[ numEps ]; dev->inEpAddr2EpIndex[ ep->num ] = numEps; totalTxFifoSize += ep->fifoSize; if ( ep->num > MAX_NUM_IN_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal IN EP address" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } else { numOutEps++; ep->fifoSize = ( ( ( ep->packetSize + 3 ) / 4 ) + 1 ) * p->bufferingMultiplier[ numEps ]; dev->outEpAddr2EpIndex[ ep->num ] = numEps; totalRxFifoSize += ep->fifoSize; if ( ep->num > MAX_NUM_OUT_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal OUT EP address" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } } else if ( *(conf + 1) == USB_INTERFACE_DESCRIPTOR ) { id = (USB_InterfaceDescriptor_TypeDef*)conf; if ( id->bAlternateSetting == 0 ) // Only check default interfaces { if ( dev->numberOfInterfaces != id->bInterfaceNumber ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal interface number" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } dev->numberOfInterfaces++; } } conf += *conf; } /* Rx-FIFO size: SETUP packets : 4*n + 6 n=#CTRL EP's * GOTNAK : 1 * Status info : 2*n n=#OUT EP's (EP0 included) in HW */ totalRxFifoSize += 10 + 1 + ( 2 * (MAX_NUM_OUT_EPS + 1) ); if ( dev->configDescriptor->bNumInterfaces != dev->numberOfInterfaces ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal interface count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( numEps != NUM_EP_USED ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( numInEps > MAX_NUM_IN_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal IN EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( numOutEps > MAX_NUM_OUT_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal OUT EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } INT_Disable(); /* Enable USB clock */ CMU->HFCORECLKEN0 |= CMU_HFCORECLKEN0_USB | CMU_HFCORECLKEN0_USBC; #if defined( CMU_OSCENCMD_USHFRCOEN ) CMU->USHFRCOCONF = CMU_USHFRCOCONF_BAND_48MHZ; CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_USHFRCO ); /* Enable USHFRCO Clock Recovery mode. */ CMU->USBCRCTRL |= CMU_USBCRCTRL_EN; /* Turn on Low Energy Mode (LEM) features. */ USB->CTRL = USB_CTRL_LEMOSCCTRL_GATE | USB_CTRL_LEMIDLEEN | USB_CTRL_LEMPHYCTRL; #else CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_HFCLK ); #endif USBHAL_DisableGlobalInt(); if ( USBDHAL_CoreInit( totalRxFifoSize, totalTxFifoSize ) == USB_STATUS_OK ) { USBDHAL_EnableUsbResetAndSuspendInt(); USBHAL_EnableGlobalInt(); NVIC_ClearPendingIRQ( USB_IRQn ); NVIC_EnableIRQ( USB_IRQn ); } else { INT_Enable(); DEBUG_USB_API_PUTS( "\nUSBD_Init(), FIFO setup error" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } #if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) if ( USBHAL_VbusIsOn() ) { USBD_SetUsbState( USBD_STATE_POWERED ); } else #endif { USBD_SetUsbState( USBD_STATE_NONE ); } INT_Enable(); return USB_STATUS_OK; }